diff --git a/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll b/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll index ca35c3a90..268f7989a 100644 Binary files a/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll and b/BaseLibs/MelonLoader/Dependencies/mono-2.0-bdwgc.dll differ diff --git a/BaseLibs/MelonLoader/Managed/ValueTupleBridge.dll b/BaseLibs/MelonLoader/Managed/ValueTupleBridge.dll new file mode 100644 index 000000000..90f9891f9 Binary files /dev/null and b/BaseLibs/MelonLoader/Managed/ValueTupleBridge.dll differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 405477901..0e120e667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### Version List: +[v0.2.6 Open-Beta](#v026-open-beta) [v0.2.5 Open-Beta](#v025-open-beta) [v0.2.4 Open-Beta](#v024-open-beta) [v0.2.3 Open-Beta](#v023-open-beta) @@ -13,6 +14,57 @@ --- +### v0.2.6 Open-Beta: + +1. Fixed Issue with Logger Timestamp making New Lines. +2. Added Silent Launch Option to Installer. (Credits to trevtv :P) +3. Fixed Internal Failure caused by DisableAnalytics for certain users. +4. Added Operating System log to startup info. +5. Added ValueTupleBridge to Managed Folder. +6. Added Unified Attributes for both Plugins and Mods. +7. Added Legacy Attribute Support. +8. Fixed Logger Issue with new Unified Attributes. +9. Deprecated MelonModLogger, use MelonLogger instead. +10. Deprecated ModPrefs, use MelonPrefs instead. +11. Added HarmonyInstance Method for easier Unpatching of Mods and Plugins. +12. Create MelonLoaderComponent after first scene load in Mono Games. (Credits to knah :D) +13. Removed Launch Option --melonloader.devpluginsonly. +14. Removed Launch Option --melonloader.devmodsonly. +15. Added Launch Option --melonloader.loadmodeplugins. +16. Added Launch Option --melonloader.loadmodemods. +17. Fixed Issue with Debug Mode causing Crashes for Some Games. +18. Fixed Issue with Manual Zip overriding Latest Version. +19. Fixed Issue where Manual Zip would get set as Default Selection. +20. Unity Analytics now redirected to localhost, rather than throwing null and exceptions. (Credits to Emilia :3) +21. Plugins are now able to use OnUpdate. +22. Plugins are now able to use OnLateUpdate. +23. Plugins are now able to use OnGUI. +24. Added GetUnityTlsInterface to Imports. +25. Implemented Native Sided SSL/TLS/HTTPS Fix. +26. Temporarily disabled DisableAnalytics. +27. Fixed Issue with Support Modules not loading due to Missing Method errors. +28. Fixed Issue with attaching dnSpy on Il2Cpp Games. +29. Replaced mono-2.0-bdwgc.dll with dnSpy Debug dll. +30. Debug Mode will now use LoadFrom instead of Load for breakpoint compatibility. +31. Fixed Crash Issue with DisableAnalytics. +32. Temporarily Disabled Console Logger Callbacks. +33. Revised Console Logger Callbacks. +34. Fixed Issue with LogMelonError not running Callbacks. +35. Deprecated MelonLoader.Main use MelonLoaderBase or MelonHandler instead. +36. Revised Base Melon Handling. +37. Revised IniFile. +38. Fixed Issue with Plugins not getting OnUpdate called. +39. Fixed Issue with Plugins not getting OnLateUpdate called. +40. Fixed Issue with Plugins not getting OnGUI called. +41. Plugins are now able to use VRChat_OnUiManagerInit. +42. Fixed Coroutine Queue for Mono Games. +43. Added Launch Option --melonloader.consoleontop. (Credits to trevtv :P) +44. Fixed Issue with Assembly Generator not stopping when failing to Download. +45. Escalated Assembly Generator failures to Internal Failures. +46. Fixed Issue where Assembly Generator failures would cause a Crash. + +--- + ### v0.2.5 Open-Beta: 1. Fixed Issue that prevented deserializing structs with TinyJSON. (Credits to zeobviouslyfakeacc :3) diff --git a/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs b/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs index 09d30b0b7..4bb6746c8 100644 --- a/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs +++ b/MelonLoader.AssemblyGenerator/AssemblyGenerator.cs @@ -26,6 +26,7 @@ internal static class Main private static string localConfigPath = null; private static LocalConfig localConfig = null; private static Il2CppConfig il2cppConfig = new Il2CppConfig(); + private static bool DownloadedSuccessfully = true; internal static bool Initialize(string unityVersion, string gameRoot, string gameDataDir) { @@ -86,14 +87,36 @@ private static bool AssemblyGenerateCheck(string unityVersion) private static void DownloadDependencies(string unityVersion) { Logger.Log("Downloading Il2CppDumper"); - DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppDumperUrl, ExternalToolVersions.Il2CppDumperVersion, localConfig.DumperVersion, Il2CppDumper.BaseFolder, TempFileCache.CreateFile()); - localConfig.DumperVersion = ExternalToolVersions.Il2CppDumperVersion; - localConfig.Save(localConfigPath); + try + { + DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppDumperUrl, ExternalToolVersions.Il2CppDumperVersion, localConfig.DumperVersion, Il2CppDumper.BaseFolder, TempFileCache.CreateFile()); + localConfig.DumperVersion = ExternalToolVersions.Il2CppDumperVersion; + localConfig.Save(localConfigPath); + } + catch (Exception ex) + { + DownloadedSuccessfully = false; + Logger.LogError(ex.ToString()); + Logger.Log("Can't download Il2CppDumper!"); + } + if (!DownloadedSuccessfully) + return; Logger.Log("Downloading Il2CppAssemblyUnhollower"); - DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppAssemblyUnhollowerUrl, ExternalToolVersions.Il2CppAssemblyUnhollowerVersion, localConfig.UnhollowerVersion, Il2CppAssemblyUnhollower.BaseFolder, TempFileCache.CreateFile()); - localConfig.UnhollowerVersion = ExternalToolVersions.Il2CppAssemblyUnhollowerVersion; - localConfig.Save(localConfigPath); + try + { + DownloaderAndUnpacker.Run(ExternalToolVersions.Il2CppAssemblyUnhollowerUrl, ExternalToolVersions.Il2CppAssemblyUnhollowerVersion, localConfig.UnhollowerVersion, Il2CppAssemblyUnhollower.BaseFolder, TempFileCache.CreateFile()); + localConfig.UnhollowerVersion = ExternalToolVersions.Il2CppAssemblyUnhollowerVersion; + localConfig.Save(localConfigPath); + } + catch (Exception ex) + { + DownloadedSuccessfully = false; + Logger.LogError(ex.ToString()); + Logger.Log("Can't download Il2CppAssemblyUnhollower!"); + } + if (!DownloadedSuccessfully) + return; Logger.Log("Downloading Unity Dependencies"); string tempfile = TempFileCache.CreateFile(); @@ -142,8 +165,8 @@ private static void DownloadDependencies(string unityVersion) } catch (Exception ex) { - Logger.LogError("Can't download Unity Dependencies, Unstripping will NOT be done!"); Logger.LogError(ex.ToString()); + Logger.LogError("Can't download Unity Dependencies, Unstripping will NOT be done!"); } } } @@ -151,7 +174,9 @@ private static void DownloadDependencies(string unityVersion) private static bool AssemblyGenerate(string gameRoot, string unityVersion, string gameDataDir) { DownloadDependencies(unityVersion); - + if (!DownloadedSuccessfully) + return false; + FixIl2CppDumperConfig(); Logger.Log("Executing Il2CppDumper..."); diff --git a/MelonLoader.AssemblyGenerator/AssemblyInfo.cs b/MelonLoader.AssemblyGenerator/AssemblyInfo.cs index 55e114ca8..dab900161 100644 --- a/MelonLoader.AssemblyGenerator/AssemblyInfo.cs +++ b/MelonLoader.AssemblyGenerator/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] [assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".GeneratorProcess")] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("b323399d-2c8d-46ee-9ce5-2f970f7f2db9")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file diff --git a/MelonLoader.AssemblyGenerator/Program.cs b/MelonLoader.AssemblyGenerator/Program.cs index bce321962..a11bd0b29 100644 --- a/MelonLoader.AssemblyGenerator/Program.cs +++ b/MelonLoader.AssemblyGenerator/Program.cs @@ -12,7 +12,7 @@ public static class Program public static int Main(string[] args) { ServicePointManager.Expect100Continue = true; - ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072; + ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | (SecurityProtocolType)3072; webClient.Headers.Add("User-Agent", "Unity web player"); if (args.Length < 4) diff --git a/MelonLoader.Installer/MainForm.cs b/MelonLoader.Installer/MainForm.cs index 8766ee5bc..6f0dfd709 100644 --- a/MelonLoader.Installer/MainForm.cs +++ b/MelonLoader.Installer/MainForm.cs @@ -117,7 +117,7 @@ private void btnInstall_Click(object sender, EventArgs e) string selectedVersion = ((((string)cbVersions.Items[cbVersions.SelectedIndex]).StartsWith("Latest")) ? (string)cbVersions.Items[cbVersions.SelectedIndex + 1] : (string)cbVersions.Items[cbVersions.SelectedIndex]); bool legacy_install = (selectedVersion.Equals("v0.2.1") || selectedVersion.Equals("v0.2") || selectedVersion.Equals("v0.1.0")); - Program.Install(dirpath, selectedVersion, legacy_install); + Program.Install(dirpath, selectedVersion, legacy_install, false); Program.SetDisplayText("SUCCESS!"); MessageBox.Show("Installation Successful!", Program.Title, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly); diff --git a/MelonLoader.Installer/MelonLoader.Installer.csproj b/MelonLoader.Installer/MelonLoader.Installer.csproj index e2e0034d3..2101fdaa8 100644 --- a/MelonLoader.Installer/MelonLoader.Installer.csproj +++ b/MelonLoader.Installer/MelonLoader.Installer.csproj @@ -5,7 +5,7 @@ net472 true latest - 2.0.0 + 2.0.1 Lava Gang discord.gg/2Wn3N2P Lava Gang @@ -15,7 +15,7 @@ icon.ico app.manifest MelonLoader.Installer.Program - 2.0.0.0 + 2.0.1.0 Installer for MelonLoader MelonLoader.Installer MelonLoader.Installer diff --git a/MelonLoader.Installer/Program.cs b/MelonLoader.Installer/Program.cs index 9619d7906..30df6fc2b 100644 --- a/MelonLoader.Installer/Program.cs +++ b/MelonLoader.Installer/Program.cs @@ -5,10 +5,9 @@ using System.IO.Compression; using System.Linq; using System.Net; +using System.Reflection; using System.Text; -using System.Threading; using System.Windows.Forms; -using System.Windows.Forms.VisualStyles; using MelonLoader.LightJson; #pragma warning disable 0168 @@ -16,6 +15,7 @@ namespace MelonLoader.Installer { static class Program { + internal static bool ShouldCheckForUpdates = true; internal static string Title = "MelonLoader Installer"; private static string ExeName = null; internal static string ManualZipPath = null; @@ -25,18 +25,32 @@ static class Program private static readonly string[] filesToCleanUp = new string[] { "Mono.Cecil.dll", "version.dll", "winmm.dll" }; private static readonly string[] foldersToCleanUp = new string[] { "Logs", "MelonLoader" }; + public static bool silent; + public static string silentPath; + [STAThread] static void Main() { ServicePointManager.Expect100Continue = true; - ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072; + ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | (SecurityProtocolType)3072; webClient.Headers.Add("User-Agent", "Unity web player"); webClient.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs info) => SetPercentage(info.ProgressPercentage); Application.SetCompatibleTextRenderingDefault(false); Application.EnableVisualStyles(); + var args = Environment.GetCommandLineArgs(); + foreach (string arg in args) + { + bool isSelf = Path.GetFullPath(arg) == Assembly.GetExecutingAssembly().Location; + if (File.Exists(arg) && arg.EndsWith(".exe") && !isSelf) + { + silent = true; + silentPath = Path.GetDirectoryName(arg); + } + } + ExeName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName); - if (ExeName.EndsWith(".tmp.exe")) + if (ShouldCheckForUpdates && ExeName.EndsWith(".tmp.exe")) { string original_exe_path = (Directory.GetCurrentDirectory() + "\\" + ExeName.Substring(0, (ExeName.Length - 8))); File.Delete(original_exe_path); @@ -46,30 +60,75 @@ static void Main() } else { - string tempfilepath = (Directory.GetCurrentDirectory() + "\\" + ExeName + ".tmp.exe"); - if (File.Exists(tempfilepath)) - File.Delete(tempfilepath); + if (ShouldCheckForUpdates) + { + string tempfilepath = (Directory.GetCurrentDirectory() + "\\" + ExeName + ".tmp.exe"); + if (File.Exists(tempfilepath)) + File.Delete(tempfilepath); + } ManualZipPath = (Directory.GetCurrentDirectory() + "\\MelonLoader.zip"); - Install_GUI(); + + if (silent) + Install_NoGUI(); + else + Install_GUI(); + } + } + + static void Install_NoGUI() + { + try + { + string version = null; + JsonArray data = (JsonArray)JsonValue.Parse(webClient.DownloadString("https://api.github.com/repos/HerpDerpinstine/MelonLoader/releases")).AsJsonArray; + if (data.Count > 0) + version = data[0]["tag_name"].AsString; + try + { + Install(silentPath, version, false, true); + Environment.Exit(0); + } + catch (Exception ex) + { + MessageBox.Show("Installation failed; upload the created log to #melonloader-support on discord", Title); + File.WriteAllText(Directory.GetCurrentDirectory() + $@"\MLInstaller_{DateTime.Now:yy-M-dd_HH-mm-ss.fff}.log", ex.ToString()); + Environment.Exit(-1); + } + + } + catch (Exception ex) + { + MessageBox.Show("Failed to Get Version List; upload the created log to #melonloader-support on discord", Title); + File.WriteAllText(Directory.GetCurrentDirectory() + $@"\MLInstaller_{DateTime.Now:yy-M-dd_HH-mm-ss.fff}.log", ex.ToString()); + Environment.Exit(-1); } } static void Install_GUI() { + int selected_index = 0; + try { mainForm = new MainForm(); mainForm.cbVersions.Items.Clear(); - if (File.Exists(ManualZipPath)) + + bool should_add_manual = File.Exists(ManualZipPath); + if (should_add_manual) mainForm.cbVersions.Items.Add("Manual Zip"); JsonArray data = (JsonArray)JsonValue.Parse(webClient.DownloadString("https://api.github.com/repos/HerpDerpinstine/MelonLoader/releases")).AsJsonArray; if (data.Count > 0) { + bool has_added_latest = false; foreach (var x in data) { string version = x["tag_name"].AsString; - if (mainForm.cbVersions.Items.Count <= 0) + if (!has_added_latest) + { + has_added_latest = true; mainForm.cbVersions.Items.Add("Latest (" + version + ")"); + selected_index = (should_add_manual ? 1 : 0); + } mainForm.cbVersions.Items.Add(version); } } @@ -83,18 +142,22 @@ static void Install_GUI() Application.Exit(); } - mainForm.cbVersions.SelectedIndex = 0; - mainForm.cbVersions.SelectedItem = mainForm.cbVersions.Items[0]; + if ((selected_index < 0) || (selected_index > (mainForm.cbVersions.Items.Count - 1))) + selected_index = 0; + mainForm.cbVersions.SelectedIndex = selected_index; + mainForm.cbVersions.SelectedItem = mainForm.cbVersions.Items[selected_index]; mainForm.Show(); Application.Run(mainForm); } - internal static void Install(string dirpath, string selectedVersion, bool legacy_install) + internal static void Install(string dirpath, string selectedVersion, bool legacy_install, bool silent_install) { if (File.Exists(ManualZipPath) && selectedVersion.Equals("Manual Zip")) Install_ManualZip(dirpath); else if (legacy_install) Install_Legacy(dirpath, selectedVersion); + else if (silent_install) + Install_Silent(dirpath, selectedVersion); else Install_Normal(dirpath, selectedVersion); TempFileCache.ClearCache(); @@ -116,6 +179,16 @@ private static void Install_Legacy(string dirpath, string selectedVersion) Install_Legacy_02(dirpath, selectedVersion); } + private static void Install_Silent(string dirpath, string selectedVersion) + { + string tempfilepath = TempFileCache.CreateFile(); + webClient.DownloadFileAsync(new Uri("https://github.com/HerpDerpinstine/MelonLoader/releases/download/" + selectedVersion + "/MelonLoader.zip"), tempfilepath); + while (webClient.IsBusy) { } + Cleanup(dirpath); + ExtractZip(dirpath, tempfilepath); + CreateDirectories(dirpath, selectedVersion, false); + } + internal static void SetDisplayText(string text) { mainForm.Invoke(new Action(() => { @@ -125,6 +198,9 @@ internal static void SetDisplayText(string text) internal static void SetPercentage(int percent) { + if (silent) + return; + mainForm.Invoke(new Action(() => { mainForm.progressBar.Value = percent; mainForm.lblProgressPer.Text = percent.ToString() + "%"; @@ -349,6 +425,8 @@ internal static string GetUnityVersion(string exepath) internal static void CheckForUpdates() { + if (!ShouldCheckForUpdates) + return; try { string response = webClient.DownloadString("https://github.com/HerpDerpinstine/MelonLoader/raw/master/MelonLoader.Installer/MelonLoader.Installer.csproj"); diff --git a/MelonLoader.Installer/version.txt b/MelonLoader.Installer/version.txt deleted file mode 100644 index d4858f8bf..000000000 --- a/MelonLoader.Installer/version.txt +++ /dev/null @@ -1 +0,0 @@ -UNUSED \ No newline at end of file diff --git a/MelonLoader.ModHandler/AssemblyGenerator.cs b/MelonLoader.ModHandler/AssemblyGenerator.cs index 12605ebef..948934328 100644 --- a/MelonLoader.ModHandler/AssemblyGenerator.cs +++ b/MelonLoader.ModHandler/AssemblyGenerator.cs @@ -1,42 +1,64 @@ using System.Diagnostics; using System.IO; using System.Threading; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; -namespace MelonLoader.AssemblyGenerator +namespace MelonLoader { - internal static class Main + internal static class AssemblyGenerator { - internal static bool Initialize() + internal static bool HasGeneratedAssembly = false; + + internal static void Check() + { + if (!Imports.IsIl2CppGame() || Initialize()) + HasGeneratedAssembly = true; + else + MelonLoaderBase.UNLOAD(false); + } + + private static bool Initialize() { string GeneratorProcessPath = Path.Combine(Path.Combine(Path.Combine(Path.Combine(Imports.GetGameDirectory(), "MelonLoader"), "Dependencies"), "AssemblyGenerator"), "MelonLoader.AssemblyGenerator.exe"); if (File.Exists(GeneratorProcessPath)) { var generatorProcessInfo = new ProcessStartInfo(GeneratorProcessPath); - generatorProcessInfo.Arguments = $"\"{MelonLoader.Main.UnityVersion}\" \"{Imports.GetGameDirectory()}\" \"{Imports.GetGameDataDirectory()}\" {(Imports.AG_Force_Regenerate() ? "true" : "false")} {(string.IsNullOrEmpty(Imports.AG_Force_Version_Unhollower()) ? "" : Imports.AG_Force_Version_Unhollower())}"; + generatorProcessInfo.Arguments = $"\"{MelonLoaderBase.UnityVersion}\" \"{Imports.GetGameDirectory()}\" \"{Imports.GetGameDataDirectory()}\" {(Force_Regenerate() ? "true" : "false")} {(string.IsNullOrEmpty(Force_Version_Unhollower()) ? "" : Force_Version_Unhollower())}"; generatorProcessInfo.UseShellExecute = false; generatorProcessInfo.RedirectStandardOutput = true; generatorProcessInfo.CreateNoWindow = true; var process = Process.Start(generatorProcessInfo); if (process == null) - MelonModLogger.LogError("Unable to Start Assembly Generator!"); + MelonLogger.LogError("Unable to Start Assembly Generator!"); else { var stdout = process.StandardOutput; while (!stdout.EndOfStream) { var line = stdout.ReadLine(); - MelonModLogger.Log(line); + MelonLogger.Log(line); } while (!process.HasExited) Thread.Sleep(100); - if (Imports.IsDebugMode()) - MelonModLogger.Log($"Assembly Generator exited with code {process.ExitCode}"); - return (process.ExitCode == 0); + if (process.ExitCode == 0) + { + if (Imports.IsDebugMode()) + MelonLogger.Log($"Assembly Generator ran Successfully!"); + return true; + } + MelonLogger.Native_ThrowInternalError($"Assembly Generator exited with code {process.ExitCode}"); } } else - MelonModLogger.LogError("MelonLoader.AssemblyGenerator.exe does not Exist!"); + MelonLogger.LogError("MelonLoader.AssemblyGenerator.exe does not Exist!"); return false; } + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static bool Force_Regenerate(); + [MethodImpl(MethodImplOptions.InternalCall)] + [return: MarshalAs(UnmanagedType.LPStr)] + private extern static string Force_Version_Unhollower(); } } diff --git a/MelonLoader.ModHandler/AssemblyInfo.cs b/MelonLoader.ModHandler/AssemblyInfo.cs index 6bd24b3b0..517feedcb 100644 --- a/MelonLoader.ModHandler/AssemblyInfo.cs +++ b/MelonLoader.ModHandler/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] [assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct(MelonLoader.BuildInfo.Name)] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("26082f09-2902-45d6-a793-2ed9ff556bdf")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file diff --git a/MelonLoader.ModHandler/BuildInfo.cs b/MelonLoader.ModHandler/BuildInfo.cs index 871149216..94daa9b5d 100644 --- a/MelonLoader.ModHandler/BuildInfo.cs +++ b/MelonLoader.ModHandler/BuildInfo.cs @@ -6,6 +6,6 @@ public static class BuildInfo public const string Description = "MelonLoader"; public const string Author = "Lava Gang"; public const string Company = "discord.gg/2Wn3N2P"; - public const string Version = "0.2.5"; + public const string Version = "0.2.6"; } } \ No newline at end of file diff --git a/MelonLoader.ModHandler/Console.cs b/MelonLoader.ModHandler/Console.cs deleted file mode 100644 index 465609f52..000000000 --- a/MelonLoader.ModHandler/Console.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.IO; -using System.Runtime.CompilerServices; - -namespace MelonLoader -{ - public class Console - { - public static bool Enabled = false; - - internal static void Create() - { - Allocate(); - System.Console.SetOut(new StreamWriter(System.Console.OpenStandardOutput()) { AutoFlush = true }); - System.Console.SetIn(new StreamReader(System.Console.OpenStandardInput())); - SetTitle(BuildInfo.Name + " v" + BuildInfo.Version + " Open-Beta"); - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void Allocate(); - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void SetTitle(string title); - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void SetColor(ConsoleColor color); - //[MethodImpl(MethodImplOptions.InternalCall)] - //public static extern bool ShouldShowGameLogs(); - - private static void RunLogCallbacks(string msg) => LogCallbackHandler?.Invoke(msg); - public static event Action LogCallbackHandler; - private static void RunWarningCallbacks(string msg) => WarningCallbackHandler1?.Invoke(msg); - private static void RunWarningCallbacks(string namesection, string msg) => WarningCallbackHandler2?.Invoke(namesection, msg); - public static event Action WarningCallbackHandler1; - public static event Action WarningCallbackHandler2; - private static void RunErrorCallbacks(string msg) => ErrorCallbackHandler1?.Invoke(msg); - private static void RunErrorCallbacks(string namesection, string msg) => ErrorCallbackHandler2?.Invoke(namesection, msg); - public static event Action ErrorCallbackHandler1; - public static event Action ErrorCallbackHandler2; - } -} \ No newline at end of file diff --git a/MelonLoader.ModHandler/DependencyGraph.cs b/MelonLoader.ModHandler/DependencyGraph.cs index d020f0d22..42de7279b 100644 --- a/MelonLoader.ModHandler/DependencyGraph.cs +++ b/MelonLoader.ModHandler/DependencyGraph.cs @@ -62,7 +62,7 @@ private DependencyGraph(IList mods, Func modNameGetter) { if (modsWithMissingDeps.Count > 0) { // Some mods are missing dependencies. Don't load these mods and show an error message - MelonModLogger.LogWarning(BuildMissingDependencyMessage(modsWithMissingDeps)); + MelonLogger.LogWarning(BuildMissingDependencyMessage(modsWithMissingDeps)); } } @@ -74,7 +74,7 @@ private static bool TryLoad(AssemblyName assembly) { } catch (FileNotFoundException) { return false; } catch (Exception ex) { - MelonModLogger.LogError("Loading mod dependency failed: " + ex); + MelonLogger.LogError("Loading mod dependency failed: " + ex); return false; } } @@ -138,7 +138,7 @@ private void TopologicalSortInto(IList loadedMods) { errorMessage.Append($"- '{vertices[i].name}'\n"); } errorMessage.Length -= 1; // Remove trailing newline - MelonModLogger.LogError(errorMessage.ToString()); + MelonLogger.LogError(errorMessage.ToString()); } } diff --git a/MelonLoader.ModHandler/Deprecated.cs b/MelonLoader.ModHandler/Deprecated.cs new file mode 100644 index 000000000..bf3e52228 --- /dev/null +++ b/MelonLoader.ModHandler/Deprecated.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +#pragma warning disable 0108 + +namespace MelonLoader +{ + [Obsolete("Main is obsolete. Please use MelonLoaderBase or MelonHandler instead.")] + public static class Main + { + public static List Mods = null; + public static List Plugins = null; + public static bool IsVRChat = false; + public static bool IsBoneworks = false; + public static string GetUnityVersion() => MelonLoaderBase.UnityVersion; + public static string GetUserDataPath() => MelonLoaderBase.UserDataPath; + internal static void LegacySupport(List mods, List plugins, bool isVRChat, bool isBoneworks) + { + Mods = mods; + Plugins = plugins; + IsVRChat = isVRChat; + IsBoneworks = isBoneworks; + } + } + [Obsolete("MelonModGame is obsolete. Please use MelonGame instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class MelonModGameAttribute : Attribute + { + public string Developer { get; } + public string GameName { get; } + public MelonModGameAttribute(string developer = null, string gameName = null) + { + Developer = developer; + GameName = gameName; + } + internal MelonGameAttribute Convert() => new MelonGameAttribute(Developer, GameName); + } + [Obsolete("MelonModInfo is obsolete. Please use MelonInfo instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public class MelonModInfoAttribute : Attribute + { + public Type SystemType { get; } + public string Name { get; } + public string Version { get; } + public string Author { get; } + public string DownloadLink { get; } + + public MelonModInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) + { + SystemType = type; + Name = name; + Version = version; + Author = author; + DownloadLink = downloadLink; + } + internal MelonInfoAttribute Convert() => new MelonInfoAttribute(SystemType, Name, Version, Author, DownloadLink); + } + [Obsolete("MelonPluginGame is obsolete. Please use MelonGame instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class MelonPluginGameAttribute : Attribute + { + public string Developer { get; } + public string GameName { get; } + public MelonPluginGameAttribute(string developer = null, string gameName = null) + { + Developer = developer; + GameName = gameName; + } + public MelonGameAttribute Convert() => new MelonGameAttribute(Developer, GameName); + } + [Obsolete("MelonPluginInfo is obsolete. Please use MelonInfo instead.")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public class MelonPluginInfoAttribute : Attribute + { + public Type SystemType { get; } + public string Name { get; } + public string Version { get; } + public string Author { get; } + public string DownloadLink { get; } + + public MelonPluginInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) + { + SystemType = type; + Name = name; + Version = version; + Author = author; + DownloadLink = downloadLink; + } + public MelonInfoAttribute Convert() => new MelonInfoAttribute(SystemType, Name, Version, Author, DownloadLink); + } + [Obsolete("MelonModLogger is obsolete. Please use MelonLogger instead.")] + public class MelonModLogger : MelonLogger {} + [Obsolete("ModPrefs is obsolete. Please use MelonPrefs instead.")] + public class ModPrefs : MelonPrefs + { + public static Dictionary> GetPrefs() + { + Dictionary> output = new Dictionary>(); + Dictionary> prefs = GetPreferences(); + for (int i = 0; i < prefs.Values.Count; i++) + { + Dictionary prefsdict = prefs.Values.ElementAt(i); + Dictionary newprefsdict = new Dictionary(); + for (int j = 0; j < prefsdict.Values.Count; j++) + { + MelonPreference pref = prefsdict.Values.ElementAt(j); + PrefDesc newpref = new PrefDesc(pref.Value, (PrefType)pref.Type, pref.Hidden, pref.DisplayText); + newpref.ValueEdited = pref.ValueEdited; + newprefsdict.Add(prefsdict.Keys.ElementAt(j), newpref); + } + output.Add(prefs.Keys.ElementAt(i), newprefsdict); + } + return output; + } + public static void RegisterPrefString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) => RegisterString(section, name, defaultValue, displayText, hideFromList); + public static void RegisterPrefBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) => RegisterBool(section, name, defaultValue, displayText, hideFromList); + public static void RegisterPrefInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) => RegisterInt(section, name, defaultValue, displayText, hideFromList); + public static void RegisterPrefFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) => RegisterFloat(section, name, defaultValue, displayText, hideFromList); + public enum PrefType + { + STRING, + BOOL, + INT, + FLOAT + } + public class PrefDesc : MelonPreference + { + public PrefType Type { get => (PrefType)base.Type; } + public PrefDesc(string value, PrefType type, bool hidden, string displayText) : base(value, type, hidden, displayText) + { + Value = value; + ValueEdited = value; + base.Type = (MelonPreferenceType)type; + Hidden = hidden; + DisplayText = displayText; + } + } + } + } \ No newline at end of file diff --git a/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs b/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs index 12f6f30d3..4b3bfe897 100644 --- a/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs +++ b/MelonLoader.ModHandler/Harmony/HarmonyInstance.cs @@ -1,4 +1,5 @@ using Harmony.Tools; +using MelonLoader; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -93,8 +94,6 @@ private MethodBase GetOutsideCaller() throw new Exception("Unexpected end of stack trace"); } - // - public void PatchAll() { var method = new StackTrace().GetFrame(1).GetMethod(); @@ -136,7 +135,30 @@ public void UnpatchAll(string harmonyID = null) } } - public static void UnpatchAllInstances() + public void UnpatchAll(MelonBase melon) + { + bool IDCheck(Patch patchInfo) => (patchInfo.patch.Module.Assembly == melon.Assembly); + var originals = GetPatchedMethods().ToList(); + foreach (var original in originals) + { + var info = GetPatchInfo(original); + info.Postfixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch)); + info.Prefixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch)); + info.Transpilers.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch)); + } + } + + public static void UnpatchAllMelonInstances(MelonBase melon) + { + if (instancelist.Count > 0) + { + for (int i = 0; i < instancelist.Count; i++) + instancelist[i].UnpatchAll(melon); + instancelist.Clear(); + } + } + + public static void UnpatchAllInstances() { if (instancelist.Count > 0) { diff --git a/MelonLoader.ModHandler/Harmony/PatchFunctions.cs b/MelonLoader.ModHandler/Harmony/PatchFunctions.cs index c3a6cc6bd..fae3bb801 100644 --- a/MelonLoader.ModHandler/Harmony/PatchFunctions.cs +++ b/MelonLoader.ModHandler/Harmony/PatchFunctions.cs @@ -157,7 +157,7 @@ private static IEnumerable UnhollowerTranspiler(MethodBase meth } if (!found) { - MelonModLogger.LogError("Harmony transpiler could not rewrite Unhollower method. Expect a stack overflow."); + MelonLogger.LogError("Harmony transpiler could not rewrite Unhollower method. Expect a stack overflow."); return instructions; } @@ -259,13 +259,13 @@ private static DynamicMethod CreateIl2CppShim(DynamicMethod patch, MethodBase or // Catch any exceptions that may have been thrown Emitter.MarkBlockBefore(il, new ExceptionBlock(ExceptionBlockType.BeginCatchBlock, typeof(Exception)), out _); - // MelonModLogger.LogError("Exception in ...\n" + exception.ToString()); + // MelonLogger.LogError("Exception in ...\n" + exception.ToString()); Emitter.Emit(il, OpCodes.Stloc, exceptionLocal); Emitter.Emit(il, OpCodes.Ldstr, $"Exception in Harmony patch of method {original.FullDescription()}:\n"); Emitter.Emit(il, OpCodes.Ldloc, exceptionLocal); Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(Exception), "ToString", new Type[0])); Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(string), "Concat", new Type[] { typeof(string), typeof(string) })); - Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(MelonModLogger), "LogError", new Type[] { typeof(string) })); + Emitter.Emit(il, OpCodes.Call, AccessTools.DeclaredMethod(typeof(MelonLogger), "LogError", new Type[] { typeof(string) })); // Close the exception block Emitter.MarkBlockAfter(il, new ExceptionBlock(ExceptionBlockType.EndExceptionBlock, null)); diff --git a/MelonLoader.ModHandler/Imports.cs b/MelonLoader.ModHandler/Imports.cs index d271da7ad..1faf1a2a3 100644 --- a/MelonLoader.ModHandler/Imports.cs +++ b/MelonLoader.ModHandler/Imports.cs @@ -6,8 +6,6 @@ namespace MelonLoader { public static class Imports { - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void UNLOAD_MELONLOADER(); [MethodImpl(MethodImplOptions.InternalCall)] [return: MarshalAs(UnmanagedType.LPStr)] public extern static string GetCompanyName(); @@ -34,23 +32,8 @@ public static class Imports [MethodImpl(MethodImplOptions.InternalCall)] public extern static bool IsDebugMode(); [MethodImpl(MethodImplOptions.InternalCall)] - public extern static bool IsConsoleEnabled(); - [MethodImpl(MethodImplOptions.InternalCall)] public extern static void Hook(IntPtr target, IntPtr detour); [MethodImpl(MethodImplOptions.InternalCall)] public extern static void Unhook(IntPtr target, IntPtr detour); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool IsOldMono(); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool IsQuitFix(); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool IsDevModsOnly(); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool IsDevPluginsOnly(); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static bool AG_Force_Regenerate(); - [MethodImpl(MethodImplOptions.InternalCall)] - [return: MarshalAs(UnmanagedType.LPStr)] - public extern static string AG_Force_Version_Unhollower(); } } \ No newline at end of file diff --git a/MelonLoader.ModHandler/IniFile.cs b/MelonLoader.ModHandler/IniFile.cs index ff002b049..2152b5c6a 100644 --- a/MelonLoader.ModHandler/IniFile.cs +++ b/MelonLoader.ModHandler/IniFile.cs @@ -11,16 +11,62 @@ public class IniFile [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; } } - public IniFile(string INIPath) { this.Path = INIPath; } - public void IniWriteValue(string Section, string Key, string Value) { WritePrivateProfileString(Section, Key, Value, this.Path); } - public string IniReadValue(string Section, string Key) + public string Path { get { return _path; } internal set { if (!File.Exists(value)) File.WriteAllText(value, "", Encoding.Unicode); _path = value; } } + public IniFile(string INIPath) { Path = INIPath; } + private void IniWriteValue(string Section, string Key, string Value) { WritePrivateProfileString(Section, Key, Value, Path); } + private string IniReadValue(string Section, string Key) { const int MAX_CHARS = 1023; StringBuilder result = new StringBuilder(MAX_CHARS); - GetPrivateProfileString(Section, Key, " _", result, MAX_CHARS, this.Path); + GetPrivateProfileString(Section, Key, " _", result, MAX_CHARS, Path); if (result.ToString().Equals(" _")) return null; return result.ToString(); } + + public bool HasKey(string section, string name) { return IniReadValue(section, name) != null; } + + public string GetString(string section, string name, string defaultValue = "", bool autoSave = false) + { + string value = IniReadValue(section, name); + if (!string.IsNullOrEmpty(value)) + return value; + else if (autoSave) + SetString(section, name, defaultValue); + return defaultValue; + } + public void SetString(string section, string name, string value) { IniWriteValue(section, name, value.Trim()); } + + public int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false) + { + int value; + if (int.TryParse(IniReadValue(section, name), out value)) + return value; + else if (autoSave) + SetInt(section, name, defaultValue); + return defaultValue; + } + public void SetInt(string section, string name, int value) { IniWriteValue(section, name, value.ToString()); } + + public float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false) + { + float value; + if (float.TryParse(IniReadValue(section, name), out value)) + return value; + else if (autoSave) + SetFloat(section, name, defaultValue); + return defaultValue; + } + public void SetFloat(string section, string name, float value) { IniWriteValue(section, name, value.ToString()); } + + public bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false) + { + string sVal = GetString(section, name, null); + if ("true".Equals(sVal) || "1".Equals(sVal) || "0".Equals(sVal) || "false".Equals(sVal)) + return ("true".Equals(sVal) || "1".Equals(sVal)); + else if (autoSave) + SetBool(section, name, defaultValue); + return defaultValue; + } + public void SetBool(string section, string name, bool value) { IniWriteValue(section, name, value ? "true" : "false"); } } } diff --git a/MelonLoader.ModHandler/Main.cs b/MelonLoader.ModHandler/Main.cs deleted file mode 100644 index ee475c7f7..000000000 --- a/MelonLoader.ModHandler/Main.cs +++ /dev/null @@ -1,649 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using Harmony; -using MelonLoader.ICSharpCode.SharpZipLib.Zip; -#pragma warning disable 0618 - -namespace MelonLoader -{ - public static class Main - { - public static List Mods = new List(); - public static List Plugins = new List(); - public static List TempPlugins = null; - internal static MelonModGameAttribute CurrentGameAttribute = null; - public static bool IsVRChat = false; - public static bool IsBoneworks = false; - public static string UnityVersion = null; - private static Assembly Assembly_CSharp = null; - private static bool HasGeneratedAssembly = false; - - private static void Initialize() - { - if (string.IsNullOrEmpty(AppDomain.CurrentDomain.BaseDirectory)) - ((AppDomainSetup)typeof(AppDomain).GetProperty("SetupInformationNoCopy", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(AppDomain.CurrentDomain, new object[0])).ApplicationBase = Imports.GetGameDirectory(); - Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); - AppDomain.CurrentDomain.UnhandledException += ExceptionHandler; - - CurrentGameAttribute = new MelonModGameAttribute(Imports.GetCompanyName(), Imports.GetProductName()); - UnityVersion = GetUnityVersion(); - - if (Imports.IsIl2CppGame()) - { - IsVRChat = CurrentGameAttribute.IsGame("VRChat", "VRChat"); - IsBoneworks = CurrentGameAttribute.IsGame("Stress Level Zero", "BONEWORKS"); - } - - if (!Imports.IsDebugMode() -#if !DEBUG - && Imports.IsConsoleEnabled() -#endif - ) - { - Console.Enabled = true; - Console.Create(); - } - - if (!Imports.IsIl2CppGame() || AssemblyGenerator.Main.Initialize()) - HasGeneratedAssembly = true; - else - Imports.UNLOAD_MELONLOADER(); - - if (HasGeneratedAssembly) - { - LoadDLLs(true); - if (Plugins.Count > 0) - { - HashSet failedPlugins = new HashSet(); - TempPlugins = Plugins.Where(plugin => (plugin.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)).ToList(); - DependencyGraph.TopologicalSort(TempPlugins, plugin => plugin.InfoAttribute.Name); - for (int i = 0; i < TempPlugins.Count; i++) - { - MelonPlugin plugin = TempPlugins[i]; - if (plugin != null) - try { plugin.OnPreInitialization(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); failedPlugins.Add(plugin); } - } - TempPlugins.RemoveAll(plugin => failedPlugins.Contains(plugin)); - } - } - } - - private static void OnApplicationStart() - { - if (!HasGeneratedAssembly) - return; - - if (Imports.IsIl2CppGame()) - { - if (IsVRChat) - Assembly_CSharp = Assembly.Load("Assembly-CSharp"); - UnhollowerSupport.Initialize(); - } - SupportModule.Initialize(); - - MelonModLogger.Log("------------------------------"); - MelonModLogger.Log("Unity " + UnityVersion); - MelonModLogger.Log("------------------------------"); - MelonModLogger.Log("Name: " + CurrentGameAttribute.GameName); - MelonModLogger.Log("Developer: " + CurrentGameAttribute.Developer); - MelonModLogger.Log("Type: " + (Imports.IsIl2CppGame() ? "Il2Cpp" : (Imports.IsOldMono() ? "Mono" : "MonoBleedingEdge"))); - MelonModLogger.Log("------------------------------"); - MelonModLogger.Log("Using v" + BuildInfo.Version + " Open-Beta"); - MelonModLogger.Log("------------------------------"); - - LoadDLLs(); - if (Plugins.Count > 0) - { - for (int i = 0; i < Plugins.Count; i++) - { - MelonPlugin plugin = Plugins[i]; - if (plugin != null) - { - MelonModLogger.Log(plugin.InfoAttribute.Name - + (!string.IsNullOrEmpty(plugin.InfoAttribute.Version) - ? (" v" + plugin.InfoAttribute.Version) : "") - + (!string.IsNullOrEmpty(plugin.InfoAttribute.Author) - ? (" by " + plugin.InfoAttribute.Author) : "") - + (!string.IsNullOrEmpty(plugin.InfoAttribute.DownloadLink) - ? (" (" + plugin.InfoAttribute.DownloadLink + ")") - : "") - ); - MelonModLogger.LogDLLStatus(plugin.Compatibility); - MelonModLogger.Log("------------------------------"); - } - } - Plugins = TempPlugins; - } - if (Plugins.Count <= 0) - { - MelonModLogger.Log("No Plugins Loaded!"); - MelonModLogger.Log("------------------------------"); - } - - if (Mods.Count > 0) - { - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - { - MelonModLogger.Log(mod.InfoAttribute.Name - + (!string.IsNullOrEmpty(mod.InfoAttribute.Version) - ? (" v" + mod.InfoAttribute.Version) : "") - + (!string.IsNullOrEmpty(mod.InfoAttribute.Author) - ? (" by " + mod.InfoAttribute.Author) : "") - + (!string.IsNullOrEmpty(mod.InfoAttribute.DownloadLink) - ? (" (" + mod.InfoAttribute.DownloadLink + ")") - : "") - ); - MelonModLogger.LogDLLStatus(mod.Compatibility); - MelonModLogger.Log("------------------------------"); - } - } - Mods.RemoveAll((MelonMod mod) => (mod.Compatibility >= MelonBase.MelonCompatibility.INCOMPATIBLE)); - DependencyGraph.TopologicalSort(Mods, mod => mod.InfoAttribute.Name); - } - if (Mods.Count <= 0) - { - MelonModLogger.Log("No Mods Loaded!"); - MelonModLogger.Log("------------------------------"); - } - - if ((Plugins.Count > 0) || (Mods.Count > 0)) - AddUnityDebugLog(); - - if (Plugins.Count > 0) - { - HashSet failedPlugins = new HashSet(); - for (int i = 0; i < Plugins.Count; i++) - { - MelonPlugin plugin = Plugins[i]; - if (plugin != null) - try { InitializeModOrPlugin(plugin); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); failedPlugins.Add(plugin); } - } - Plugins.RemoveAll(plugin => failedPlugins.Contains(plugin)); - } - - if (Mods.Count > 0) - { - HashSet failedMods = new HashSet(); - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { InitializeModOrPlugin(mod); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); failedMods.Add(mod); } - } - Mods.RemoveAll(mod => failedMods.Contains(mod)); - } - - if ((Plugins.Count <= 0) && (Mods.Count <= 0)) - SupportModule.Destroy(); - } - - private static void InitializeModOrPlugin(MelonBase modOrPlugin) { - string harmonyId = modOrPlugin.Assembly.FullName; - HarmonyInstance harmony = HarmonyInstance.Create(harmonyId); - try - { - harmony.PatchAll(modOrPlugin.Assembly); - modOrPlugin.OnApplicationStart(); - } - catch (Exception) - { - harmony.UnpatchAll(harmonyId); - throw; - } - } - - public static void OnApplicationQuit() - { - if (Plugins.Count > 0) - { - for (int i = 0; i < Plugins.Count; i++) - { - MelonPlugin plugin = Plugins[i]; - if (plugin != null) - try { plugin.OnApplicationQuit(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); } - } - } - if (Mods.Count > 0) - { - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnApplicationQuit(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - if ((Plugins.Count > 0) || (Mods.Count > 0)) - ModPrefs.SaveConfig(); - Harmony.HarmonyInstance.UnpatchAllInstances(); - Imports.UNLOAD_MELONLOADER(); - if (Imports.IsQuitFix()) Process.GetCurrentProcess().Kill(); - } - - public static void OnModSettingsApplied() - { - if (Plugins.Count > 0) - for (int i = 0; i < Plugins.Count; i++) - { - MelonPlugin plugin = Plugins[i]; - if (plugin != null) - try { plugin.OnModSettingsApplied(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), plugin.InfoAttribute.Name); } - } - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnModSettingsApplied(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - public static void OnUpdate() - { - SceneHandler.CheckForSceneChange(); - if (Imports.IsIl2CppGame() && IsVRChat) - VRChat_CheckUiManager(); - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnUpdate(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - public static void OnFixedUpdate() - { - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnFixedUpdate(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - public static void OnLateUpdate() - { - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnLateUpdate(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - public static void OnGUI() - { - if (Mods.Count > 0) - { - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnGUI(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - } - - internal static void OnLevelIsLoading() - { - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnLevelIsLoading(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - internal static void OnLevelWasLoaded(int level) - { - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnLevelWasLoaded(level); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - internal static void OnLevelWasInitialized(int level) - { - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.OnLevelWasInitialized(level); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - - private static bool ShouldCheckForUiManager = true; - private static Type VRCUiManager = null; - private static PropertyInfo VRCUiManager_Instance = null; - private static void VRChat_CheckUiManager() - { - if (ShouldCheckForUiManager) - { - if (VRCUiManager == null) - VRCUiManager = Assembly_CSharp.GetType("VRCUiManager"); - if (VRCUiManager != null) - { - if (VRCUiManager_Instance == null) - VRCUiManager_Instance = VRCUiManager.GetProperty("field_Protected_Static_VRCUiManager_0"); - if (VRCUiManager_Instance != null) - { - object returnval = VRCUiManager_Instance.GetValue(null, new object[0]); - if (returnval != null) - { - ShouldCheckForUiManager = false; - if (Mods.Count > 0) - for (int i = 0; i < Mods.Count; i++) - { - MelonMod mod = Mods[i]; - if (mod != null) - try { mod.VRChat_OnUiManagerInit(); } catch (Exception ex) { MelonModLogger.LogDLLError(ex.ToString(), mod.InfoAttribute.Name); } - } - } - } - } - } - } - - private static void LoadDLLs(bool plugins = false) - { - string searchdir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, (plugins ? "Plugins" : "Mods")); - if (!Directory.Exists(searchdir)) - Directory.CreateDirectory(searchdir); - else - { - // DLL - string[] files = Directory.GetFiles(searchdir, "*.dll"); - if (files.Length > 0) - { - for (int i = 0; i < files.Length; i++) - { - string file = files[i]; - if (!string.IsNullOrEmpty(file)) - { - if (plugins) - { - if ((Imports.IsDevPluginsOnly() && !file.EndsWith("-dev.dll")) || (!Imports.IsDevPluginsOnly() && file.EndsWith("-dev.dll"))) - continue; - } - else - { - if ((Imports.IsDevModsOnly() && !file.EndsWith("-dev.dll")) || (!Imports.IsDevModsOnly() && file.EndsWith("-dev.dll"))) - continue; - } - try - { - LoadAssembly(File.ReadAllBytes(file), plugins, file); - } - catch (Exception e) - { - MelonModLogger.LogError("Unable to load " + file + ":\n" + e.ToString()); - MelonModLogger.Log("------------------------------"); - } - } - } - } - - // ZIP - string[] zippedFiles = Directory.GetFiles(searchdir, "*.zip"); - if (zippedFiles.Length > 0) - { - for (int i = 0; i < zippedFiles.Length; i++) - { - string file = zippedFiles[i]; - if (!string.IsNullOrEmpty(file)) - { - try - { - using (var fileStream = File.OpenRead(file)) - { - using (var zipInputStream = new ZipInputStream(fileStream)) - { - ZipEntry entry; - while ((entry = zipInputStream.GetNextEntry()) != null) - { - string filename = Path.GetFileName(entry.Name); - if (string.IsNullOrEmpty(filename) || !filename.EndsWith(".dll")) - continue; - - if (plugins) - { - if ((Imports.IsDevPluginsOnly() && !filename.EndsWith("-dev.dll")) || (!Imports.IsDevPluginsOnly() && filename.EndsWith("-dev.dll"))) - continue; - } - else - { - if ((Imports.IsDevModsOnly() && !filename.EndsWith("-dev.dll")) || (!Imports.IsDevModsOnly() && filename.EndsWith("-dev.dll"))) - continue; - } - - using (var unzippedFileStream = new MemoryStream()) - { - int size = 0; - byte[] buffer = new byte[4096]; - while (true) - { - size = zipInputStream.Read(buffer, 0, buffer.Length); - if (size > 0) - unzippedFileStream.Write(buffer, 0, size); - else - break; - } - LoadAssembly(unzippedFileStream.ToArray(), plugins, (file + "/" + filename)); - } - } - } - } - } - catch (Exception e) - { - MelonModLogger.LogError("Unable to load " + file + ":\n" + e.ToString()); - MelonModLogger.Log("------------------------------"); - } - } - } - } - } - } - - private static void LoadDLLFromAssembly(Assembly assembly, bool isPlugin = false, string filelocation = null) - { - if (isPlugin) - LoadPluginFromAssembly(assembly, filelocation); - else - LoadModFromAssembly(assembly, filelocation); - } - - private static void LoadPluginFromAssembly(Assembly assembly, string filelocation = null) - { - MelonPluginInfoAttribute pluginInfoAttribute = assembly.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonPluginInfoAttribute))) as MelonPluginInfoAttribute; - if ((pluginInfoAttribute != null) && (pluginInfoAttribute.SystemType != null) && pluginInfoAttribute.SystemType.IsSubclassOf(typeof(MelonPlugin))) - { - bool isCompatible = false; - bool isUniversal = false; - bool hasAttribute = true; - MelonPluginGameAttribute[] pluginGameAttributes = assembly.GetCustomAttributes(typeof(MelonPluginGameAttribute), true) as MelonPluginGameAttribute[]; - int pluginGameAttributes_Count = pluginGameAttributes.Length; - if (pluginGameAttributes_Count > 0) - { - for (int i = 0; i < pluginGameAttributes_Count; i++) - { - MelonPluginGameAttribute pluginGameAttribute = pluginGameAttributes[i]; - if (CurrentGameAttribute.IsCompatible(pluginGameAttribute)) - { - isCompatible = true; - isUniversal = CurrentGameAttribute.IsCompatibleBecauseUniversal(pluginGameAttribute); - break; - } - } - } - else - hasAttribute = false; - try - { - MelonPlugin pluginInstance = Activator.CreateInstance(pluginInfoAttribute.SystemType) as MelonPlugin; - if (pluginInstance != null) - { - pluginInstance.InfoAttribute = pluginInfoAttribute; - if (pluginGameAttributes_Count > 0) - pluginInstance.GameAttributes = pluginGameAttributes; - else - pluginInstance.GameAttributes = null; - pluginInstance.Location = filelocation; - pluginInstance.Compatibility = (isUniversal ? MelonBase.MelonCompatibility.UNIVERSAL : (isCompatible ? MelonBase.MelonCompatibility.COMPATIBLE : (!hasAttribute ? MelonBase.MelonCompatibility.NOATTRIBUTE : MelonBase.MelonCompatibility.INCOMPATIBLE))); - if (pluginInstance.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE) - { - pluginInstance.Assembly = assembly; - } - Plugins.Add(pluginInstance); - } - else - MelonModLogger.LogError("Unable to load Plugin in " + assembly.GetName() + "! Failed to Create Instance!"); - } - catch (Exception e) { MelonModLogger.LogError("Unable to load Plugin in " + assembly.GetName() + "! " + e.ToString()); } - } - } - - private static void LoadModFromAssembly(Assembly assembly, string filelocation = null) - { - MelonModInfoAttribute modInfoAttribute = assembly.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonModInfoAttribute))) as MelonModInfoAttribute; - if ((modInfoAttribute != null) && (modInfoAttribute.SystemType != null) && modInfoAttribute.SystemType.IsSubclassOf(typeof(MelonMod))) - { - bool isCompatible = false; - bool isUniversal = false; - bool hasAttribute = true; - MelonModGameAttribute[] modGameAttributes = assembly.GetCustomAttributes(typeof(MelonModGameAttribute), true) as MelonModGameAttribute[]; - int modGameAttributes_Count = modGameAttributes.Length; - if (modGameAttributes_Count > 0) - { - for (int i = 0; i < modGameAttributes_Count; i++) - { - MelonModGameAttribute modGameAttribute = modGameAttributes[i]; - if (CurrentGameAttribute.IsCompatible(modGameAttribute)) - { - isCompatible = true; - isUniversal = CurrentGameAttribute.IsCompatibleBecauseUniversal(modGameAttribute); - break; - } - } - } - else - hasAttribute = false; - try - { - MelonMod modInstance = Activator.CreateInstance(modInfoAttribute.SystemType) as MelonMod; - if (modInstance != null) - { - modInstance.InfoAttribute = modInfoAttribute; - if (modGameAttributes_Count > 0) - modInstance.GameAttributes = modGameAttributes; - else - modInstance.GameAttributes = null; - modInstance.Location = filelocation; - modInstance.Compatibility = (isUniversal ? MelonBase.MelonCompatibility.UNIVERSAL : (isCompatible ? MelonBase.MelonCompatibility.COMPATIBLE : (!hasAttribute ? MelonBase.MelonCompatibility.NOATTRIBUTE : MelonBase.MelonCompatibility.INCOMPATIBLE))); - if (modInstance.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE) - { - modInstance.Assembly = assembly; - } - Mods.Add(modInstance); - } - else - MelonModLogger.LogError("Unable to load Mod in " + assembly.GetName() + "! Failed to Create Instance!"); - } - catch (Exception e) { MelonModLogger.LogError("Unable to load Mod in " + assembly.GetName() + "! " + e.ToString()); } - } - } - - private static void LoadAssembly(string file, bool isPlugin = false) => LoadDLLFromAssembly(Assembly.LoadFrom(file), isPlugin, file); - private static void LoadAssembly(byte[] data, bool isPlugin = false, string filelocation = null) => LoadDLLFromAssembly(Assembly.Load(data), isPlugin, filelocation); - private static void LoadAssembly(Assembly asm, bool isPlugin = false, string filelocation = null) - { - if (!asm.Equals(null)) - LoadDLLFromAssembly(asm, isPlugin, filelocation); - else - MelonModLogger.LogError("Unable to load " + asm); - } - - private static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e) => MelonModLogger.LogError((e.ExceptionObject as Exception).ToString()); - - internal static string GetUnityVersion() - { - string exepath = Imports.GetExePath(); - string ggm_path = Path.Combine(Imports.GetGameDataDirectory(), "globalgamemanagers"); - if (!File.Exists(ggm_path)) - { - FileVersionInfo versioninfo = FileVersionInfo.GetVersionInfo(exepath); - if ((versioninfo == null) || string.IsNullOrEmpty(versioninfo.FileVersion)) - return "UNKNOWN"; - return versioninfo.FileVersion.Substring(0, versioninfo.FileVersion.LastIndexOf('.')); - } - else - { - byte[] ggm_bytes = File.ReadAllBytes(ggm_path); - if ((ggm_bytes == null) || (ggm_bytes.Length <= 0)) - return "UNKNOWN"; - int start_position = 0; - for (int i = 10; i < ggm_bytes.Length; i++) - { - byte pos_byte = ggm_bytes[i]; - if ((pos_byte <= 0x39) && (pos_byte >= 0x30)) - { - start_position = i; - break; - } - } - if (start_position == 0) - return "UNKNOWN"; - int end_position = 0; - for (int i = start_position; i < ggm_bytes.Length; i++) - { - byte pos_byte = ggm_bytes[i]; - if ((pos_byte != 0x2E) && ((pos_byte > 0x39) || (pos_byte < 0x30))) - { - end_position = (i - 1); - break; - } - } - if (end_position == 0) - return "UNKNOWN"; - int verstr_byte_pos = 0; - byte[] verstr_byte = new byte[((end_position - start_position) + 1)]; - for (int i = start_position; i <= end_position; i++) - { - verstr_byte[verstr_byte_pos] = ggm_bytes[i]; - verstr_byte_pos++; - } - return Encoding.UTF8.GetString(verstr_byte, 0, verstr_byte.Length); - } - } - - private static void AddUnityDebugLog() - { - SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------"); - SupportModule.UnityDebugLog("~ This Game has been MODIFIED using MelonLoader. DO NOT report any issues to the Developers! ~"); - SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------"); - } - - public static string GetUserDataPath() - { - string userDataDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "UserData"); - if (!Directory.Exists(userDataDir)) - Directory.CreateDirectory(userDataDir); - return userDataDir; - } - } -} \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonBase.cs b/MelonLoader.ModHandler/MelonBase.cs index f2fd10726..4849bc52c 100644 --- a/MelonLoader.ModHandler/MelonBase.cs +++ b/MelonLoader.ModHandler/MelonBase.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace MelonLoader { @@ -7,7 +10,7 @@ public abstract class MelonBase /// /// Gets the Assembly of the Mod or Plugin. /// - public System.Reflection.Assembly Assembly { get; internal set; } + public Assembly Assembly { get; internal set; } /// /// Gets the File Location of the Mod or Plugin. @@ -15,7 +18,7 @@ public abstract class MelonBase public string Location { get; internal set; } /// - /// Enum for DLL Compatibility + /// Enum for Melon Compatibility /// public enum MelonCompatibility { @@ -30,9 +33,43 @@ public enum MelonCompatibility /// public MelonCompatibility Compatibility { get; internal set; } + /// + /// Gets the Info Attribute of the Mod or Plugin. + /// + public MelonInfoAttribute Info { get; internal set; } + + [Obsolete()] + internal MelonModInfoAttribute LegacyModInfo { get; set; } + [Obsolete()] + internal MelonPluginInfoAttribute LegacyPluginInfo { get; set; } + + /// + /// Gets the Game Attributes of the Mod or Plugin. + /// + public MelonOptionalDependenciesAttribute OptionalDependenciesAttribute { get; internal set; } + + /// + /// Gets the Game Attributes of the Mod or Plugin. + /// + public MelonGameAttribute[] Games { get; internal set; } + + /// + /// Gets the Auto-Created Harmony Instance of the Mod or Plugin. + /// + public Harmony.HarmonyInstance harmonyInstance { get; internal set; } + + [Obsolete()] + internal MelonModGameAttribute[] LegacyModGames { get; set; } + [Obsolete()] + internal MelonPluginGameAttribute[] LegacyPluginGames { get; set; } + public virtual void OnApplicationStart() { } + public virtual void OnUpdate() { } + public virtual void OnLateUpdate() { } + public virtual void OnGUI() { } public virtual void OnApplicationQuit() { } public virtual void OnModSettingsApplied() { } + public virtual void VRChat_OnUiManagerInit() { } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] @@ -41,11 +78,221 @@ public class MelonOptionalDependenciesAttribute : Attribute /// /// The (simple) assembly names of the dependencies that should be regarded as optional. /// - public string[] AssemblyNames { get; } + public string[] AssemblyNames { get; internal set; } public MelonOptionalDependenciesAttribute(params string[] assemblyNames) { AssemblyNames = assemblyNames; } } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] + public class MelonInfoAttribute : Attribute + { + /// + /// Gets the System.Type of the Mod. + /// + public Type SystemType { get; internal set; } + + /// + /// Gets the Name of the Mod. + /// + public string Name { get; internal set; } + + /// + /// Gets the Version of the Mod. + /// + public string Version { get; internal set; } + + /// + /// Gets the Author of the Mod. + /// + public string Author { get; internal set; } + + /// + /// Gets the Download Link of the Mod. + /// + public string DownloadLink { get; internal set; } + + public MelonInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) + { + SystemType = type; + Name = name; + Version = version; + Author = author; + DownloadLink = downloadLink; + } + + [Obsolete()] + internal MelonModInfoAttribute ConvertLegacy_Mod() => new MelonModInfoAttribute(SystemType, Name, Version, Author, DownloadLink); + [Obsolete()] + internal MelonPluginInfoAttribute ConvertLegacy_Plugin() => new MelonPluginInfoAttribute(SystemType, Name, Version, Author, DownloadLink); + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class MelonGameAttribute : Attribute + { + /// + /// Gets the target Developer + /// + public string Developer { get; internal set; } + + /// + /// Gets target Game Name + /// + public string GameName { get; internal set; } + + /// + /// Gets whether this Mod can target any Game. + /// + public bool Universal { get => string.IsNullOrEmpty(Developer) || string.IsNullOrEmpty(GameName); } + + /// + /// Mark this Mod as Universal or Compatible with specific Games. + /// + public MelonGameAttribute(string developer = null, string gameName = null) + { + Developer = developer; + GameName = gameName; + } + + [Obsolete()] + internal MelonModGameAttribute ConvertLegacy_Mod() => new MelonModGameAttribute(Developer, GameName); + [Obsolete()] + internal MelonPluginGameAttribute ConvertLegacy_Plugin() => new MelonPluginGameAttribute(Developer, GameName); + + public bool IsGame(string developer, string gameName) => (Universal || ((developer != null) && (gameName != null) && Developer.Equals(developer) && GameName.Equals(gameName))); + public bool IsCompatible(MelonGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName))); + public bool IsCompatibleBecauseUniversal(MelonGameAttribute att) => ((att == null) || Universal || att.Universal); + } + + [Obsolete()] + internal class MelonLegacyAttributeSupport + { + internal class Response_Info + { + internal MelonInfoAttribute Default; + [Obsolete()] + internal MelonModInfoAttribute Legacy_Mod; + [Obsolete()] + internal MelonPluginInfoAttribute Legacy_Plugin; + internal Response_Info(MelonInfoAttribute def, MelonModInfoAttribute legacy_mod, MelonPluginInfoAttribute legacy_plugin) + { + Default = def; + Legacy_Mod = legacy_mod; + Legacy_Plugin = legacy_plugin; + } + internal void SetupMelon(MelonBase baseInstance) + { + if (Default != null) + baseInstance.Info = Default; + if (Legacy_Mod != null) + baseInstance.LegacyModInfo = Legacy_Mod; + if (Legacy_Plugin != null) + baseInstance.LegacyPluginInfo = Legacy_Plugin; + } + } + + internal class Response_Game + { + internal MelonGameAttribute[] Default; + internal MelonModGameAttribute[] Legacy_Mod; + internal MelonPluginGameAttribute[] Legacy_Plugin; + internal Response_Game(MelonGameAttribute[] def, MelonModGameAttribute[] legacy_mod, MelonPluginGameAttribute[] legacy_plugin) + { + Default = def; + Legacy_Mod = legacy_mod; + Legacy_Plugin = legacy_plugin; + } + internal void SetupMelon(MelonBase baseInstance) + { + if (Default.Length > 0) + baseInstance.Games = Default; + if (Legacy_Mod.Length > 0) + baseInstance.LegacyModGames = Legacy_Mod; + if (Legacy_Plugin.Length > 0) + baseInstance.LegacyPluginGames = Legacy_Plugin; + } + } + + internal static Response_Info GetMelonInfoAttribute(Assembly asm, bool isPlugin = false) + { + MelonInfoAttribute def = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonInfoAttribute))) as MelonInfoAttribute; + MelonModInfoAttribute legacy_mod = null; + MelonPluginInfoAttribute legacy_plugin = null; + if (def == null) + { + if (isPlugin) + { + legacy_plugin = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonPluginInfoAttribute))) as MelonPluginInfoAttribute; + if ((legacy_plugin != null) && (def == null)) + def = legacy_plugin.Convert(); + } + else + { + legacy_mod = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonModInfoAttribute))) as MelonModInfoAttribute; + if ((legacy_mod != null) && (def == null)) + def = legacy_mod.Convert(); + } + } + else + { + if (isPlugin) + legacy_plugin = def.ConvertLegacy_Plugin(); + else + legacy_mod = def.ConvertLegacy_Mod(); + } + return new Response_Info(def, legacy_mod, legacy_plugin); + } + + internal static Response_Game GetMelonGameAttributes(Assembly asm, bool isPlugin = false) + { + MelonGameAttribute[] def = asm.GetCustomAttributes(typeof(MelonGameAttribute), true) as MelonGameAttribute[]; + MelonModGameAttribute[] legacy_mod = new MelonModGameAttribute[0]; + MelonPluginGameAttribute[] legacy_plugin = new MelonPluginGameAttribute[0]; + if (def.Length <= 0) + { + if (isPlugin) + { + legacy_plugin = asm.GetCustomAttributes(typeof(MelonPluginGameAttribute), true) as MelonPluginGameAttribute[]; + if (legacy_plugin.Length > 0) + { + List deflist = new List(); + foreach (MelonPluginGameAttribute att in legacy_plugin) + deflist.Add(att.Convert()); + def = deflist.ToArray(); + } + } + else + { + legacy_mod = asm.GetCustomAttributes(typeof(MelonModGameAttribute), true) as MelonModGameAttribute[]; + if (legacy_mod.Length > 0) + { + List deflist = new List(); + foreach (MelonModGameAttribute att in legacy_mod) + deflist.Add(att.Convert()); + def = deflist.ToArray(); + } + } + } + else + { + if (isPlugin) + { + List legacy_pluginlist = new List(); + foreach (MelonGameAttribute att in def) + legacy_pluginlist.Add(att.ConvertLegacy_Plugin()); + legacy_plugin = legacy_pluginlist.ToArray(); + } + else + { + List legacy_modlist = new List(); + foreach (MelonGameAttribute att in def) + legacy_modlist.Add(att.ConvertLegacy_Mod()); + legacy_mod = legacy_modlist.ToArray(); + } + } + return new Response_Game(def, legacy_mod, legacy_plugin); + } + } } diff --git a/MelonLoader.ModHandler/MelonConsole.cs b/MelonLoader.ModHandler/MelonConsole.cs new file mode 100644 index 000000000..2f27b2522 --- /dev/null +++ b/MelonLoader.ModHandler/MelonConsole.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +namespace MelonLoader +{ + public class MelonConsole + { + public static bool Enabled { get; internal set; } + + internal static void Check() + { + if (!Imports.IsDebugMode() +#if !DEBUG + && IsConsoleEnabled() +#endif + ) + Create(); + } + + private static void Create() + { + Enabled = true; + Allocate(); + Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }); + Console.SetIn(new StreamReader(Console.OpenStandardInput())); + SetTitle(BuildInfo.Name + " v" + BuildInfo.Version + " Open-Beta"); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static bool IsConsoleEnabled(); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void Allocate(); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void SetTitle(string title); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void SetColor(ConsoleColor color); + + internal static void RunLogCallbacks(string namesection, string msg) => LogCallbackHandler?.Invoke(namesection, msg); + public static event Action LogCallbackHandler; + internal static void RunWarningCallbacks(string namesection, string msg) => WarningCallbackHandler?.Invoke(namesection, msg); + public static event Action WarningCallbackHandler; + internal static void RunErrorCallbacks(string namesection, string msg) => ErrorCallbackHandler?.Invoke(namesection, msg); + public static event Action ErrorCallbackHandler; + } +} \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonHandler.cs b/MelonLoader.ModHandler/MelonHandler.cs new file mode 100644 index 000000000..6a9efa47d --- /dev/null +++ b/MelonLoader.ModHandler/MelonHandler.cs @@ -0,0 +1,394 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using Harmony; +using MelonLoader.ICSharpCode.SharpZipLib.Zip; +#pragma warning disable 0612 +#pragma warning disable 0618 + +namespace MelonLoader +{ + public static class MelonHandler + { + internal static bool HasMelons = false; + internal static Assembly Assembly_CSharp = null; + private static List _TempPlugins = null; + internal static List _Plugins = new List(); + public static List Plugins { get => _Plugins; } + internal static List _Mods = new List(); + public static List Mods { get => _Mods; } + + internal static void LoadAll(bool plugins = false) + { + string searchdir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, (plugins ? "Plugins" : "Mods")); + if (!Directory.Exists(searchdir)) + { + Directory.CreateDirectory(searchdir); + return; + } + LoadMode loadmode = (plugins ? GetLoadMode_Plugins() : GetLoadMode_Mods()); + + // DLL + string[] files = Directory.GetFiles(searchdir, "*.dll"); + if (files.Length > 0) + { + for (int i = 0; i < files.Length; i++) + { + string file = files[i]; + if (string.IsNullOrEmpty(file)) + continue; + + bool file_extension_check = Path.GetFileNameWithoutExtension(file).EndsWith("-dev"); + if ((loadmode != LoadMode.BOTH) && ((loadmode == LoadMode.DEV) ? !file_extension_check : file_extension_check)) + continue; + + try + { + LoadFromFile(file, plugins); + } + catch (Exception e) + { + MelonLogger.LogError("Unable to load " + file + ":\n" + e.ToString()); + MelonLogger.Log("------------------------------"); + } + } + } + + // ZIP + string[] zippedFiles = Directory.GetFiles(searchdir, "*.zip"); + if (zippedFiles.Length > 0) + { + for (int i = 0; i < zippedFiles.Length; i++) + { + string file = zippedFiles[i]; + if (string.IsNullOrEmpty(file)) + continue; + try + { + using (var fileStream = File.OpenRead(file)) + { + using (var zipInputStream = new ZipInputStream(fileStream)) + { + ZipEntry entry; + while ((entry = zipInputStream.GetNextEntry()) != null) + { + string filename = Path.GetFileName(entry.Name); + if (string.IsNullOrEmpty(filename) || !filename.EndsWith(".dll")) + continue; + + bool file_extension_check = Path.GetFileNameWithoutExtension(file).EndsWith("-dev"); + if ((loadmode != LoadMode.BOTH) && ((loadmode == LoadMode.DEV) ? !file_extension_check : file_extension_check)) + continue; + + using (var unzippedFileStream = new MemoryStream()) + { + int size = 0; + byte[] buffer = new byte[4096]; + while (true) + { + size = zipInputStream.Read(buffer, 0, buffer.Length); + if (size > 0) + unzippedFileStream.Write(buffer, 0, size); + else + break; + } + LoadFromAssembly(Assembly.Load(unzippedFileStream.ToArray()), plugins, (file + "/" + filename)); + } + } + } + } + } + catch (Exception e) + { + MelonLogger.LogError("Unable to load " + file + ":\n" + e.ToString()); + MelonLogger.Log("------------------------------"); + } + } + } + Main.LegacySupport(_Mods, _Plugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks); + } + + public static void LoadFromFile(string filelocation, bool isPlugin = false) => LoadFromAssembly((Imports.IsDebugMode() ? Assembly.LoadFrom(filelocation) : Assembly.Load(File.ReadAllBytes(filelocation))), isPlugin, filelocation); + public static void LoadFromAssembly(Assembly asm, bool isPlugin = false, string filelocation = null) + { + if (!asm.Equals(null)) + { + MelonLegacyAttributeSupport.Response_Info response_Info = MelonLegacyAttributeSupport.GetMelonInfoAttribute(asm, isPlugin); + MelonInfoAttribute InfoAttribute = response_Info.Default; + if ((InfoAttribute != null) && (InfoAttribute.SystemType != null) && InfoAttribute.SystemType.IsSubclassOf((isPlugin ? typeof(MelonPlugin) : typeof(MelonMod)))) + { + bool isCompatible = false; + bool isUniversal = false; + bool hasAttribute = true; + MelonLegacyAttributeSupport.Response_Game response_Game = MelonLegacyAttributeSupport.GetMelonGameAttributes(asm, isPlugin); + MelonGameAttribute[] GameAttributes = response_Game.Default; + int GameAttributes_Count = GameAttributes.Length; + if (GameAttributes_Count > 0) + { + for (int i = 0; i < GameAttributes_Count; i++) + { + MelonGameAttribute GameAttribute = GameAttributes[i]; + if (MelonLoaderBase.CurrentGameAttribute.IsCompatible(GameAttribute)) + { + isCompatible = true; + isUniversal = MelonLoaderBase.CurrentGameAttribute.IsCompatibleBecauseUniversal(GameAttribute); + break; + } + } + } + else + hasAttribute = false; + MelonBase baseInstance = Activator.CreateInstance(InfoAttribute.SystemType) as MelonBase; + if (baseInstance != null) + { + response_Info.SetupMelon(baseInstance); + response_Game.SetupMelon(baseInstance); + baseInstance.OptionalDependenciesAttribute = asm.GetCustomAttributes(false).FirstOrDefault(x => (x.GetType() == typeof(MelonOptionalDependenciesAttribute))) as MelonOptionalDependenciesAttribute; + baseInstance.Location = filelocation; + baseInstance.Compatibility = (isUniversal ? MelonBase.MelonCompatibility.UNIVERSAL : + (isCompatible ? MelonBase.MelonCompatibility.COMPATIBLE : + (!hasAttribute ? MelonBase.MelonCompatibility.NOATTRIBUTE : MelonBase.MelonCompatibility.INCOMPATIBLE) + ) + ); + if (baseInstance.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE) + { + baseInstance.Assembly = asm; + baseInstance.harmonyInstance = HarmonyInstance.Create(asm.FullName); + } + if (isPlugin) + _Plugins.Add((MelonPlugin)baseInstance); + else + _Mods.Add((MelonMod)baseInstance); + } + else + MelonLogger.LogError("Unable to load " + asm.GetName() + "! Failed to Create Instance!"); + } + } + } + + internal static void LogAndPrune() + { + if (_Plugins.Count > 0) + { + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + LogMelonInfo(_Plugins[i]); + _Plugins = _TempPlugins; + } + if (_Plugins.Count <= 0) + { + MelonLogger.Log("No Plugins Loaded!"); + MelonLogger.Log("------------------------------"); + } + else + HasMelons = true; + + if (_Mods.Count > 0) + { + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + LogMelonInfo(_Mods[i]); + _Mods.RemoveAll((MelonMod mod) => ((mod == null) || (mod.Compatibility >= MelonBase.MelonCompatibility.INCOMPATIBLE))); + DependencyGraph.TopologicalSort(_Mods, mod => mod.Info.Name); + } + if (_Mods.Count <= 0) + { + MelonLogger.Log("No Mods Loaded!"); + MelonLogger.Log("------------------------------"); + } + else + HasMelons = true; + } + + private static void LogMelonInfo(MelonBase melon) + { + MelonLogger.Log(melon.Info.Name + + (!string.IsNullOrEmpty(melon.Info.Version) + ? (" v" + melon.Info.Version) : "") + + (!string.IsNullOrEmpty(melon.Info.Author) + ? (" by " + melon.Info.Author) : "") + + (!string.IsNullOrEmpty(melon.Info.DownloadLink) + ? (" (" + melon.Info.DownloadLink + ")") + : "") + ); + MelonLogger.LogMelonCompatibility(melon.Compatibility); + MelonLogger.Log("------------------------------"); + } + + internal static void OnPreInitialization() + { + if (_Plugins.Count > 0) + { + HashSet failedPlugins = new HashSet(); + _TempPlugins = _Plugins.Where(plugin => (plugin.Compatibility < MelonBase.MelonCompatibility.INCOMPATIBLE)).ToList(); + DependencyGraph.TopologicalSort(_TempPlugins, plugin => plugin.Info.Name); + for (int i = 0; i < _TempPlugins.Count; i++) + if (_TempPlugins[i] != null) + try { _TempPlugins[i].OnPreInitialization(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _TempPlugins[i].Info.Name); failedPlugins.Add(_TempPlugins[i]); } + _TempPlugins.RemoveAll(plugin => ((plugin == null) || failedPlugins.Contains(plugin))); + Main.LegacySupport(_Mods, _TempPlugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks); + } + } + + internal static void OnApplicationStart() + { + if (_Plugins.Count > 0) + { + HashSet failedPlugins = new HashSet(); + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + try { _Plugins[i].harmonyInstance.PatchAll(_Plugins[i].Assembly); _Plugins[i].OnApplicationStart(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); HarmonyInstance.UnpatchAllMelonInstances(_Plugins[i]); failedPlugins.Add(_Plugins[i]); } + _Plugins.RemoveAll(plugin => ((plugin == null) || failedPlugins.Contains(plugin))); + Main.LegacySupport(_Mods, _Plugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks); + } + if (_Mods.Count > 0) + { + HashSet failedMods = new HashSet(); + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].harmonyInstance.PatchAll(_Mods[i].Assembly); _Mods[i].OnApplicationStart(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); HarmonyInstance.UnpatchAllMelonInstances(_Mods[i]); failedMods.Add(_Mods[i]); } + _Mods.RemoveAll(mod => ((mod == null) || failedMods.Contains(mod))); + Main.LegacySupport(_Mods, _Plugins, MelonLoaderBase._IsVRChat, MelonLoaderBase._IsBoneworks); + } + } + + public static void OnApplicationQuit() + { + if (_Plugins.Count > 0) + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + try { _Plugins[i].OnApplicationQuit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); } + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnApplicationQuit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + MelonLoaderBase.Quit(); + } + + public static void OnModSettingsApplied() + { + if (_Plugins.Count > 0) + for (int i = 0; i < _Plugins.Count; i++) + try { _Plugins[i].OnModSettingsApplied(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); } + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnModSettingsApplied(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + public static void OnUpdate() + { + SceneHandler.CheckForSceneChange(); + if (Imports.IsIl2CppGame() && MelonLoaderBase._IsVRChat) + VRChat_CheckUiManager(); + if (_Plugins.Count > 0) + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + try { _Plugins[i].OnUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); } + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + public static void OnFixedUpdate() + { + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnFixedUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + public static void OnLateUpdate() + { + if (_Plugins.Count > 0) + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + try { _Plugins[i].OnLateUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); } + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnLateUpdate(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + public static void OnGUI() + { + if (_Plugins.Count > 0) + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + try { _Plugins[i].OnGUI(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); } + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnGUI(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + internal static void OnLevelIsLoading() + { + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnLevelIsLoading(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + internal static void OnLevelWasLoaded(int level) + { + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnLevelWasLoaded(level); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + internal static void OnLevelWasInitialized(int level) + { + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].OnLevelWasInitialized(level); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + } + + private static bool ShouldCheckForUiManager = true; + private static Type VRCUiManager = null; + private static PropertyInfo VRCUiManager_Instance = null; + private static void VRChat_CheckUiManager() + { + if (!ShouldCheckForUiManager) + return; + if (VRCUiManager == null) + VRCUiManager = Assembly_CSharp.GetType("VRCUiManager"); + if (VRCUiManager == null) + return; + if (VRCUiManager_Instance == null) + VRCUiManager_Instance = VRCUiManager.GetProperty("field_Protected_Static_VRCUiManager_0"); + if (VRCUiManager_Instance == null) + return; + object returnval = VRCUiManager_Instance.GetValue(null, new object[0]); + if (returnval == null) + return; + ShouldCheckForUiManager = false; + if (_Mods.Count > 0) + for (int i = 0; i < _Mods.Count; i++) + if (_Mods[i] != null) + try { _Mods[i].VRChat_OnUiManagerInit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Mods[i].Info.Name); } + if (_Plugins.Count > 0) + for (int i = 0; i < _Plugins.Count; i++) + if (_Plugins[i] != null) + try { _Plugins[i].VRChat_OnUiManagerInit(); } catch (Exception ex) { MelonLogger.LogMelonError(ex.ToString(), _Plugins[i].Info.Name); } + } + + private enum LoadMode + { + NORMAL, + DEV, + BOTH + } + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static LoadMode GetLoadMode_Plugins(); + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static LoadMode GetLoadMode_Mods(); + } +} diff --git a/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj b/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj index d2e4c1a57..a5e4e6a28 100644 --- a/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj +++ b/MelonLoader.ModHandler/MelonLoader.ModHandler.csproj @@ -43,8 +43,9 @@ - + + @@ -80,11 +81,12 @@ - - + + + - + diff --git a/MelonLoader.ModHandler/MelonLoaderBase.cs b/MelonLoader.ModHandler/MelonLoaderBase.cs new file mode 100644 index 000000000..126dd0ae5 --- /dev/null +++ b/MelonLoader.ModHandler/MelonLoaderBase.cs @@ -0,0 +1,186 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using Harmony; + +namespace MelonLoader +{ + public static class MelonLoaderBase + { + internal static MelonGameAttribute CurrentGameAttribute = null; + internal static bool _IsVRChat = false; + public static bool IsVRChat { get => _IsVRChat; } + internal static bool _IsBoneworks = false; + public static bool IsBoneworks { get => _IsBoneworks; } + + private static void Initialize() + { + Setup(); + MelonConsole.Check(); + AssemblyGenerator.Check(); + if (!AssemblyGenerator.HasGeneratedAssembly) + return; + MelonHandler.LoadAll(true); + if (MelonHandler.HasMelons) + MelonPrefs.Setup(); + MelonHandler.OnPreInitialization(); + } + + private static void Startup() + { + if (!AssemblyGenerator.HasGeneratedAssembly) + return; + SetupSupport(); + WelcomeLog(); + MelonHandler.LoadAll(); + MelonHandler.LogAndPrune(); + if (!MelonHandler.HasMelons) + return; + MelonPrefs.Setup(); + AddUnityDebugLog(); + MelonHandler.OnApplicationStart(); + if (!MelonHandler.HasMelons) + SupportModule.Destroy(); + } + + internal static void Quit() + { + if (MelonHandler.HasMelons) + MelonPrefs.SaveConfig(); + HarmonyInstance.UnpatchAllInstances(); + UNLOAD(); + if (IsQuitFix()) Process.GetCurrentProcess().Kill(); + } + + private static void Setup() + { + FixCurrentBaseDirectory(); + AppDomain.CurrentDomain.UnhandledException += ExceptionHandler; + CurrentGameAttribute = new MelonGameAttribute(Imports.GetCompanyName(), Imports.GetProductName()); + if (Imports.IsIl2CppGame()) + { + _IsVRChat = CurrentGameAttribute.IsGame("VRChat", "VRChat"); + _IsBoneworks = CurrentGameAttribute.IsGame("Stress Level Zero", "BONEWORKS"); + } + } + + private static void SetupSupport() + { + if (Imports.IsIl2CppGame()) + { + if (_IsVRChat) + MelonHandler.Assembly_CSharp = Assembly.Load("Assembly-CSharp"); + UnhollowerSupport.Initialize(); + } + SupportModule.Initialize(); + } + + private static void WelcomeLog() + { + MelonLogger.Log("------------------------------"); + MelonLogger.Log("Unity " + _UnityVersion); + MelonLogger.Log("OS: " + Environment.OSVersion.ToString()); + MelonLogger.Log("------------------------------"); + MelonLogger.Log("Name: " + CurrentGameAttribute.GameName); + MelonLogger.Log("Developer: " + CurrentGameAttribute.Developer); + MelonLogger.Log("Type: " + (Imports.IsIl2CppGame() ? "Il2Cpp" : (IsOldMono() ? "Mono" : "MonoBleedingEdge"))); + MelonLogger.Log("------------------------------"); + MelonLogger.Log("Using v" + BuildInfo.Version + " Open-Beta"); + MelonLogger.Log("------------------------------"); + } + + private static void AddUnityDebugLog() + { + SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------"); + SupportModule.UnityDebugLog("~ This Game has been MODIFIED using MelonLoader. DO NOT report any issues to the Developers! ~"); + SupportModule.UnityDebugLog("--------------------------------------------------------------------------------------------------"); + } + + public static void FixCurrentBaseDirectory() + { + ((AppDomainSetup)typeof(AppDomain).GetProperty("SetupInformationNoCopy", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(AppDomain.CurrentDomain, new object[0])).ApplicationBase = Imports.GetGameDirectory(); + Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + } + + private static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e) => MelonLogger.LogError((e.ExceptionObject as Exception).ToString()); + + private static string _UserDataPath = null; + public static string UserDataPath + { + get + { + if (_UserDataPath == null) + { + _UserDataPath = Path.Combine(Imports.GetGameDirectory(), "UserData"); + if (!Directory.Exists(_UserDataPath)) + Directory.CreateDirectory(_UserDataPath); + } + return _UserDataPath; + } + } + + private static string _UnityVersion = null; + public static string UnityVersion + { + get + { + if (_UnityVersion != null) + return _UnityVersion; + string exepath = Imports.GetExePath(); + string ggm_path = Path.Combine(Imports.GetGameDataDirectory(), "globalgamemanagers"); + if (!File.Exists(ggm_path)) + { + FileVersionInfo versioninfo = FileVersionInfo.GetVersionInfo(exepath); + if ((versioninfo == null) || string.IsNullOrEmpty(versioninfo.FileVersion)) + return "UNKNOWN"; + return versioninfo.FileVersion.Substring(0, versioninfo.FileVersion.LastIndexOf('.')); + } + byte[] ggm_bytes = File.ReadAllBytes(ggm_path); + if ((ggm_bytes == null) || (ggm_bytes.Length <= 0)) + return "UNKNOWN"; + int start_position = 0; + for (int i = 10; i < ggm_bytes.Length; i++) + { + byte pos_byte = ggm_bytes[i]; + if ((pos_byte <= 0x39) && (pos_byte >= 0x30)) + { + start_position = i; + break; + } + } + if (start_position == 0) + return "UNKNOWN"; + int end_position = 0; + for (int i = start_position; i < ggm_bytes.Length; i++) + { + byte pos_byte = ggm_bytes[i]; + if ((pos_byte != 0x2E) && ((pos_byte > 0x39) || (pos_byte < 0x30))) + { + end_position = (i - 1); + break; + } + } + if (end_position == 0) + return "UNKNOWN"; + int verstr_byte_pos = 0; + byte[] verstr_byte = new byte[((end_position - start_position) + 1)]; + for (int i = start_position; i <= end_position; i++) + { + verstr_byte[verstr_byte_pos] = ggm_bytes[i]; + verstr_byte_pos++; + } + return _UnityVersion = Encoding.UTF8.GetString(verstr_byte, 0, verstr_byte.Length); + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool IsOldMono(); + [MethodImpl(MethodImplOptions.InternalCall)] + private extern static bool IsQuitFix(); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void UNLOAD(bool doquitfix = true); + } +} \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonLogger.cs b/MelonLoader.ModHandler/MelonLogger.cs new file mode 100644 index 000000000..3227e2855 --- /dev/null +++ b/MelonLoader.ModHandler/MelonLogger.cs @@ -0,0 +1,131 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace MelonLoader +{ + public class MelonLogger + { + public static void Log(string s) + { + string namesection = GetNameSection(); + Native_Log(namesection, s); + MelonConsole.RunLogCallbacks(namesection, s); + } + + public static void Log(ConsoleColor color, string s) + { + string namesection = GetNameSection(); + Native_LogColor(namesection, s, color); + MelonConsole.RunLogCallbacks(namesection, s); + } + + public static void Log(string s, params object[] args) + { + string namesection = GetNameSection(); + string fmt = string.Format(s, args); + Native_Log(namesection, fmt); + MelonConsole.RunLogCallbacks(namesection, fmt); + } + + public static void Log(ConsoleColor color, string s, params object[] args) + { + string namesection = GetNameSection(); + string fmt = string.Format(s, args); + Native_LogColor(namesection, fmt, color); + MelonConsole.RunLogCallbacks(namesection, fmt); + } + + public static void LogWarning(string s) + { + string namesection = GetNameSection(); + Native_LogWarning(namesection, s); + MelonConsole.RunWarningCallbacks(namesection, s); + } + + public static void LogWarning(string s, params object[] args) + { + string namesection = GetNameSection(); + string fmt = string.Format(s, args); + Native_LogWarning(namesection, fmt); + MelonConsole.RunWarningCallbacks(namesection, fmt); + Native_LogWarning(GetNameSection(), fmt); + } + + public static void LogError(string s) + { + string namesection = GetNameSection(); + Native_LogError(namesection, s); + MelonConsole.RunErrorCallbacks(namesection, s); + } + public static void LogError(string s, params object[] args) + { + string namesection = GetNameSection(); + string fmt = string.Format(s, args); + Native_LogError(namesection, fmt); + MelonConsole.RunErrorCallbacks(namesection, fmt); + } + + internal static void LogMelonError(string msg, string modname) + { + string namesection = (string.IsNullOrEmpty(modname) ? "" : ("[" + modname.Replace(" ", "_") + "] ")); + Native_LogMelonError(namesection, msg); + MelonConsole.RunErrorCallbacks(namesection, msg); + } + + internal static void LogMelonCompatibility(MelonBase.MelonCompatibility comp) => Native_LogMelonCompatibility(comp); + + internal static string GetNameSection() + { + StackTrace st = new StackTrace(2, true); + StackFrame sf = st.GetFrame(0); + if (sf != null) + { + MethodBase method = sf.GetMethod(); + if (!method.Equals(null)) + { + Type methodClassType = method.DeclaringType; + if (!methodClassType.Equals(null)) + { + Assembly asm = methodClassType.Assembly; + if (!asm.Equals(null)) + { + MelonPlugin plugin = MelonHandler.Plugins.Find(x => (x.Assembly == asm)); + if (plugin != null) + { + if (!string.IsNullOrEmpty(plugin.Info.Name)) + return "[" + plugin.Info.Name.Replace(" ", "_") + "] "; + } + else + { + MelonMod mod = MelonHandler.Mods.Find(x => (x.Assembly == asm)); + if (mod != null) + { + if (!string.IsNullOrEmpty(mod.Info.Name)) + return "[" + mod.Info.Name.Replace(" ", "_") + "] "; + } + } + } + } + } + } + return ""; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_Log(string namesection, string txt); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_LogColor(string namesection, string txt, ConsoleColor color); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_LogWarning(string namesection, string txt); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_LogError(string namesection, string txt); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_LogMelonError(string namesection, string txt); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_LogMelonCompatibility(MelonBase.MelonCompatibility comp); + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void Native_ThrowInternalError(string txt); + } +} \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonMod.cs b/MelonLoader.ModHandler/MelonMod.cs index 390fd65c0..a90005d64 100644 --- a/MelonLoader.ModHandler/MelonMod.cs +++ b/MelonLoader.ModHandler/MelonMod.cs @@ -1,98 +1,18 @@ using System; +#pragma warning disable 0108 namespace MelonLoader { public abstract class MelonMod : MelonBase { - /// - /// Gets the Info Attribute of the Mod or Plugin. - /// - public MelonModInfoAttribute InfoAttribute { get; internal set; } - - /// - /// Gets the Game Attributes of the Mod or Plugin. - /// - public MelonModGameAttribute[] GameAttributes { get; internal set; } + [Obsolete()] + public MelonModInfoAttribute InfoAttribute { get => LegacyModInfo; } + [Obsolete()] + public MelonModGameAttribute[] GameAttributes { get => LegacyModGames; } public virtual void OnLevelIsLoading() {} public virtual void OnLevelWasLoaded(int level) {} public virtual void OnLevelWasInitialized(int level) {} - public virtual void OnUpdate() {} public virtual void OnFixedUpdate() {} - public virtual void OnLateUpdate() {} - public virtual void OnGUI() {} - public virtual void VRChat_OnUiManagerInit() {} - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - public class MelonModGameAttribute : Attribute - { - /// - /// Gets the target Developer - /// - public string Developer { get; } - - /// - /// Gets target Game Name - /// - public string GameName { get; } - - /// - /// Gets whether this Mod can target any Game. - /// - public bool Universal { get => string.IsNullOrEmpty(Developer) || string.IsNullOrEmpty(GameName); } - - /// - /// Mark this Mod as Universal or Compatible with specific Games. - /// - public MelonModGameAttribute(string developer = null, string gameName = null) - { - Developer = developer; - GameName = gameName; - } - - public bool IsGame(string developer, string gameName) => (Universal || ((developer != null) && (gameName != null) && Developer.Equals(developer) && GameName.Equals(gameName))); - public bool IsCompatible(MelonModGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName))); - public bool IsCompatibleBecauseUniversal(MelonModGameAttribute att) => ((att == null) || Universal || att.Universal); - public bool IsCompatible(MelonPluginGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName))); - public bool IsCompatibleBecauseUniversal(MelonPluginGameAttribute att) => ((att == null) || Universal || att.Universal); - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] - public class MelonModInfoAttribute : Attribute - { - /// - /// Gets the System.Type of the Mod. - /// - public Type SystemType { get; } - - /// - /// Gets the Name of the Mod. - /// - public string Name { get; } - - /// - /// Gets the Version of the Mod. - /// - public string Version { get; } - - /// - /// Gets the Author of the Mod. - /// - public string Author { get; } - - /// - /// Gets the Download Link of the Mod. - /// - public string DownloadLink { get; } - - public MelonModInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) - { - SystemType = type; - Name = name; - Version = version; - Author = author; - DownloadLink = downloadLink; - } } } \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonModLogger.cs b/MelonLoader.ModHandler/MelonModLogger.cs deleted file mode 100644 index 4e9529243..000000000 --- a/MelonLoader.ModHandler/MelonModLogger.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Diagnostics; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace MelonLoader -{ - public class MelonModLogger - { - internal static bool consoleEnabled = false; - - private static string GetNameSection() - { - StackTrace st = new StackTrace(2, true); - StackFrame sf = st.GetFrame(0); - if (sf != null) - { - MethodBase method = sf.GetMethod(); - if (!method.Equals(null)) - { - Type methodClassType = method.DeclaringType; - if (!methodClassType.Equals(null)) - { - Assembly asm = methodClassType.Assembly; - if (!asm.Equals(null)) - { - object[] attrArray = asm.GetCustomAttributes(typeof(MelonPluginInfoAttribute), false); - if ((attrArray.Length > 0) && (attrArray[0] != null)) - { - MelonPluginInfoAttribute attr = attrArray[0] as MelonPluginInfoAttribute; - if (!string.IsNullOrEmpty(attr.Name)) - return "[" + attr.Name.Replace(" ", "_") + "] "; - } - attrArray = asm.GetCustomAttributes(typeof(MelonModInfoAttribute), false); - if ((attrArray.Length > 0) && (attrArray[0] != null)) - { - MelonModInfoAttribute attr = attrArray[0] as MelonModInfoAttribute; - if (!string.IsNullOrEmpty(attr.Name)) - return "[" + attr.Name.Replace(" ", "_") + "] "; - } - } - } - } - } - return ""; - } - - public static void Log(string s) => Native_Log((GetNameSection() + s)); - public static void Log(ConsoleColor color, string s) => Native_LogColor((GetNameSection() + s), color); - public static void Log(string s, params object[] args) => Native_Log((GetNameSection() + string.Format(s, args))); - public static void Log(ConsoleColor color, string s, params object[] args) => Native_LogColor((GetNameSection() + string.Format(s, args)), color); - - public static void LogWarning(string s) => Native_LogWarning(GetNameSection(), s); - public static void LogWarning(string s, params object[] args) => Native_LogWarning(GetNameSection(), string.Format(s, args)); - - public static void LogError(string s) => Native_LogError(GetNameSection(), s); - public static void LogError(string s, params object[] args) => Native_LogError(GetNameSection(), string.Format(s, args)); - internal static void LogDLLError(string msg, string modname) => Native_LogDLLError((string.IsNullOrEmpty(modname) ? "" : ("[" + modname.Replace(" ", "_") + "] ")), msg); - - internal static void LogDLLStatus(MelonBase.MelonCompatibility type) => Native_LogDLLStatus(type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void Native_Log(string txt); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void Native_LogColor(string txt, ConsoleColor color); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void Native_LogWarning(string namesection, string txt); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void Native_LogError(string namesection, string txt); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void Native_LogDLLError(string namesection, string msg); - [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void Native_LogDLLStatus(MelonBase.MelonCompatibility type); - } -} \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonPlugin.cs b/MelonLoader.ModHandler/MelonPlugin.cs index af25c772f..17ff1d041 100644 --- a/MelonLoader.ModHandler/MelonPlugin.cs +++ b/MelonLoader.ModHandler/MelonPlugin.cs @@ -1,91 +1,15 @@ using System; +#pragma warning disable 0108 namespace MelonLoader { public abstract class MelonPlugin : MelonBase { - /// - /// Gets the Info Attribute of the Mod or Plugin. - /// - public MelonPluginInfoAttribute InfoAttribute { get; internal set; } - - /// - /// Gets the Game Attributes of the Mod or Plugin. - /// - public MelonPluginGameAttribute[] GameAttributes { get; internal set; } + [Obsolete()] + public MelonPluginInfoAttribute InfoAttribute { get => LegacyPluginInfo; } + [Obsolete()] + public MelonPluginGameAttribute[] GameAttributes { get => LegacyPluginGames; } public virtual void OnPreInitialization() { } } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - public class MelonPluginGameAttribute : Attribute - { - /// - /// Gets the target Developer - /// - public string Developer { get; } - - /// - /// Gets target Game Name - /// - public string GameName { get; } - - /// - /// Gets whether this Mod can target any Game. - /// - public bool Universal { get => string.IsNullOrEmpty(Developer) || string.IsNullOrEmpty(GameName); } - - /// - /// Mark this Mod as Universal or Compatible with specific Games. - /// - public MelonPluginGameAttribute(string developer = null, string gameName = null) - { - Developer = developer; - GameName = gameName; - } - - public bool IsGame(string developer, string gameName) => (Universal || ((developer != null) && (gameName != null) && Developer.Equals(developer) && GameName.Equals(gameName))); - public bool IsCompatible(MelonPluginGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName))); - public bool IsCompatibleBecauseUniversal(MelonPluginGameAttribute att) => ((att == null) || Universal || att.Universal); - public bool IsCompatible(MelonModGameAttribute att) => ((att == null) || IsCompatibleBecauseUniversal(att) || (att.Developer.Equals(Developer) && att.GameName.Equals(GameName))); - public bool IsCompatibleBecauseUniversal(MelonModGameAttribute att) => ((att == null) || Universal || att.Universal); - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] - public class MelonPluginInfoAttribute : Attribute - { - /// - /// Gets the System.Type of the Mod. - /// - public Type SystemType { get; } - - /// - /// Gets the Name of the Mod. - /// - public string Name { get; } - - /// - /// Gets the Version of the Mod. - /// - public string Version { get; } - - /// - /// Gets the Author of the Mod. - /// - public string Author { get; } - - /// - /// Gets the Download Link of the Mod. - /// - public string DownloadLink { get; } - - public MelonPluginInfoAttribute(Type type, string name, string version, string author, string downloadLink = null) - { - SystemType = type; - Name = name; - Version = version; - Author = author; - DownloadLink = downloadLink; - } - } -} +} \ No newline at end of file diff --git a/MelonLoader.ModHandler/MelonPrefs.cs b/MelonLoader.ModHandler/MelonPrefs.cs new file mode 100644 index 000000000..c86e40c91 --- /dev/null +++ b/MelonLoader.ModHandler/MelonPrefs.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace MelonLoader +{ + public class MelonPrefs + { + private static string ConfigFileName = "modprefs.ini"; + private static IniFile ConfigFile = null; + private static Dictionary> prefs = new Dictionary>(); + private static Dictionary categoryDisplayNames = new Dictionary(); + + internal static void Setup() { if (ConfigFile == null) ConfigFile = new IniFile(Path.Combine(MelonLoaderBase.UserDataPath, ConfigFileName)); } + + public static void RegisterCategory(string name, string displayText) { categoryDisplayNames[name] = displayText; } + public static void RegisterString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, defaultValue, displayText, MelonPreferenceType.STRING, hideFromList); } + public static void RegisterBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, defaultValue ? "true" : "false", displayText, MelonPreferenceType.BOOL, hideFromList); } + public static void RegisterInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, "" + defaultValue, displayText, MelonPreferenceType.INT, hideFromList); } + public static void RegisterFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) { Register(section, name, "" + defaultValue, displayText, MelonPreferenceType.FLOAT, hideFromList); } + private static void Register(string section, string name, string defaultValue, string displayText, MelonPreferenceType type, bool hideFromList) + { + if (prefs.TryGetValue(section, out Dictionary prefsInSection)) + { + if (prefsInSection.TryGetValue(name, out MelonPreference pref)) + MelonLogger.LogError("Trying to registered Pref " + section + ":" + name + " more than one time"); + else + { + string toStoreValue = defaultValue; + if (ConfigFile.HasKey(section, name)) + toStoreValue = ConfigFile.GetString(section, name, defaultValue); + else ConfigFile.SetString(section, name, defaultValue); + prefsInSection.Add(name, new MelonPreference(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText)); + } + } + else + { + Dictionary dic = new Dictionary(); + string toStoreValue = defaultValue; + if (ConfigFile.HasKey(section, name)) + toStoreValue = ConfigFile.GetString(section, name, defaultValue); + else ConfigFile.SetString(section, name, defaultValue); + dic.Add(name, new MelonPreference(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText)); + prefs.Add(section, dic); + } + } + + public static bool HasKey(string section, string name) { return prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.ContainsKey(name); } + public static Dictionary> GetPreferences() { return prefs; } + public static string GetCategoryDisplayName(string key) { if (categoryDisplayNames.TryGetValue(key, out string name)) return name; return key; } + + public static void SaveConfig() + { + foreach (KeyValuePair> prefsInSection in prefs) + { + foreach (KeyValuePair pref in prefsInSection.Value) + { + pref.Value.Value = pref.Value.ValueEdited; + ConfigFile.SetString(prefsInSection.Key, pref.Key, pref.Value.Value); + } + } + MelonHandler.OnModSettingsApplied(); + MelonLogger.Log("Config Saved!"); + } + + public static string GetString(string section, string name) + { + if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref)) + return pref.Value; + MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); + return ""; + } + + public static void SetString(string section, string name, string value) + { + if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref)) + { + pref.Value = pref.ValueEdited = value; + ConfigFile.SetString(section, name, value); + } + else + MelonLogger.LogError("Trying to save unknown pref " + section + ":" + name); + } + + public static bool GetBool(string section, string name) + { + if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref)) + return (pref.Value.Equals("true") || pref.Value.Equals("1")); + MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); + return false; + } + public static void SetBool(string section, string name, bool value) { SetString(section, name, value ? "true" : "false"); } + + public static int GetInt(string section, string name) + { + if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref)) + if (int.TryParse(pref.Value, out int valueI)) + return valueI; + MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); + return 0; + } + public static void SetInt(string section, string name, int value) { SetString(section, name, value.ToString()); } + + public static float GetFloat(string section, string name) + { + if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out MelonPreference pref)) + if (float.TryParse(pref.Value, out float valueF)) + return valueF; + MelonLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); + return 0.0f; + } + public static void SetFloat(string section, string name, float value) { SetString(section, name, value.ToString()); } + + public enum MelonPreferenceType + { + STRING, + BOOL, + INT, + FLOAT + } + + public class MelonPreference + { + public string Value { get; set; } + public string ValueEdited { get; set; } + public MelonPreferenceType Type { get; internal set; } + public bool Hidden { get; internal set; } + public String DisplayText { get; internal set; } + + public MelonPreference(string value, MelonPreferenceType type, bool hidden, string displayText) + { + Value = value; + ValueEdited = value; + Type = type; + Hidden = hidden; + DisplayText = displayText; + } + + [Obsolete()] + public MelonPreference(string value, ModPrefs.PrefType type, bool hidden, string displayText) + { + Value = value; + ValueEdited = value; + Type = (MelonPreferenceType)type; + Hidden = hidden; + DisplayText = displayText; + } + } + } +} \ No newline at end of file diff --git a/MelonLoader.ModHandler/ModPrefs.cs b/MelonLoader.ModHandler/ModPrefs.cs deleted file mode 100644 index 4ac528519..000000000 --- a/MelonLoader.ModHandler/ModPrefs.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -#pragma warning disable CS0618 - -namespace MelonLoader -{ - internal static class ModPrefsController - { - private static string filename = "modprefs.ini"; - private static IniFile _instance; - private static IniFile Instance - { - get - { - if (_instance == null) - _instance = new IniFile(Path.Combine(Main.GetUserDataPath(), filename)); - return _instance; - } - } - - internal static string GetString(string section, string name, string defaultValue = "", bool autoSave = false) - { - string value = Instance.IniReadValue(section, name); - if (!string.IsNullOrEmpty(value)) - return value; - else if (autoSave) - SetString(section, name, defaultValue); - return defaultValue; - } - internal static void SetString(string section, string name, string value) { Instance.IniWriteValue(section, name, value.Trim()); } - - internal static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false) - { - int value; - if (int.TryParse(Instance.IniReadValue(section, name), out value)) - return value; - else if (autoSave) - SetInt(section, name, defaultValue); - return defaultValue; - } - internal static void SetInt(string section, string name, int value) { Instance.IniWriteValue(section, name, value.ToString()); } - - internal static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false) - { - float value; - if (float.TryParse(Instance.IniReadValue(section, name), out value)) - return value; - else if (autoSave) - SetFloat(section, name, defaultValue); - return defaultValue; - } - internal static void SetFloat(string section, string name, float value) { Instance.IniWriteValue(section, name, value.ToString()); } - - internal static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false) - { - string sVal = GetString(section, name, null); - if ("true".Equals(sVal) || "1".Equals(sVal) || "0".Equals(sVal) || "false".Equals(sVal)) - return ("true".Equals(sVal) || "1".Equals(sVal)); - else if (autoSave) - SetBool(section, name, defaultValue); - return defaultValue; - } - internal static void SetBool(string section, string name, bool value) { Instance.IniWriteValue(section, name, value ? "true" : "false"); } - - internal static bool HasKey(string section, string name) { return Instance.IniReadValue(section, name) != null; } - } - - public static class ModPrefs - { - private static Dictionary> prefs = new Dictionary>(); - private static Dictionary categoryDisplayNames = new Dictionary(); - public static void RegisterCategory(string name, string displayText) { categoryDisplayNames[name] = displayText; } - public static void RegisterPrefString(string section, string name, string defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, defaultValue, displayText, PrefType.STRING, hideFromList); } - public static void RegisterPrefBool(string section, string name, bool defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, defaultValue ? "true" : "false", displayText, PrefType.BOOL, hideFromList); } - public static void RegisterPrefInt(string section, string name, int defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, "" + defaultValue, displayText, PrefType.INT, hideFromList); } - public static void RegisterPrefFloat(string section, string name, float defaultValue, string displayText = null, bool hideFromList = false) { RegisterPref(section, name, "" + defaultValue, displayText, PrefType.FLOAT, hideFromList); } - - private static void RegisterPref(string section, string name, string defaultValue, string displayText, PrefType type, bool hideFromList) - { - if (prefs.TryGetValue(section, out Dictionary prefsInSection)) - { - if (prefsInSection.TryGetValue(name, out PrefDesc pref)) - MelonModLogger.LogError("Trying to registered Pref " + section + ":" + name + " more than one time"); - else - { - string toStoreValue = defaultValue; - if (ModPrefsController.HasKey(section, name)) - toStoreValue = ModPrefsController.GetString(section, name, defaultValue); - else ModPrefsController.SetString(section, name, defaultValue); - prefsInSection.Add(name, new PrefDesc(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText)); - } - } - else - { - Dictionary dic = new Dictionary(); - string toStoreValue = defaultValue; - if (ModPrefsController.HasKey(section, name)) - toStoreValue = ModPrefsController.GetString(section, name, defaultValue); - else ModPrefsController.SetString(section, name, defaultValue); - dic.Add(name, new PrefDesc(toStoreValue, type, hideFromList, (displayText ?? "") == "" ? name : displayText)); - prefs.Add(section, dic); - } - } - - public static bool HasKey(string section, string name) { return prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.ContainsKey(name); } - public static Dictionary> GetPrefs() { return prefs; } - public static string GetCategoryDisplayName(string key) { if (categoryDisplayNames.TryGetValue(key, out string name)) return name; return key; } - - public static void SaveConfig() - { - foreach (KeyValuePair> prefsInSection in prefs) - { - foreach (KeyValuePair pref in prefsInSection.Value) - { - pref.Value.Value = pref.Value.ValueEdited; - ModPrefsController.SetString(prefsInSection.Key, pref.Key, pref.Value.Value); - } - } - Main.OnModSettingsApplied(); - MelonModLogger.Log("Config Saved!"); - } - - public static string GetString(string section, string name) - { - if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref)) - return pref.Value; - MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); - return ""; - } - - public static void SetString(string section, string name, string value) - { - if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref)) - { - pref.Value = pref.ValueEdited = value; - ModPrefsController.SetString(section, name, value); - } - else - MelonModLogger.LogError("Trying to save unknown pref " + section + ":" + name); - } - - public static bool GetBool(string section, string name) - { - if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref)) - return (pref.Value.Equals("true") || pref.Value.Equals("1")); - MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); - return false; - } - public static void SetBool(string section, string name, bool value) { SetString(section, name, value ? "true" : "false"); } - - public static int GetInt(string section, string name) - { - if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref)) - if (int.TryParse(pref.Value, out int valueI)) - return valueI; - MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); - return 0; - } - public static void SetInt(string section, string name, int value) { SetString(section, name, value.ToString()); } - - public static float GetFloat(string section, string name) - { - if (prefs.TryGetValue(section, out Dictionary prefsInSection) && prefsInSection.TryGetValue(name, out PrefDesc pref)) - if (float.TryParse(pref.Value, out float valueF)) - return valueF; - MelonModLogger.LogError("Trying to get unregistered Pref " + section + ":" + name); - return 0.0f; - } - public static void SetFloat(string section, string name, float value) { SetString(section, name, value.ToString()); } - - public enum PrefType - { - STRING, - BOOL, - INT, - FLOAT - } - - public class PrefDesc - { - public string Value { get; set; } - public string ValueEdited { get; set; } - public PrefType Type { get; private set; } - public bool Hidden { get; private set; } - public String DisplayText { get; private set; } - - public PrefDesc(string value, PrefType type, bool hidden, string displayText) - { - Value = value; - ValueEdited = value; - Type = type; - Hidden = hidden; - DisplayText = displayText; - } - } - } -} \ No newline at end of file diff --git a/MelonLoader.ModHandler/SceneHandler.cs b/MelonLoader.ModHandler/SceneHandler.cs index 4702a8742..114e50936 100644 --- a/MelonLoader.ModHandler/SceneHandler.cs +++ b/MelonLoader.ModHandler/SceneHandler.cs @@ -14,7 +14,7 @@ public static void OnSceneLoad(int current_scene) bool should_run = true; if (Imports.IsIl2CppGame()) { - if (Main.IsBoneworks) + if (MelonLoaderBase._IsBoneworks) { if (!Boneworks_HasGotLoadingSceneIndex) { @@ -55,8 +55,8 @@ public static void OnSceneLoad(int current_scene) LastSceneIndex = current_scene; if (!ShouldWait) IsLoading = true; - if (Main.IsBoneworks) - Main.OnLevelIsLoading(); + if (MelonLoaderBase._IsBoneworks) + MelonHandler.OnLevelIsLoading(); else CheckForSceneFinishedLoading(); } @@ -72,7 +72,7 @@ private static void CheckForSceneFinishedLoading() { if (IsLoading) { - Main.OnLevelWasLoaded(LastSceneIndex); + MelonHandler.OnLevelWasLoaded(LastSceneIndex); HasFinishedLoading = true; IsLoading = false; } @@ -82,7 +82,7 @@ private static void CheckForSceneInitialized() { if (HasFinishedLoading) { - Main.OnLevelWasInitialized(LastSceneIndex); + MelonHandler.OnLevelWasInitialized(LastSceneIndex); HasFinishedLoading = false; } } diff --git a/MelonLoader.ModHandler/SupportModule.cs b/MelonLoader.ModHandler/SupportModule.cs index c26512472..90012a91a 100644 --- a/MelonLoader.ModHandler/SupportModule.cs +++ b/MelonLoader.ModHandler/SupportModule.cs @@ -61,14 +61,14 @@ internal static void Initialize() } else { - MelonModLogger.LogError("Unable to load Support Module! Support Module is Missing!"); - MelonModLogger.Log("------------------------------"); + MelonLogger.LogError("Unable to load Support Module! Support Module is Missing!"); + MelonLogger.Log("------------------------------"); } } catch (Exception e) { - MelonModLogger.LogError("Unable to load Support Module!\n" + e.ToString()); - MelonModLogger.Log("------------------------------"); + MelonLogger.LogError("Unable to load Support Module!\n" + e.ToString()); + MelonLogger.Log("------------------------------"); } } diff --git a/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs b/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs index ea4ecd662..760cab23b 100644 --- a/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs +++ b/MelonLoader.Support.Il2Cpp/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] [assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Il2Cpp")] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("8eaaed02-b3c7-4380-9474-3dbe16394590")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file diff --git a/MelonLoader.Support.Il2Cpp/Main.cs b/MelonLoader.Support.Il2Cpp/Main.cs index 29be0eb40..00523de24 100644 --- a/MelonLoader.Support.Il2Cpp/Main.cs +++ b/MelonLoader.Support.Il2Cpp/Main.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; using UnhollowerBaseLib; using UnhollowerBaseLib.Runtime; using UnhollowerRuntimeLib; @@ -24,42 +23,14 @@ internal static class Main private static ISupportModule Initialize() { LogSupport.RemoveAllHandlers(); - if (Console.Enabled || Imports.IsDebugMode()) - LogSupport.InfoHandler += MelonModLogger.Log; - LogSupport.WarningHandler += MelonModLogger.LogWarning; - LogSupport.ErrorHandler += MelonModLogger.LogError; + if (MelonConsole.Enabled || Imports.IsDebugMode()) + LogSupport.InfoHandler += MelonLogger.Log; + LogSupport.WarningHandler += MelonLogger.LogWarning; + LogSupport.ErrorHandler += MelonLogger.LogError; if (Imports.IsDebugMode()) - LogSupport.TraceHandler += MelonModLogger.Log; + LogSupport.TraceHandler += MelonLogger.Log; - try - { - Assembly il2cppSystem = Assembly.Load("Il2CppSystem"); - if (il2cppSystem != null) - { - Type unitytls = il2cppSystem.GetType("Il2CppMono.Unity.UnityTls"); - if (unitytls != null) - { - unsafe - { - var tlsHookTarget = typeof(Uri).Assembly.GetType("Mono.Unity.UnityTls").GetMethod("GetUnityTlsInterface", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).MethodHandle.GetFunctionPointer(); - var unityMethodField = UnhollowerUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(unitytls.GetMethod("GetUnityTlsInterface", BindingFlags.Public | BindingFlags.Static)); - var unityMethodPtr = (IntPtr)unityMethodField.GetValue(null); - var unityMethod = *(IntPtr*)unityMethodPtr; - Imports.Hook((IntPtr)(&tlsHookTarget), unityMethod); - } - } - else - throw new Exception("Failed to get Type Il2CppMono.Unity.UnityTls!"); - } - else - throw new Exception("Failed to get Assembly Il2CppSystem!"); - } - catch (Exception ex) - { - MelonModLogger.LogWarning("Exception while setting up TLS, mods will not be able to use HTTPS: " + ex); - } - - if (MelonLoader.Main.IsVRChat) + if (MelonLoaderBase.IsVRChat) { try { @@ -82,7 +53,7 @@ private static ISupportModule Initialize() } catch (Exception ex) { - MelonModLogger.LogWarning("Exception while setting up Auth Token Hider, Auth Tokens may show in Console: " + ex); + MelonLogger.LogWarning("Exception while setting up Auth Token Hider, Auth Tokens may show in Console: " + ex); } } @@ -111,7 +82,7 @@ private static ISupportModule Initialize() private static void GetUnityVersionNumbers(out int major, out int minor, out int patch) { - var unityVersionSplit = MelonLoader.Main.UnityVersion.Split('.'); + var unityVersionSplit = MelonLoaderBase.UnityVersion.Split('.'); major = int.Parse(unityVersionSplit[0]); minor = int.Parse(unityVersionSplit[1]); var patchString = unityVersionSplit[2]; @@ -126,7 +97,7 @@ private static void GetUnityVersionNumbers(out int major, out int minor, out int internal static SetAsLastSiblingDelegate SetAsLastSiblingDelegateField; private static bool Il2CppSystem_Console_WriteLine_Patch() => false; - private static void Transmtn_HttpConnection_get_Prefix() => harmonyInstance.Patch(Il2CppSystem_Console_WriteLine, new Harmony.HarmonyMethod(typeof(Main).GetMethod("Il2CppSystem_Console_WriteLine_Patch", BindingFlags.NonPublic | BindingFlags.Static))); + private static void Transmtn_HttpConnection_get_Prefix() => harmonyInstance.Patch(Il2CppSystem_Console_WriteLine, new HarmonyMethod(typeof(Main).GetMethod("Il2CppSystem_Console_WriteLine_Patch", BindingFlags.NonPublic | BindingFlags.Static))); private static void Transmtn_HttpConnection_get_Postfix() => harmonyInstance.Unpatch(Il2CppSystem_Console_WriteLine, typeof(Main).GetMethod("Il2CppSystem_Console_WriteLine_Patch", BindingFlags.NonPublic | BindingFlags.Static)); } @@ -134,9 +105,9 @@ public class MelonLoaderComponent : MonoBehaviour { internal static void Create() { - Main.obj = new GameObject("MelonLoader"); + Main.obj = new GameObject(); DontDestroyOnLoad(Main.obj); - Main.comp = Main.obj.AddComponent(); + Main.comp = new MelonLoaderComponent(Main.obj.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of()).Pointer); Main.SetAsLastSiblingDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(Main.obj.transform)); Main.SetAsLastSiblingDelegateField(IL2CPP.Il2CppObjectBaseToPtrNotNull(Main.comp.transform)); } @@ -146,17 +117,17 @@ public MelonLoaderComponent(IntPtr intPtr) : base(intPtr) { } void Update() { transform.SetAsLastSibling(); - MelonLoader.Main.OnUpdate(); + MelonHandler.OnUpdate(); MelonCoroutines.Process(); } void FixedUpdate() { - MelonLoader.Main.OnFixedUpdate(); + MelonHandler.OnFixedUpdate(); MelonCoroutines.ProcessWaitForFixedUpdate(); } - void LateUpdate() => MelonLoader.Main.OnLateUpdate(); - void OnGUI() => MelonLoader.Main.OnGUI(); + void LateUpdate() => MelonHandler.OnLateUpdate(); + void OnGUI() => MelonHandler.OnGUI(); void OnDestroy() { if (!Main.IsDestroying) Create(); } - void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); } + void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); } } } \ No newline at end of file diff --git a/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs b/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs index daa11bc5d..63024df8b 100644 --- a/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs +++ b/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs @@ -97,7 +97,7 @@ private static void ProcessNextOfCoroutine(IEnumerator enumerator) } catch (Exception e) { - MelonModLogger.LogError(e.ToString()); + MelonLogger.LogError(e.ToString()); Stop(FindOriginalCoro(enumerator)); // We want the entire coroutine hierachy to stop when an error happen } @@ -120,7 +120,7 @@ private static void ProcessNextOfCoroutine(IEnumerator enumerator) if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction next = new Il2CppEnumeratorWrapper(nextAsEnumerator); else - MelonModLogger.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}"); + MelonLogger.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}"); break; } diff --git a/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs b/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs index 8ed328b1d..7b6de44ad 100644 --- a/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs +++ b/MelonLoader.Support.Mono.Pre2017.2/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] [assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Mono.Pre2017")] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("19af2b6e-7217-4396-bb56-a5cffb37633d")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file diff --git a/MelonLoader.Support.Mono.Pre2017.2/Main.cs b/MelonLoader.Support.Mono.Pre2017.2/Main.cs index ba918a2f6..697764c7f 100644 --- a/MelonLoader.Support.Mono.Pre2017.2/Main.cs +++ b/MelonLoader.Support.Mono.Pre2017.2/Main.cs @@ -1,8 +1,6 @@ -using System; -using System.Linq; -using System.Reflection; +using System.Collections; +using System.Collections.Generic; using UnityEngine; -using Harmony; namespace MelonLoader.Support { @@ -12,24 +10,30 @@ internal static class Main internal static GameObject obj = null; internal static MelonLoaderComponent comp = null; internal static int CurrentScene = -9; - private static ISupportModule Initialize() { MelonLoaderComponent.Create(); return new Module(); } } + public class MelonLoaderComponent : MonoBehaviour { + internal static readonly List QueuedCoroutines = new List(); internal static void Create() { Main.obj = new GameObject("MelonLoader"); DontDestroyOnLoad(Main.obj); - Main.comp = Main.obj.AddComponent(); + Main.comp = (MelonLoaderComponent)Main.obj.AddComponent(typeof(MelonLoaderComponent)); Main.obj.transform.SetAsLastSibling(); Main.comp.transform.SetAsLastSibling(); } internal static void Destroy() { Main.IsDestroying = true; if (Main.obj != null) GameObject.Destroy(Main.obj); } + void Awake() + { + foreach (var queuedCoroutine in QueuedCoroutines) StartCoroutine(queuedCoroutine); + QueuedCoroutines.Clear(); + } void Start() => transform.SetAsLastSibling(); void Update() { @@ -39,12 +43,12 @@ void Update() SceneHandler.OnSceneLoad(Application.loadedLevel); Main.CurrentScene = Application.loadedLevel; } - MelonLoader.Main.OnUpdate(); + MelonHandler.OnUpdate(); } - void FixedUpdate() => MelonLoader.Main.OnFixedUpdate(); - void LateUpdate() => MelonLoader.Main.OnLateUpdate(); - void OnGUI() => MelonLoader.Main.OnGUI(); + void FixedUpdate() => MelonHandler.OnFixedUpdate(); + void LateUpdate() => MelonHandler.OnLateUpdate(); + void OnGUI() => MelonHandler.OnGUI(); void OnDestroy() { if (!Main.IsDestroying) Create(); } - void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); } + void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); } } } \ No newline at end of file diff --git a/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs b/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs index 264a30caa..a12c35d0f 100644 --- a/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs +++ b/MelonLoader.Support.Mono.Pre2017.2/SupportModule.cs @@ -6,8 +6,21 @@ namespace MelonLoader.Support public class Module : ISupportModule { public int GetActiveSceneIndex() => Main.CurrentScene; - public object StartCoroutine(IEnumerator coroutine) => Main.comp.StartCoroutine(coroutine); - public void StopCoroutine(object coroutineToken) => Main.comp.StopCoroutine((Coroutine) coroutineToken); + public object StartCoroutine(IEnumerator coroutine) + { + if (Main.comp != null) return Main.comp.StartCoroutine(coroutine); + + MelonLoaderComponent.QueuedCoroutines.Add(coroutine); + return coroutine; + } + + public void StopCoroutine(object coroutineToken) + { + if (Main.comp == null) + MelonLoaderComponent.QueuedCoroutines.Remove(coroutineToken as IEnumerator); + else + Main.comp.StopCoroutine(coroutineToken as Coroutine); + } public void UnityDebugLog(string msg) => Debug.Log(msg); public void Destroy() => MelonLoaderComponent.Destroy(); } diff --git a/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs b/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs index 8ed328b1d..2dac5064d 100644 --- a/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs +++ b/MelonLoader.Support.Mono.Pre2017/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] [assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Mono.Pre2017")] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("0c02945f-7ec8-4992-94d3-c21915f79eae")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file diff --git a/MelonLoader.Support.Mono.Pre2017/Main.cs b/MelonLoader.Support.Mono.Pre2017/Main.cs index d9e0afda9..53f900d4c 100644 --- a/MelonLoader.Support.Mono.Pre2017/Main.cs +++ b/MelonLoader.Support.Mono.Pre2017/Main.cs @@ -1,9 +1,7 @@ -using System; -using System.Linq; -using System.Reflection; +using System.Collections; +using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; -using Harmony; namespace MelonLoader.Support { @@ -12,34 +10,41 @@ internal static class Main internal static bool IsDestroying = false; internal static GameObject obj = null; internal static MelonLoaderComponent comp = null; - private static ISupportModule Initialize() { - MelonLoaderComponent.Create(); SceneManager.sceneLoaded += OnSceneLoad; return new Module(); } - - private static void OnSceneLoad(Scene scene, LoadSceneMode mode) { if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex); } - + private static void OnSceneLoad(Scene scene, LoadSceneMode mode) + { + if (obj == null) MelonLoaderComponent.Create(); + if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex); + } } + public class MelonLoaderComponent : MonoBehaviour { + internal static readonly List QueuedCoroutines = new List(); internal static void Create() { - Main.obj = new GameObject("MelonLoader"); + Main.obj = new GameObject(); DontDestroyOnLoad(Main.obj); - Main.comp = Main.obj.AddComponent(); + Main.comp = (MelonLoaderComponent)Main.obj.AddComponent(typeof(MelonLoaderComponent)); Main.obj.transform.SetAsLastSibling(); Main.comp.transform.SetAsLastSibling(); } internal static void Destroy() { Main.IsDestroying = true; if (Main.obj != null) GameObject.Destroy(Main.obj); } + void Awake() + { + foreach (var queuedCoroutine in QueuedCoroutines) StartCoroutine(queuedCoroutine); + QueuedCoroutines.Clear(); + } void Start() => transform.SetAsLastSibling(); - void Update() { transform.SetAsLastSibling(); MelonLoader.Main.OnUpdate(); } - void FixedUpdate() => MelonLoader.Main.OnFixedUpdate(); - void LateUpdate() => MelonLoader.Main.OnLateUpdate(); - void OnGUI() => MelonLoader.Main.OnGUI(); + void Update() { transform.SetAsLastSibling(); MelonHandler.OnUpdate(); } + void FixedUpdate() => MelonHandler.OnFixedUpdate(); + void LateUpdate() => MelonHandler.OnLateUpdate(); + void OnGUI() => MelonHandler.OnGUI(); void OnDestroy() { if (!Main.IsDestroying) Create(); } - void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); } + void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); } } } \ No newline at end of file diff --git a/MelonLoader.Support.Mono.Pre2017/SupportModule.cs b/MelonLoader.Support.Mono.Pre2017/SupportModule.cs index 75dd6be38..d6d752dcc 100644 --- a/MelonLoader.Support.Mono.Pre2017/SupportModule.cs +++ b/MelonLoader.Support.Mono.Pre2017/SupportModule.cs @@ -7,8 +7,22 @@ namespace MelonLoader.Support public class Module : ISupportModule { public int GetActiveSceneIndex() => SceneManager.GetActiveScene().buildIndex; - public object StartCoroutine(IEnumerator coroutine) => Main.comp.StartCoroutine(coroutine); - public void StopCoroutine(object coroutineToken) => Main.comp.StopCoroutine((Coroutine) coroutineToken); + public object StartCoroutine(IEnumerator coroutine) + { + if (Main.comp != null) return Main.comp.StartCoroutine(coroutine); + + MelonLoaderComponent.QueuedCoroutines.Add(coroutine); + return coroutine; + } + + public void StopCoroutine(object coroutineToken) + { + if (Main.comp == null) + MelonLoaderComponent.QueuedCoroutines.Remove(coroutineToken as IEnumerator); + else + Main.comp.StopCoroutine(coroutineToken as Coroutine); + } + public void UnityDebugLog(string msg) => Debug.Log(msg); public void Destroy() => MelonLoaderComponent.Destroy(); } diff --git a/MelonLoader.Support.Mono/AssemblyInfo.cs b/MelonLoader.Support.Mono/AssemblyInfo.cs index d61280ae9..2d5366faa 100644 --- a/MelonLoader.Support.Mono/AssemblyInfo.cs +++ b/MelonLoader.Support.Mono/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle(MelonLoader.BuildInfo.Description)] [assembly: AssemblyDescription(MelonLoader.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct(MelonLoader.BuildInfo.Name + ".Support.Mono")] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("981297b8-ef03-41a8-ae14-1e0e4fcdee30")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file diff --git a/MelonLoader.Support.Mono/Main.cs b/MelonLoader.Support.Mono/Main.cs index 9eb5c9482..53f900d4c 100644 --- a/MelonLoader.Support.Mono/Main.cs +++ b/MelonLoader.Support.Mono/Main.cs @@ -1,9 +1,7 @@ -using System; -using System.Linq; -using System.Reflection; +using System.Collections; +using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; -using Harmony; namespace MelonLoader.Support { @@ -12,33 +10,41 @@ internal static class Main internal static bool IsDestroying = false; internal static GameObject obj = null; internal static MelonLoaderComponent comp = null; - private static ISupportModule Initialize() { - MelonLoaderComponent.Create(); SceneManager.sceneLoaded += OnSceneLoad; return new Module(); } - - private static void OnSceneLoad(Scene scene, LoadSceneMode mode) { if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex); } + private static void OnSceneLoad(Scene scene, LoadSceneMode mode) + { + if (obj == null) MelonLoaderComponent.Create(); + if (!scene.Equals(null)) SceneHandler.OnSceneLoad(scene.buildIndex); + } } + public class MelonLoaderComponent : MonoBehaviour { + internal static readonly List QueuedCoroutines = new List(); internal static void Create() { - Main.obj = new GameObject("MelonLoader"); + Main.obj = new GameObject(); DontDestroyOnLoad(Main.obj); - Main.comp = Main.obj.AddComponent(); + Main.comp = (MelonLoaderComponent)Main.obj.AddComponent(typeof(MelonLoaderComponent)); Main.obj.transform.SetAsLastSibling(); Main.comp.transform.SetAsLastSibling(); } internal static void Destroy() { Main.IsDestroying = true; if (Main.obj != null) GameObject.Destroy(Main.obj); } + void Awake() + { + foreach (var queuedCoroutine in QueuedCoroutines) StartCoroutine(queuedCoroutine); + QueuedCoroutines.Clear(); + } void Start() => transform.SetAsLastSibling(); - void Update() { transform.SetAsLastSibling(); MelonLoader.Main.OnUpdate(); } - void FixedUpdate() => MelonLoader.Main.OnFixedUpdate(); - void LateUpdate() => MelonLoader.Main.OnLateUpdate(); - void OnGUI() => MelonLoader.Main.OnGUI(); + void Update() { transform.SetAsLastSibling(); MelonHandler.OnUpdate(); } + void FixedUpdate() => MelonHandler.OnFixedUpdate(); + void LateUpdate() => MelonHandler.OnLateUpdate(); + void OnGUI() => MelonHandler.OnGUI(); void OnDestroy() { if (!Main.IsDestroying) Create(); } - void OnApplicationQuit() { Destroy(); MelonLoader.Main.OnApplicationQuit(); } + void OnApplicationQuit() { Destroy(); MelonHandler.OnApplicationQuit(); } } } \ No newline at end of file diff --git a/MelonLoader.Support.Mono/SupportModule.cs b/MelonLoader.Support.Mono/SupportModule.cs index 75dd6be38..d6d752dcc 100644 --- a/MelonLoader.Support.Mono/SupportModule.cs +++ b/MelonLoader.Support.Mono/SupportModule.cs @@ -7,8 +7,22 @@ namespace MelonLoader.Support public class Module : ISupportModule { public int GetActiveSceneIndex() => SceneManager.GetActiveScene().buildIndex; - public object StartCoroutine(IEnumerator coroutine) => Main.comp.StartCoroutine(coroutine); - public void StopCoroutine(object coroutineToken) => Main.comp.StopCoroutine((Coroutine) coroutineToken); + public object StartCoroutine(IEnumerator coroutine) + { + if (Main.comp != null) return Main.comp.StartCoroutine(coroutine); + + MelonLoaderComponent.QueuedCoroutines.Add(coroutine); + return coroutine; + } + + public void StopCoroutine(object coroutineToken) + { + if (Main.comp == null) + MelonLoaderComponent.QueuedCoroutines.Remove(coroutineToken as IEnumerator); + else + Main.comp.StopCoroutine(coroutineToken as Coroutine); + } + public void UnityDebugLog(string msg) => Debug.Log(msg); public void Destroy() => MelonLoaderComponent.Destroy(); } diff --git a/MelonLoader/AssertionManager.cpp b/MelonLoader/AssertionManager.cpp index 87a6b6a07..3db6bb526 100644 --- a/MelonLoader/AssertionManager.cpp +++ b/MelonLoader/AssertionManager.cpp @@ -17,16 +17,27 @@ void AssertionManager::ThrowError(std::string msg, const char* filepath) { if (!Result) { - Result = true; msg += (" for [ " + std::string(FileName) + " | " + Position + " ]"); if (filepath != NULL) msg += " in { " + std::string(filepath) + "}"; - Logger::LogError(msg); + ThrowInternalError(msg); + MelonLoader::UNLOAD(true); + } +} + +void AssertionManager::ThrowInternalError(std::string msg) +{ + if (!Result) + { + Result = true; + Logger::LogTimestamp(ConsoleColor_Red); + Logger::LogFile << "[Error] " << msg << std::endl; + Console::Write("[MelonLoader] ", ConsoleColor_Red); + Console::WriteLine(("[Error] " + msg), ConsoleColor_Red); if (MelonLoader::DebugMode) MessageBox(NULL, msg.c_str(), "MelonLoader - INTERNAL FAILURE", MB_OK | MB_ICONERROR); else MessageBox(NULL, "Please Post your Latest Log File\non #internal-failure in the MelonLoader Discord!", "MelonLoader - INTERNAL FAILURE!", MB_OK | MB_ICONERROR); - MelonLoader::UNLOAD(); } } diff --git a/MelonLoader/AssertionManager.h b/MelonLoader/AssertionManager.h index cc5c191aa..6e028ed26 100644 --- a/MelonLoader/AssertionManager.h +++ b/MelonLoader/AssertionManager.h @@ -6,16 +6,15 @@ class AssertionManager { public: + static const char* FileName; + static const char* Position; static bool Result; static void Start(const char* filename, const char* position); static void ThrowError(std::string msg, const char* filepath = NULL); + static void ThrowInternalError(std::string msg); static void Decide(void* thing, const char* name); static HMODULE LoadLib(const char* name, const char* filepath); static HMODULE GetModuleHandlePtr(const char* name); static FARPROC GetExport(HMODULE mod, const char* export_name); - -private: - static const char* FileName; - static const char* Position; }; \ No newline at end of file diff --git a/MelonLoader/Console.cpp b/MelonLoader/Console.cpp index 2d63d7eb5..dae71b46e 100644 --- a/MelonLoader/Console.cpp +++ b/MelonLoader/Console.cpp @@ -9,6 +9,7 @@ bool Console::HordiniMode = false; bool Console::HordiniMode_Random = false; bool Console::ChromiumMode = false; bool Console::ShouldShowGameLogs = false; +bool Console::AlwaysOnTop = false; void Console::Create() { @@ -19,6 +20,7 @@ void Console::Create() hwndConsole = GetConsoleWindow(); SetTitle(("MelonLoader " + (MelonLoader::DebugMode ? std::string("Debug") : std::string("Normal")) + " Console").c_str()); SetForegroundWindow(hwndConsole); + AlwaysOnTopCheck(); freopen_s(reinterpret_cast(stdout), "CONOUT$", "w", stdout); } else @@ -50,12 +52,22 @@ void Console::ChromiumCheck() SetColor(ConsoleColor_Magenta); } +void Console::AlwaysOnTopCheck() +{ + if (IsInitialized() && AlwaysOnTop) + { + SetWindowPos(hwndConsole, HWND_TOPMOST, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + ShowWindow(hwndConsole, SW_NORMAL); + } +} + void Console::Write(const char* txt) { if (IsInitialized()) { ChromiumCheck(); RainbowCheck(); + AlwaysOnTopCheck(); std::cout << txt; ResetColor(); } diff --git a/MelonLoader/Console.h b/MelonLoader/Console.h index 4a33c5b04..40e63a5dc 100644 --- a/MelonLoader/Console.h +++ b/MelonLoader/Console.h @@ -82,6 +82,7 @@ class Console static bool HordiniMode_Random; static bool ChromiumMode; static bool ShouldShowGameLogs; + static bool AlwaysOnTop; static bool IsInitialized() { return (hwndConsole != NULL); } static void Create(); @@ -91,6 +92,7 @@ class Console static void ResetColor() { SetColor(ConsoleColor_Gray); } static void RainbowCheck(); static void ChromiumCheck(); + static void AlwaysOnTopCheck(); static void Write(const char* txt); static void Write(const char* txt, ConsoleColor color); diff --git a/MelonLoader/DisableAnalytics.cpp b/MelonLoader/DisableAnalytics.cpp index 7785ab41e..4eadb7159 100644 --- a/MelonLoader/DisableAnalytics.cpp +++ b/MelonLoader/DisableAnalytics.cpp @@ -1,7 +1,6 @@ #include "DisableAnalytics.h" #include "HookManager.h" #include "Logger.h" -#include "AssertionManager.h" gethostbyname_t DisableAnalytics::Original_gethostbyname = NULL; getaddrinfo_t DisableAnalytics::Original_getaddrinfo = NULL; @@ -27,27 +26,31 @@ std::list DisableAnalytics::URL_Blacklist = { "crashlytics.com" }; -bool DisableAnalytics::Setup() +void DisableAnalytics::Setup() { - AssertionManager::Start("DisableAnalytics.cpp", "DisableAnalytics::Setup"); - HMODULE wsock32 = GetModuleHandle("wsock32.dll"); if (wsock32 != NULL) { - Original_gethostbyname = (gethostbyname_t)AssertionManager::GetExport(wsock32, "gethostbyname"); + Original_gethostbyname = (gethostbyname_t)GetProcAddress(wsock32, "gethostbyname"); if (Original_gethostbyname != NULL) HookManager::Hook(&(LPVOID&)Original_gethostbyname, Hooked_gethostbyname); + else + Logger::DebugLogWarning("Failed to GetProcAddress ( gethostbyname ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]"); } + else + Logger::DebugLogWarning("Failed to GetModuleHandle ( wsock32.dll ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]"); - HMODULE ws2_32 = AssertionManager::GetModuleHandlePtr("ws2_32"); + HMODULE ws2_32 = GetModuleHandle("ws2_32"); if (ws2_32 != NULL) { - Original_getaddrinfo = (getaddrinfo_t)AssertionManager::GetExport(ws2_32, "getaddrinfo"); + Original_getaddrinfo = (getaddrinfo_t)GetProcAddress(ws2_32, "getaddrinfo"); if (Original_getaddrinfo != NULL) HookManager::Hook(&(LPVOID&)Original_getaddrinfo, Hooked_getaddrinfo); + else + Logger::DebugLogWarning("Failed to GetProcAddress ( getaddrinfo ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]"); } - - return !AssertionManager::Result; + else + Logger::DebugLogWarning("Failed to GetModuleHandle ( ws2_32 ) for [ DisableAnalytics.cpp | DisableAnalytics::Setup ]"); } bool DisableAnalytics::CheckBlacklist(std::string url) @@ -56,4 +59,18 @@ bool DisableAnalytics::CheckBlacklist(std::string url) if (url_found) Logger::DebugLog("Analytics URL Blocked: " + url); return url_found; -} \ No newline at end of file +} + +void* DisableAnalytics::Hooked_gethostbyname(const char* name) +{ + if (CheckBlacklist(name)) + return NULL; + return Original_gethostbyname(name); +} + +int DisableAnalytics::Hooked_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, void* pHints, void* ppResult) +{ + if (CheckBlacklist(pNodeName)) + return WSAHOST_NOT_FOUND; + return Original_getaddrinfo(pNodeName, pServiceName, pHints, ppResult); +} diff --git a/MelonLoader/DisableAnalytics.h b/MelonLoader/DisableAnalytics.h index 1d1acf74f..ad057f487 100644 --- a/MelonLoader/DisableAnalytics.h +++ b/MelonLoader/DisableAnalytics.h @@ -13,8 +13,8 @@ class DisableAnalytics static getaddrinfo_t Original_getaddrinfo; static std::listURL_Blacklist; - static bool Setup(); + static void Setup(); static bool CheckBlacklist(std::string url); - static void* Hooked_gethostbyname(const char* name) { if (CheckBlacklist(name)) return NULL; return Original_gethostbyname(name); } - static int Hooked_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, void* pHints, void* ppResult) { if (CheckBlacklist(pNodeName)) return WSAEHOSTDOWN; return Original_getaddrinfo(pNodeName, pServiceName, pHints, ppResult); } + static void* Hooked_gethostbyname(const char* name); + static int Hooked_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, void* pHints, void* ppResult); }; \ No newline at end of file diff --git a/MelonLoader/DllMain.cpp b/MelonLoader/DllMain.cpp index f30ecb608..f34fe7552 100644 --- a/MelonLoader/DllMain.cpp +++ b/MelonLoader/DllMain.cpp @@ -1,6 +1,6 @@ #include #include "MelonLoader.h" -#include "ModHandler.h" +#include "MelonLoader_Base.h" BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { diff --git a/MelonLoader/Exports.cpp b/MelonLoader/Exports.cpp index 36524c0e0..b91173288 100644 --- a/MelonLoader/Exports.cpp +++ b/MelonLoader/Exports.cpp @@ -4,13 +4,14 @@ #include "Mono.h" #include "HookManager.h" #include "Logger.h" +#include "AssertionManager.h" -void Log(MonoString* txt) { Logger::Log(Mono::mono_string_to_utf8(txt)); } -void LogColor(MonoString* txt, ConsoleColor color) { Logger::Log(Mono::mono_string_to_utf8(txt), color); } +void Log(MonoString* namesection, MonoString* txt) { Logger::Log(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); } +void LogColor(MonoString* namesection, MonoString* txt, ConsoleColor color) { Logger::Log(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt), color); } void LogWarning(MonoString* namesection, MonoString* txt) { Logger::LogWarning(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); } void LogError(MonoString* namesection, MonoString* txt) { Logger::LogError(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); } -void LogDLLError(MonoString* namesection, MonoString* msg) { Logger::LogDLLError(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(msg)); } -void LogDLLStatus(ModHandler_DLLStatus type) { Logger::LogDLLStatus(type); } +void LogMelonError(MonoString* namesection, MonoString* txt) { Logger::LogMelonError(Mono::mono_string_to_utf8(namesection), Mono::mono_string_to_utf8(txt)); } +void LogMelonCompatibility(MelonLoader_Base::MelonCompatibility comp) { Logger::LogMelonCompatibility(comp); } bool IsIl2CppGame() { return MelonLoader::IsGameIl2Cpp; } bool IsDebugMode() { return MelonLoader::DebugMode; } bool IsConsoleEnabled() { return Console::Enabled; } @@ -26,43 +27,47 @@ MonoString* GetAssemblyDirectory() { return Mono::mono_string_new(Mono::Domain, MonoString* GetMonoConfigDirectory() { return Mono::mono_string_new(Mono::Domain, Mono::ConfigPath); } MonoString* GetExePath() { return Mono::mono_string_new(Mono::Domain, MelonLoader::ExePath); } bool IsQuitFix() { return MelonLoader::QuitFix; } -bool IsDevModsOnly() { return MelonLoader::DevModsOnly; } -bool IsDevPluginsOnly() { return MelonLoader::DevPluginsOnly; } +MelonLoader::LoadMode GetLoadMode_Plugins() { return MelonLoader::LoadMode_Plugins; } +MelonLoader::LoadMode GetLoadMode_Mods() { return MelonLoader::LoadMode_Mods; } bool AG_Force_Regenerate() { return MelonLoader::AG_Force_Regenerate; } MonoString* AG_Force_Version_Unhollower() { if (MelonLoader::ForceUnhollowerVersion != NULL) return Mono::mono_string_new(Mono::Domain, MelonLoader::ForceUnhollowerVersion); return NULL; } void SetTitleForConsole(MonoString* txt) { Console::SetTitle(Mono::mono_string_to_utf8(txt)); } +void ThrowInternalError(MonoString* txt) { AssertionManager::ThrowInternalError(Mono::mono_string_to_utf8(txt)); } void Exports::AddInternalCalls() { - Mono::mono_add_internal_call("MelonLoader.Imports::UNLOAD_MELONLOADER", MelonLoader::UNLOAD); Mono::mono_add_internal_call("MelonLoader.Imports::IsIl2CppGame", IsIl2CppGame); Mono::mono_add_internal_call("MelonLoader.Imports::IsDebugMode", IsDebugMode); - Mono::mono_add_internal_call("MelonLoader.Imports::IsConsoleEnabled", IsConsoleEnabled); Mono::mono_add_internal_call("MelonLoader.Imports::GetGameDirectory", GetGameDirectory); Mono::mono_add_internal_call("MelonLoader.Imports::GetGameDataDirectory", GetGameDataDirectory); Mono::mono_add_internal_call("MelonLoader.Imports::GetAssemblyDirectory", GetAssemblyDirectory); Mono::mono_add_internal_call("MelonLoader.Imports::GetMonoConfigDirectory", GetMonoConfigDirectory); Mono::mono_add_internal_call("MelonLoader.Imports::Hook", Hook); Mono::mono_add_internal_call("MelonLoader.Imports::Unhook", Unhook); - Mono::mono_add_internal_call("MelonLoader.Imports::IsOldMono", IsOldMono); Mono::mono_add_internal_call("MelonLoader.Imports::GetCompanyName", GetCompanyName); Mono::mono_add_internal_call("MelonLoader.Imports::GetProductName", GetProductName); Mono::mono_add_internal_call("MelonLoader.Imports::GetExePath", GetExePath); - Mono::mono_add_internal_call("MelonLoader.Imports::IsQuitFix", IsQuitFix); - Mono::mono_add_internal_call("MelonLoader.Imports::IsDevModsOnly", IsDevModsOnly); - Mono::mono_add_internal_call("MelonLoader.Imports::IsDevPluginsOnly", IsDevPluginsOnly); - Mono::mono_add_internal_call("MelonLoader.Imports::AG_Force_Regenerate", AG_Force_Regenerate); - Mono::mono_add_internal_call("MelonLoader.Imports::AG_Force_Version_Unhollower", AG_Force_Version_Unhollower); - Mono::mono_add_internal_call("MelonLoader.Console::Allocate", Console::Create); - Mono::mono_add_internal_call("MelonLoader.Console::SetTitle", SetTitleForConsole); - Mono::mono_add_internal_call("MelonLoader.Console::SetColor", Console::SetColor); - //Mono::mono_add_internal_call("MelonLoader.Console::ShouldShowGameLogs", ShouldShowGameLogs); + Mono::mono_add_internal_call("MelonLoader.MelonLoaderBase::IsOldMono", IsOldMono); + Mono::mono_add_internal_call("MelonLoader.MelonLoaderBase::IsQuitFix", IsQuitFix); + Mono::mono_add_internal_call("MelonLoader.MelonLoaderBase::UNLOAD", MelonLoader::UNLOAD); - Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_Log", Log); - Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogColor", LogColor); - Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogWarning", LogWarning); - Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogError", LogError); - Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogDLLError", LogDLLError); - Mono::mono_add_internal_call("MelonLoader.MelonModLogger::Native_LogDLLStatus", LogDLLStatus); + Mono::mono_add_internal_call("MelonLoader.MelonHandler::GetLoadMode_Plugins", GetLoadMode_Plugins); + Mono::mono_add_internal_call("MelonLoader.MelonHandler::GetLoadMode_Mods", GetLoadMode_Mods); + + Mono::mono_add_internal_call("MelonLoader.MelonConsole::Allocate", Console::Create); + Mono::mono_add_internal_call("MelonLoader.MelonConsole::SetTitle", SetTitleForConsole); + Mono::mono_add_internal_call("MelonLoader.MelonConsole::SetColor", Console::SetColor); + Mono::mono_add_internal_call("MelonLoader.MelonConsole::IsConsoleEnabled", IsConsoleEnabled); + + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_Log", Log); + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogColor", LogColor); + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogWarning", LogWarning); + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogError", LogError); + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogMelonError", LogMelonError); + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_LogMelonCompatibility", LogMelonCompatibility); + Mono::mono_add_internal_call("MelonLoader.MelonLogger::Native_ThrowInternalError", ThrowInternalError); + + Mono::mono_add_internal_call("MelonLoader.AssemblyGenerator::Force_Regenerate", AG_Force_Regenerate); + Mono::mono_add_internal_call("MelonLoader.AssemblyGenerator::Force_Version_Unhollower", AG_Force_Version_Unhollower); } \ No newline at end of file diff --git a/MelonLoader/HookManager.cpp b/MelonLoader/HookManager.cpp index 24162f660..72f01bf40 100644 --- a/MelonLoader/HookManager.cpp +++ b/MelonLoader/HookManager.cpp @@ -1,12 +1,13 @@ #include #include "HookManager.h" -#include "ModHandler.h" +#include "MelonLoader_Base.h" #include "MelonLoader.h" #include "Console.h" #include "Detours/detours.h" #include "AssertionManager.h" #include "Logger.h" #include "Exports.h" +#include "DisableAnalytics.h" #include #pragma region Core @@ -126,8 +127,8 @@ HMODULE __stdcall HookManager::Hooked_LoadLibraryW(LPCWSTR lpLibFileName) { if (Il2Cpp::Setup(lib)) { - Mono::CreateDomain(); Hook(&(LPVOID&)Il2Cpp::il2cpp_init, Hooked_il2cpp_init); + Hook(&(LPVOID&)Il2Cpp::il2cpp_unity_install_unitytls_interface, Hooked_il2cpp_unity_install_unitytls_interface); } LoadLibraryW_Unhook(); } @@ -147,11 +148,23 @@ HMODULE __stdcall HookManager::Hooked_LoadLibraryW(LPCWSTR lpLibFileName) } #pragma endregion +#pragma region il2cpp_unity_install_unitytls_interface +void HookManager::Hooked_il2cpp_unity_install_unitytls_interface(const void* unitytlsInterfaceStruct) +{ + Il2Cpp::il2cpp_unity_install_unitytls_interface(unitytlsInterfaceStruct); + Mono::mono_unity_install_unitytls_interface(unitytlsInterfaceStruct); +} +#pragma endregion + #pragma region il2cpp_init Il2CppDomain* HookManager::Hooked_il2cpp_init(const char* name) { - Exports::AddInternalCalls(); - ModHandler::Initialize(); + if (Mono::Load() && Mono::Setup()) + { + Mono::CreateDomain(); + Exports::AddInternalCalls(); + MelonLoader_Base::Initialize(); + } Il2Cpp::Domain = Il2Cpp::il2cpp_init(name); Unhook(&(LPVOID&)Il2Cpp::il2cpp_init, Hooked_il2cpp_init); return Il2Cpp::Domain; @@ -165,7 +178,7 @@ MonoDomain* HookManager::Hooked_mono_jit_init_version(const char* name, const ch Mono::Domain = Mono::mono_jit_init_version(name, version); Mono::FixDomainBaseDir(); Exports::AddInternalCalls(); - ModHandler::Initialize(); + MelonLoader_Base::Initialize(); return Mono::Domain; } #pragma endregion @@ -184,7 +197,7 @@ void* HookManager::Hooked_runtime_invoke(const void* method, void* obj, void** p Unhook(&(LPVOID&)Il2Cpp::il2cpp_runtime_invoke, Hooked_runtime_invoke); else Unhook(&(LPVOID&)Mono::mono_runtime_invoke, Hooked_runtime_invoke); - ModHandler::OnApplicationStart(); + MelonLoader_Base::Startup(); } if (MelonLoader::IsGameIl2Cpp) return Il2Cpp::il2cpp_runtime_invoke((Il2CppMethod*)method, obj, params, (Il2CppObject**)exc); diff --git a/MelonLoader/HookManager.h b/MelonLoader/HookManager.h index 8a7b5d7c2..bbaa3a9d0 100644 --- a/MelonLoader/HookManager.h +++ b/MelonLoader/HookManager.h @@ -37,4 +37,5 @@ class HookManager static Il2CppDomain* Hooked_il2cpp_init(const char* name); static MonoDomain* Hooked_mono_jit_init_version(const char* name, const char* version); static void* Hooked_runtime_invoke(const void* method, void* obj, void** params, void** exc); + static void Hooked_il2cpp_unity_install_unitytls_interface(const void* unitytlsInterfaceStruct); }; \ No newline at end of file diff --git a/MelonLoader/IL2CPP.cpp b/MelonLoader/IL2CPP.cpp index b09f6289d..963a24559 100644 --- a/MelonLoader/IL2CPP.cpp +++ b/MelonLoader/IL2CPP.cpp @@ -7,6 +7,7 @@ Il2CppDomain* Il2Cpp::Domain = NULL; il2cpp_init_t Il2Cpp::il2cpp_init = NULL; il2cpp_runtime_invoke_t Il2Cpp::il2cpp_runtime_invoke = NULL; il2cpp_method_get_name_t Il2Cpp::il2cpp_method_get_name = NULL; +il2cpp_unity_install_unitytls_interface_t Il2Cpp::il2cpp_unity_install_unitytls_interface = NULL; bool Il2Cpp::Setup(HMODULE mod) { @@ -15,6 +16,7 @@ bool Il2Cpp::Setup(HMODULE mod) il2cpp_init = (il2cpp_init_t)AssertionManager::GetExport(mod, "il2cpp_init"); il2cpp_runtime_invoke = (il2cpp_runtime_invoke_t)AssertionManager::GetExport(mod, "il2cpp_runtime_invoke"); il2cpp_method_get_name = (il2cpp_method_get_name_t)AssertionManager::GetExport(mod, "il2cpp_method_get_name"); + il2cpp_unity_install_unitytls_interface = (il2cpp_unity_install_unitytls_interface_t)AssertionManager::GetExport(mod, "il2cpp_unity_install_unitytls_interface"); return !AssertionManager::Result; } \ No newline at end of file diff --git a/MelonLoader/IL2CPP.h b/MelonLoader/IL2CPP.h index 54c597756..3b4ed07bd 100644 --- a/MelonLoader/IL2CPP.h +++ b/MelonLoader/IL2CPP.h @@ -11,6 +11,7 @@ struct Il2CppObject; typedef Il2CppDomain* (*il2cpp_init_t) (const char* name); typedef Il2CppObject* (*il2cpp_runtime_invoke_t) (Il2CppMethod* method, void* obj, void** params, Il2CppObject** exc); typedef const char* (*il2cpp_method_get_name_t) (Il2CppMethod* method); +typedef void (*il2cpp_unity_install_unitytls_interface_t)(const void* unitytlsInterfaceStruct); class Il2Cpp { @@ -19,6 +20,7 @@ class Il2Cpp static il2cpp_init_t il2cpp_init; static il2cpp_runtime_invoke_t il2cpp_runtime_invoke; static il2cpp_method_get_name_t il2cpp_method_get_name; + static il2cpp_unity_install_unitytls_interface_t il2cpp_unity_install_unitytls_interface; static bool Setup(HMODULE mod); }; \ No newline at end of file diff --git a/MelonLoader/Logger.cpp b/MelonLoader/Logger.cpp index cde310650..b513b5746 100644 --- a/MelonLoader/Logger.cpp +++ b/MelonLoader/Logger.cpp @@ -24,13 +24,13 @@ void Logger::Initialize(std::string filepathstr) MaxWarnings = 0; MaxErrors = 0; } - auto now = std::chrono::system_clock::now(); - auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; - auto timer = std::chrono::system_clock::to_time_t(now); + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + time_t timer = std::chrono::system_clock::to_time_t(now); std::tm bt; localtime_s(&bt, &timer); std::string logFolderPath = filepathstr + "\\Logs"; - if (!MelonLoader::DirectoryExists(logFolderPath.c_str())) + if (!DirectoryExists(logFolderPath.c_str())) int returnval = _mkdir(logFolderPath.c_str()); else CleanOldLogs(logFolderPath); @@ -64,9 +64,9 @@ void Logger::CleanOldLogs(std::string logFolderPath) void Logger::LogTimestamp(ConsoleColor color) { - auto now = std::chrono::system_clock::now(); - auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; - auto timer = std::chrono::system_clock::to_time_t(now); + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + time_t timer = std::chrono::system_clock::to_time_t(now); std::tm bt; localtime_s(&bt, &timer); std::stringstream output; @@ -85,7 +85,6 @@ void Logger::Log(const char* txt) Console::Write("MelonLoader", ConsoleColor_Magenta); Console::Write("] "); Console::WriteLine(txt); - ModHandler::RunLogCallbacks(txt); } void Logger::Log(const char* txt, ConsoleColor color) @@ -96,7 +95,26 @@ void Logger::Log(const char* txt, ConsoleColor color) Console::Write("MelonLoader", ConsoleColor_Magenta); Console::Write("] "); Console::WriteLine(txt, color); - ModHandler::RunLogCallbacks(txt); +} + +void Logger::Log(const char* namesection, const char* txt) +{ + LogTimestamp(); + LogFile << namesection << txt << std::endl; + Console::Write("["); + Console::Write("MelonLoader", ConsoleColor_Magenta); + Console::Write("] "); + Console::WriteLine(std::string(namesection) + txt); +} + +void Logger::Log(const char* namesection, const char* txt, ConsoleColor color) +{ + LogTimestamp(color); + LogFile << namesection << txt << std::endl; + Console::Write("["); + Console::Write("MelonLoader", ConsoleColor_Magenta); + Console::Write("] "); + Console::WriteLine(std::string(namesection) + txt, color); } void Logger::LogWarning(const char* txt) @@ -109,7 +127,6 @@ void Logger::LogWarning(const char* txt) { Console::Write("[MelonLoader] ", ConsoleColor_Yellow); Console::WriteLine(("[Warning] " + std::string(txt)), ConsoleColor_Yellow); - ModHandler::RunWarningCallbacks(txt); } if (MaxWarnings > 0) WarningCount++; @@ -126,7 +143,6 @@ void Logger::LogWarning(const char* namesection, const char* txt) { Console::Write("[MelonLoader] ", ConsoleColor_Yellow); Console::WriteLine((std::string(namesection) + "[Warning] " + std::string(txt)), ConsoleColor_Yellow); - ModHandler::RunWarningCallbacks(namesection, txt); } if (MaxWarnings > 0) WarningCount++; @@ -141,7 +157,6 @@ void Logger::LogError(const char* txt) LogFile << "[Error] " << txt << std::endl; Console::Write("[MelonLoader] ", ConsoleColor_Red); Console::WriteLine(("[Error] " + std::string(txt)), ConsoleColor_Red); - ModHandler::RunErrorCallbacks(txt); if (MaxErrors > 0) ErrorCount++; } @@ -155,40 +170,49 @@ void Logger::LogError(const char* namesection, const char* txt) LogFile << namesection << "[Error] " << txt << std::endl; Console::Write("[MelonLoader] ", ConsoleColor_Red); Console::WriteLine((std::string(namesection) + "[Error] " + std::string(txt)), ConsoleColor_Red); - ModHandler::RunErrorCallbacks(namesection, txt); if (MaxErrors > 0) ErrorCount++; } } -void Logger::LogDLLError(const char* namesection, const char* msg) +void Logger::LogMelonError(const char* namesection, const char* txt) { if ((MaxErrors <= 0) || (ErrorCount < MaxErrors)) { LogTimestamp(ConsoleColor_Yellow); - LogFile << namesection << "[Error] " << msg << std::endl; + LogFile << namesection << "[Error] " << txt << std::endl; Console::Write("[MelonLoader] ", ConsoleColor_Yellow); - Console::WriteLine((std::string(namesection) + "[Error] " + std::string(msg)), ConsoleColor_Yellow); - ModHandler::RunErrorCallbacks(namesection, msg); + Console::WriteLine((std::string(namesection) + "[Error] " + std::string(txt)), ConsoleColor_Yellow); if (MaxErrors > 0) ErrorCount++; } } -void Logger::LogDLLStatus(ModHandler_DLLStatus type) +void Logger::LogMelonCompatibility(MelonLoader_Base::MelonCompatibility comp) { LogTimestamp(); - LogFile << "Game Compatibility: " << ((type == ModHandler_DLLStatus_UNIVERSAL) ? "Universal" : ((type == ModHandler_DLLStatus_COMPATIBLE) ? "Compatible" : ((type == ModHandler_DLLStatus_NOATTRIBUTE) ? "No MelonModGameAttribute!" : "INCOMPATIBLE!"))) << std::endl; Console::Write("["); Console::Write("MelonLoader", ConsoleColor_Magenta); Console::Write("] "); + LogFile << "Game Compatibility: "; Console::Write("Game Compatibility: ", ConsoleColor_Blue); - if (type == ModHandler_DLLStatus_UNIVERSAL) + switch (comp) + { + case MelonLoader_Base::MelonCompatibility::UNIVERSAL: + LogFile << "Universal"; Console::WriteLine("Universal", ConsoleColor_Cyan); - else if (type == ModHandler_DLLStatus_COMPATIBLE) + break; + case MelonLoader_Base::MelonCompatibility::COMPATIBLE: + LogFile << "Compatible"; Console::WriteLine("Compatible", ConsoleColor_Green); - else if (type == ModHandler_DLLStatus_NOATTRIBUTE) - Console::WriteLine("No MelonModGameAttribute!", ConsoleColor_Yellow); - else + break; + case MelonLoader_Base::MelonCompatibility::NOATTRIBUTE: + LogFile << "No MelonGameAttribute!"; + Console::WriteLine("No MelonGameAttribute!", ConsoleColor_Yellow); + break; + default: + LogFile << "INCOMPATIBLE!"; Console::WriteLine("INCOMPATIBLE!", ConsoleColor_Red); -} + }; + LogFile << std::endl; +} \ No newline at end of file diff --git a/MelonLoader/Logger.h b/MelonLoader/Logger.h index dbe79cb25..379a0deb3 100644 --- a/MelonLoader/Logger.h +++ b/MelonLoader/Logger.h @@ -4,7 +4,7 @@ #include #include #include "Console.h" -#include "ModHandler.h" +#include "MelonLoader_Base.h" class LogStream { @@ -43,6 +43,8 @@ class Logger static void Log(const char* txt); static void Log(const char* txt, ConsoleColor color); + static void Log(const char* namesection, const char* txt); + static void Log(const char* namesection, const char* txt, ConsoleColor color); static void Log(std::string txt) { Log(txt.c_str()); }; static void Log(std::string txt, ConsoleColor color) { Log(txt.c_str(), color); } @@ -53,21 +55,25 @@ class Logger static void LogError(const char* txt); static void LogError(const char* namesection, const char* txt); static void LogError(std::string txt) { LogError(txt.c_str()); } - + static void DebugLog(const char* txt) { if (MelonLoader::DebugMode) Log(txt); } static void DebugLog(const char* txt, ConsoleColor color) { if (MelonLoader::DebugMode) Log(txt, color); }; static void DebugLog(std::string txt) { DebugLog(txt.c_str()); } static void DebugLog(std::string txt, ConsoleColor color) { DebugLog(txt.c_str(), color); } + static void DebugLogWarning(const char* txt) { if (MelonLoader::DebugMode) LogWarning(txt); } + static void DebugLogWarning(const char* namesection, const char* txt) { if (MelonLoader::DebugMode) LogWarning(namesection, txt); } + static void DebugLogWarning(std::string txt) { DebugLogWarning(txt.c_str()); } + static void DebugLogError(const char* txt) { if (MelonLoader::DebugMode) LogError(txt); } static void DebugLogError(const char* namesection, const char* txt) { if (MelonLoader::DebugMode) LogError(namesection, txt); } static void DebugLogError(std::string txt) { DebugLogError(txt.c_str()); } - static void LogDLLError(const char* namesection, const char* msg); - - static void LogDLLStatus(ModHandler_DLLStatus type); + static void LogMelonError(const char* namesection, const char* txt); + static void LogMelonCompatibility(MelonLoader_Base::MelonCompatibility comp); + static void LogTimestamp(ConsoleColor color = ConsoleColor_Black); private: - static void LogTimestamp(ConsoleColor color = ConsoleColor_Black); static bool CompareWritetime(const std::filesystem::directory_entry& first, const std::filesystem::directory_entry& second) { return first.last_write_time().time_since_epoch() >= second.last_write_time().time_since_epoch(); } + static bool DirectoryExists(const char* path) { struct stat info; if (stat(path, &info) != NULL) return false; if (info.st_mode & S_IFDIR) return true; return false; } }; \ No newline at end of file diff --git a/MelonLoader/MelonLoader.cpp b/MelonLoader/MelonLoader.cpp index 1ac6c2b55..c30683e71 100644 --- a/MelonLoader/MelonLoader.cpp +++ b/MelonLoader/MelonLoader.cpp @@ -8,7 +8,7 @@ #include "Mono.h" #include "HookManager.h" #include "Logger.h" -#include "ModHandler.h" +#include "MelonLoader_Base.h" #include #include "DisableAnalytics.h" #pragma warning(disable:4996) @@ -19,8 +19,6 @@ char* MelonLoader::CommandLineV[64]; bool MelonLoader::IsGameIl2Cpp = false; bool MelonLoader::DebugMode = false; bool MelonLoader::QuitFix = false; -bool MelonLoader::DevModsOnly = false; -bool MelonLoader::DevPluginsOnly = false; bool MelonLoader::AG_Force_Regenerate = false; char* MelonLoader::ExePath = NULL;; char* MelonLoader::GamePath = NULL; @@ -29,6 +27,8 @@ char* MelonLoader::CompanyName = NULL; char* MelonLoader::ProductName = NULL; char* MelonLoader::ForceUnhollowerVersion = NULL; char* MelonLoader::ForceUnityVersion = NULL; +MelonLoader::LoadMode MelonLoader::LoadMode_Plugins = MelonLoader::LoadMode::NORMAL; +MelonLoader::LoadMode MelonLoader::LoadMode_Mods = MelonLoader::LoadMode::NORMAL; void MelonLoader::Main() { @@ -60,7 +60,6 @@ void MelonLoader::Main() #ifdef DEBUG DebugMode = true; - Console::ShouldShowGameLogs = true; Console::Create(); #endif @@ -134,16 +133,8 @@ void MelonLoader::Main() ReadAppInfo(); - if (DisableAnalytics::Setup()) - { - if (IsGameIl2Cpp) - { - if (Mono::Load() && Mono::Setup()) - HookManager::LoadLibraryW_Hook(); - } - else - HookManager::LoadLibraryW_Hook(); - } + DisableAnalytics::Setup(); + HookManager::LoadLibraryW_Hook(); } } } @@ -173,10 +164,26 @@ void MelonLoader::ParseCommandLine() Console::HordiniMode = true; else if (strstr(command, "--melonloader.randomrainbow") != NULL) Console::HordiniMode_Random = true; - else if (strstr(command, "--melonloader.devmodsonly") != NULL) - DevModsOnly = true; - else if (strstr(command, "--melonloader.devpluginsonly") != NULL) - DevPluginsOnly = true; + else if (strstr(command, "--melonloader.consoleontop") != NULL) + Console::AlwaysOnTop = true; + else if (strstr(command, "--melonloader.loadmodeplugins") != NULL) + { + int loadmode = atoi(CommandLineV[i + 1]); + if (loadmode < 0) + loadmode = 0; + else if (loadmode > 2) + loadmode = 0; + LoadMode_Plugins = (LoadMode)loadmode; + } + else if (strstr(command, "--melonloader.loadmodemods") != NULL) + { + int loadmode = atoi(CommandLineV[i + 1]); + if (loadmode < 0) + loadmode = 0; + else if (loadmode > 2) + loadmode = 0; + LoadMode_Mods = (LoadMode)loadmode; + } else if (strstr(command, "--melonloader.agregenerate") != NULL) AG_Force_Regenerate = true; else if (strstr(command, "--melonloader.agfvunhollower")) @@ -194,8 +201,6 @@ void MelonLoader::ParseCommandLine() Console::Enabled = false; else if (strstr(command, "--melonloader.hidewarnings") != NULL) Console::HideWarnings = false; - //else if (strstr(command, "--melonloader.showgamelogs") != NULL) - // Console::ShouldShowGameLogs = false; else if (strstr(command, "--melonloader.debug") != NULL) { DebugMode = true; @@ -245,11 +250,11 @@ bool MelonLoader::CheckOSVersion() return true; } -void MelonLoader::UNLOAD() +void MelonLoader::UNLOAD(bool doquitfix) { HookManager::UnhookAll(); Logger::Stop(); - if (MelonLoader::QuitFix) + if (doquitfix && MelonLoader::QuitFix) MelonLoader::KillProcess(); } @@ -263,59 +268,23 @@ void MelonLoader::KillProcess() } } -int MelonLoader::CountSubstring(std::string pat, std::string txt) -{ - size_t M = pat.length(); - size_t N = txt.length(); - int res = 0; - for (int i = 0; i <= N - M; i++) - { - int j; - for (j = 0; j < M; j++) - if (txt[i + j] != pat[j]) - break; - if (j == M) - { - res++; - j = 0; - } - } - return res; -} - -bool MelonLoader::DirectoryExists(const char* path) -{ - struct stat info; - if (stat(path, &info) != NULL) - return false; - if (info.st_mode & S_IFDIR) - return true; - return false; -} - -long MelonLoader::GetFileSize(std::string filename) -{ - struct stat stat_buf; - return ((stat(filename.c_str(), &stat_buf) == 0) ? stat_buf.st_size : -1); -} - int MelonLoader::GetIntFromConstChar(const char* str, int defaultval) { - if (str == NULL || *str == '\0') + if ((str == NULL) || (*str == '\0')) return defaultval; bool negate = (str[0] == '-'); - if ( *str == '+' || *str == '-' ) + if ((*str == '+') || (*str == '-')) ++str; if (*str == '\0') return defaultval; int result = 0; while(*str) { - if (*str >= '0' && *str <= '9') - result = result * 10 - (*str - '0'); + if ((*str >= '0') && (*str <= '9')) + result = ((result * 10) - (*str - '0')); else return defaultval; ++str; } - return negate ? result : -result; + return (negate ? result : -result); } \ No newline at end of file diff --git a/MelonLoader/MelonLoader.h b/MelonLoader/MelonLoader.h index d3bfbe986..2849f374f 100644 --- a/MelonLoader/MelonLoader.h +++ b/MelonLoader/MelonLoader.h @@ -11,8 +11,6 @@ class MelonLoader static bool IsGameIl2Cpp; static bool DebugMode; static bool QuitFix; - static bool DevModsOnly; - static bool DevPluginsOnly; static bool AG_Force_Regenerate; static char* ExePath; static char* GamePath; @@ -22,14 +20,20 @@ class MelonLoader static char* ForceUnhollowerVersion; static char* ForceUnityVersion; + enum LoadMode + { + NORMAL, + DEV, + BOTH + }; + static LoadMode LoadMode_Plugins; + static LoadMode LoadMode_Mods; + static void Main(); static void ParseCommandLine(); static void ReadAppInfo(); static bool CheckOSVersion(); - static void UNLOAD(); + static void UNLOAD(bool doquitfix = true); static void KillProcess(); - static int CountSubstring(std::string pat, std::string txt); - static bool DirectoryExists(const char* path); - static long GetFileSize(std::string filename); static int GetIntFromConstChar(const char* str, int defaultval = 0); }; \ No newline at end of file diff --git a/MelonLoader/MelonLoader.vcxproj b/MelonLoader/MelonLoader.vcxproj index a4faf29ae..45994604e 100644 --- a/MelonLoader/MelonLoader.vcxproj +++ b/MelonLoader/MelonLoader.vcxproj @@ -98,7 +98,7 @@ - + @@ -112,7 +112,7 @@ - + diff --git a/MelonLoader/MelonLoader.vcxproj.filters b/MelonLoader/MelonLoader.vcxproj.filters index f20da8f8c..365ab21c0 100644 --- a/MelonLoader/MelonLoader.vcxproj.filters +++ b/MelonLoader/MelonLoader.vcxproj.filters @@ -6,19 +6,18 @@ - + - @@ -30,6 +29,7 @@ Detours + diff --git a/MelonLoader/MelonLoader_Base.cpp b/MelonLoader/MelonLoader_Base.cpp new file mode 100644 index 000000000..34d2cd203 --- /dev/null +++ b/MelonLoader/MelonLoader_Base.cpp @@ -0,0 +1,65 @@ +#include +#include +#include "MelonLoader_Base.h" +#include "MelonLoader.h" +#include "AssertionManager.h" +#include "Logger.h" +#include "HookManager.h" +#include "Il2Cpp.h" + +bool MelonLoader_Base::HasInitialized = false; +MonoMethod* MelonLoader_Base::startup = NULL; + +void MelonLoader_Base::Initialize() +{ + AssertionManager::Start("MelonLoader_Base.cpp", "MelonLoader_Base::Initialize"); + if (Mono::Domain != NULL) + { + std::string modhandlerpath = std::string(MelonLoader::GamePath) + "\\MelonLoader\\MelonLoader.ModHandler.dll"; + MonoAssembly* assembly = Mono::mono_domain_assembly_open(Mono::Domain, modhandlerpath.c_str()); + AssertionManager::Decide(assembly, "MelonLoader.ModHandler.dll"); + if (assembly != NULL) + { + MonoImage* image = Mono::mono_assembly_get_image(assembly); + AssertionManager::Decide(assembly, "Image"); + if (image != NULL) + { + MonoClass* klass = Mono::mono_class_from_name(image, "MelonLoader", "MelonLoaderBase"); + AssertionManager::Decide(assembly, "MelonLoader.MelonLoaderBase"); + if (klass != NULL) + { + MonoMethod* initialize = Mono::mono_class_get_method_from_name(klass, "Initialize", NULL); + AssertionManager::Decide(initialize, "Initialize"); + if (initialize != NULL) + { + MonoObject* exceptionObject = NULL; + Mono::mono_runtime_invoke(initialize, NULL, NULL, &exceptionObject); + if ((exceptionObject != NULL) && MelonLoader::DebugMode) + Mono::LogExceptionMessage(exceptionObject); + else + { + startup = Mono::mono_class_get_method_from_name(klass, "Startup", NULL); + AssertionManager::Decide(startup, "Startup"); + if (MelonLoader::IsGameIl2Cpp) + HookManager::Hook(&(LPVOID&)Il2Cpp::il2cpp_runtime_invoke, HookManager::Hooked_runtime_invoke); + else + HookManager::Hook(&(LPVOID&)Mono::mono_runtime_invoke, HookManager::Hooked_runtime_invoke); + HasInitialized = true; + } + } + } + } + } + } +} + +void MelonLoader_Base::Startup() +{ + if (startup != NULL) + { + MonoObject* exceptionObject = NULL; + Mono::mono_runtime_invoke(startup, NULL, NULL, &exceptionObject); + if ((exceptionObject != NULL) && MelonLoader::DebugMode) + Mono::LogExceptionMessage(exceptionObject); + } +} \ No newline at end of file diff --git a/MelonLoader/MelonLoader_Base.h b/MelonLoader/MelonLoader_Base.h new file mode 100644 index 000000000..ab2812d34 --- /dev/null +++ b/MelonLoader/MelonLoader_Base.h @@ -0,0 +1,20 @@ +#pragma once +#include "Mono.h" + +class MelonLoader_Base +{ +public: + enum MelonCompatibility + { + UNIVERSAL, + COMPATIBLE, + NOATTRIBUTE, + INCOMPATIBLE + }; + + static bool HasInitialized; + static MonoMethod* startup; + + static void Initialize(); + static void Startup(); +}; \ No newline at end of file diff --git a/MelonLoader/ModHandler.cpp b/MelonLoader/ModHandler.cpp deleted file mode 100644 index b2c5f3957..000000000 --- a/MelonLoader/ModHandler.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include -#include -#include "ModHandler.h" -#include "MelonLoader.h" -#include "AssertionManager.h" -#include "Logger.h" -#include "HookManager.h" -#include "Il2Cpp.h" - -bool ModHandler::HasInitialized = false; -MonoMethod* ModHandler::onApplicationStart = NULL; -MonoMethod* ModHandler::onApplicationQuit = NULL; -MonoMethod* ModHandler::runLogCallbacks = NULL; -MonoMethod* ModHandler::runWarningCallbacks1 = NULL; -MonoMethod* ModHandler::runWarningCallbacks2 = NULL; -MonoMethod* ModHandler::runErrorCallbacks1 = NULL; -MonoMethod* ModHandler::runErrorCallbacks2 = NULL; - -void ModHandler::Initialize() -{ - AssertionManager::Start("ModHandler.cpp", "ModHandler::Initialize"); - if (Mono::Domain != NULL) - { - std::string modhandlerpath = std::string(MelonLoader::GamePath) + "\\MelonLoader\\MelonLoader.ModHandler.dll"; - MonoAssembly* assembly = Mono::mono_domain_assembly_open(Mono::Domain, modhandlerpath.c_str()); - AssertionManager::Decide(assembly, "MelonLoader.ModHandler.dll"); - if (assembly != NULL) - { - MonoImage* image = Mono::mono_assembly_get_image(assembly); - AssertionManager::Decide(assembly, "Image"); - if (image != NULL) - { - MonoClass* klass = Mono::mono_class_from_name(image, "MelonLoader", "Main"); - AssertionManager::Decide(assembly, "MelonLoader.Main"); - if (klass != NULL) - { - MonoClass* klass2 = Mono::mono_class_from_name(image, "MelonLoader", "Console"); - AssertionManager::Decide(assembly, "MelonLoader.Console"); - if (klass2 != NULL) - { - MonoMethod* initialize = Mono::mono_class_get_method_from_name(klass, "Initialize", NULL); - AssertionManager::Decide(initialize, "Initialize"); - if (initialize != NULL) - { - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(initialize, NULL, NULL, &exceptionObject); - if (exceptionObject && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - else - { - onApplicationStart = Mono::mono_class_get_method_from_name(klass, "OnApplicationStart", NULL); - AssertionManager::Decide(onApplicationStart, "OnApplicationStart"); - onApplicationQuit = Mono::mono_class_get_method_from_name(klass, "OnApplicationQuit", NULL); - AssertionManager::Decide(onApplicationQuit, "OnApplicationQuit"); - runLogCallbacks = Mono::mono_class_get_method_from_name(klass2, "RunLogCallbacks", 1); - AssertionManager::Decide(runLogCallbacks, "RunLogCallbacks"); - runWarningCallbacks1 = Mono::mono_class_get_method_from_name(klass2, "RunWarningCallbacks", 1); - AssertionManager::Decide(runWarningCallbacks1, "RunWarningCallbacks (1)"); - runWarningCallbacks2 = Mono::mono_class_get_method_from_name(klass2, "RunWarningCallbacks", 2); - AssertionManager::Decide(runWarningCallbacks2, "RunWarningCallbacks (2)"); - runErrorCallbacks1 = Mono::mono_class_get_method_from_name(klass2, "RunErrorCallbacks", 1); - AssertionManager::Decide(runErrorCallbacks1, "RunErrorCallbacks (1)"); - runErrorCallbacks2 = Mono::mono_class_get_method_from_name(klass2, "RunErrorCallbacks", 2); - AssertionManager::Decide(runErrorCallbacks2, "RunErrorCallbacks (2)"); - if (MelonLoader::IsGameIl2Cpp) - HookManager::Hook(&(LPVOID&)Il2Cpp::il2cpp_runtime_invoke, HookManager::Hooked_runtime_invoke); - else - HookManager::Hook(&(LPVOID&)Mono::mono_runtime_invoke, HookManager::Hooked_runtime_invoke); - HasInitialized = true; - } - } - } - } - } - } - } -} - -void ModHandler::OnApplicationStart() -{ - if (onApplicationStart != NULL) - { - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(onApplicationStart, NULL, NULL, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} - -void ModHandler::OnApplicationQuit() -{ - if (onApplicationQuit != NULL) - { - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(onApplicationQuit, NULL, NULL, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} - -void ModHandler::RunLogCallbacks(const char* msg) -{ - if (runLogCallbacks != NULL) - { - MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg); - void* args[1] = { msgstr }; - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(runLogCallbacks, NULL, args, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} - -void ModHandler::RunWarningCallbacks(const char* msg) -{ - if (runWarningCallbacks1 != NULL) - { - MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg); - void* args[1] = { msgstr }; - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(runWarningCallbacks1, NULL, args, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} - -void ModHandler::RunWarningCallbacks(const char* namesection, const char* msg) -{ - if (runWarningCallbacks2 != NULL) - { - MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg); - MonoString* namesectionstr = Mono::mono_string_new(Mono::Domain, namesection); - void* args[2] = { namesectionstr, msgstr }; - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(runWarningCallbacks2, NULL, args, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} - -void ModHandler::RunErrorCallbacks(const char* msg) -{ - if (runErrorCallbacks1 != NULL) - { - MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg); - void* args[1] = { msgstr }; - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(runErrorCallbacks1, NULL, args, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} - -void ModHandler::RunErrorCallbacks(const char* namesection, const char* msg) -{ - if (runErrorCallbacks2 != NULL) - { - MonoString* msgstr = Mono::mono_string_new(Mono::Domain, msg); - MonoString* namesectionstr = Mono::mono_string_new(Mono::Domain, namesection); - void* args[2] = { namesectionstr, msgstr }; - MonoObject* exceptionObject = NULL; - Mono::mono_runtime_invoke(runErrorCallbacks2, NULL, args, &exceptionObject); - if ((exceptionObject != NULL) && MelonLoader::DebugMode) - Mono::LogExceptionMessage(exceptionObject); - } -} \ No newline at end of file diff --git a/MelonLoader/ModHandler.h b/MelonLoader/ModHandler.h deleted file mode 100644 index 9b5353341..000000000 --- a/MelonLoader/ModHandler.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "Mono.h" - -class ModHandler -{ -public: - static bool HasInitialized; - static MonoMethod* onApplicationStart; - static MonoMethod* onApplicationQuit; - static MonoMethod* runLogCallbacks; - static MonoMethod* runWarningCallbacks1; - static MonoMethod* runWarningCallbacks2; - static MonoMethod* runErrorCallbacks1; - static MonoMethod* runErrorCallbacks2; - - static void Initialize(); - static void OnApplicationStart(); - static void OnApplicationQuit(); - static void RunLogCallbacks(const char* msg); - static void RunWarningCallbacks(const char* msg); - static void RunWarningCallbacks(const char* namesection, const char* msg); - static void RunErrorCallbacks(const char* msg); - static void RunErrorCallbacks(const char* namesection, const char* msg); -}; - -enum ModHandler_DLLStatus -{ - ModHandler_DLLStatus_UNIVERSAL, - ModHandler_DLLStatus_COMPATIBLE, - ModHandler_DLLStatus_NOATTRIBUTE, - ModHandler_DLLStatus_INCOMPATIBLE -}; \ No newline at end of file diff --git a/MelonLoader/Mono.cpp b/MelonLoader/Mono.cpp index b34d1ffba..3021286f1 100644 --- a/MelonLoader/Mono.cpp +++ b/MelonLoader/Mono.cpp @@ -2,7 +2,7 @@ #include "Mono.h" #include "MelonLoader.h" #include "AssertionManager.h" -#include "ModHandler.h" +#include "MelonLoader_Base.h" #include "Logger.h" bool Mono::IsOldMono = false; @@ -34,9 +34,7 @@ mono_object_get_class_t Mono::mono_object_get_class = NULL; mono_runtime_set_main_args_t Mono::mono_runtime_set_main_args = NULL; mono_domain_set_config_t Mono::mono_domain_set_config = NULL; mono_method_get_name_t Mono::mono_method_get_name = NULL; -mono_debug_init_t Mono::mono_debug_init = NULL; -mono_debug_domain_create_t Mono::mono_debug_domain_create = NULL; -mono_jit_parse_options_t Mono::mono_jit_parse_options = NULL; +mono_unity_install_unitytls_interface_t Mono::mono_unity_install_unitytls_interface = NULL; bool Mono::Load() { @@ -84,11 +82,7 @@ bool Mono::Setup() mono_object_get_class = (mono_object_get_class_t)AssertionManager::GetExport(Module, "mono_object_get_class"); if (MelonLoader::IsGameIl2Cpp) - { - mono_debug_init = (mono_debug_init_t)AssertionManager::GetExport(Module, "mono_debug_init"); - mono_debug_domain_create = (mono_debug_domain_create_t)AssertionManager::GetExport(Module, "mono_debug_domain_create"); - mono_jit_parse_options = (mono_jit_parse_options_t)AssertionManager::GetExport(Module, "mono_jit_parse_options"); - } + mono_unity_install_unitytls_interface = (mono_unity_install_unitytls_interface_t)AssertionManager::GetExport(Module, "mono_unity_install_unitytls_interface"); if (!IsOldMono) { @@ -109,9 +103,6 @@ void Mono::CreateDomain() mono_runtime_set_main_args(MelonLoader::CommandLineC, MelonLoader::CommandLineV); Domain = mono_jit_init("MelonLoader"); Mono::FixDomainBaseDir(); - mono_debug_init(MONO_DEBUG_FORMAT_MONO); - mono_debug_domain_create(Domain); - mono_jit_parse_options(MelonLoader::CommandLineC, MelonLoader::CommandLineV); } } @@ -122,5 +113,43 @@ void Mono::FixDomainBaseDir() mono_domain_set_config(Domain, MelonLoader::GamePath, "MelonLoader"); } -const char* Mono::GetStringProperty(const char* propertyName, MonoClass* classType, MonoObject* classObject) { return mono_string_to_utf8((MonoString*)mono_runtime_invoke(mono_property_get_get_method(mono_class_get_property_from_name(classType, propertyName)), classObject, NULL, NULL)); } -void Mono::LogExceptionMessage(MonoObject* exceptionObject, bool shouldThrow) { if (shouldThrow) AssertionManager::ThrowError(GetStringProperty("Message", mono_object_get_class(exceptionObject), exceptionObject)); else Logger::LogError(GetStringProperty("Message", mono_object_get_class(exceptionObject), exceptionObject)); } \ No newline at end of file +const char* Mono::GetStringProperty(const char* propertyName, MonoClass* classType, MonoObject* classObject) +{ + if (propertyName == NULL) + return NULL; + if (classType == NULL) + return NULL; + if (classObject == NULL) + return NULL; + MonoProperty* prop = mono_class_get_property_from_name(classType, propertyName); + if (prop == NULL) + return NULL; + MonoMethod* method = mono_property_get_get_method(prop); + if (method == NULL) + return NULL; + MonoString* returnstr = (MonoString*)mono_runtime_invoke(method, classObject, NULL, NULL); + if (returnstr == NULL) + return NULL; + return mono_string_to_utf8(returnstr); +} + +void Mono::LogExceptionMessage(MonoObject* exceptionObject, bool shouldThrow) +{ + if (exceptionObject == NULL) + return; + MonoClass* klass = mono_object_get_class(exceptionObject); + if (klass == NULL) + return; + const char* stringprop = GetStringProperty("Message", klass, exceptionObject); + if (stringprop == NULL) + return; + if (shouldThrow) + AssertionManager::ThrowError(stringprop); + else + { + Logger::LogTimestamp(ConsoleColor_Red); + Logger::LogFile << "[Error] " << stringprop << std::endl; + Console::Write("[MelonLoader] ", ConsoleColor_Red); + Console::WriteLine(("[Error] " + std::string(stringprop)), ConsoleColor_Red); + } +} \ No newline at end of file diff --git a/MelonLoader/Mono.h b/MelonLoader/Mono.h index f67dbbd18..48a844356 100644 --- a/MelonLoader/Mono.h +++ b/MelonLoader/Mono.h @@ -43,6 +43,7 @@ typedef void (*mono_domain_set_config_t)(MonoDomain* domain, const char* configp typedef void (*mono_debug_init_t) (MonoDebugFormat format); typedef void (*mono_debug_domain_create_t) (MonoDomain* domain); typedef void (*mono_jit_parse_options_t)(int argc, char* argv[]); +typedef void (*mono_unity_install_unitytls_interface_t)(const void* unitytlsInterfaceStruct); class Mono { @@ -76,9 +77,7 @@ class Mono static mono_object_get_class_t mono_object_get_class; static mono_runtime_set_main_args_t mono_runtime_set_main_args; static mono_domain_set_config_t mono_domain_set_config; - static mono_debug_init_t mono_debug_init; - static mono_debug_domain_create_t mono_debug_domain_create; - static mono_jit_parse_options_t mono_jit_parse_options; + static mono_unity_install_unitytls_interface_t mono_unity_install_unitytls_interface; static bool Load(); static bool Setup(); diff --git a/README.md b/README.md index fd759323f..d37db3caa 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ +### GENERAL INFORMATION: + +- Normal Console is for Debugging and Displaying the Output of Plugins and Mods. +- Debug Mode/Console is for Debugging MelonLoader Internals. +- All Plugins get placed in the created Plugins folder inside the Game's Install Folder. +- All Mods get placed in the created Mods folder inside the Game's Install Folder. +- All Logs are made in the created Logs folder inside the Game's Install Folder. +- In order to properly use dnSpy's Debugger run the game with the Debug Mode/Console launch option. + +--- + ### REQUIREMENTS: - [.NET Framework 4.7.2 Runtime](https://dotnet.microsoft.com/download/dotnet-framework/net472) @@ -48,25 +59,10 @@ --- -### GENERAL INFORMATION: - -- All Plugins get placed in the created Plugins folder inside the Game's Install Folder. -- All Mods get placed in the created Mods folder inside the Game's Install Folder. -- All Logs are made in the created Logs folder inside the Game's Install Folder. - ---- - -### CONSOLE DIFFERENCES: - -- Normal Console is for Debugging and Displaying the Output of Plugins and Mods. -- Debug Mode/Console is for Debugging MelonLoader Internals. - ---- - ### LAUNCH OPTIONS: -| Argument | Description | -| --------------------- | ---------------------------------------- | +| Argument | Description | +| - | - | | --no-mods | Launch game without loading Mods | | --quitfix | Fixes the Hanging Process Issue with some Games | | --melonloader.hideconsole | Hides the Normal Console | @@ -75,20 +71,32 @@ | --melonloader.magenta | Magenta Console Color | | --melonloader.rainbow | Rainbow Console Color | | --melonloader.randomrainbow | Random Rainbow Console Color | -| --melonloader.maxlogs | Max Log Files [ Default: 10 ] [ Disable: 0 ] | -| --melonloader.maxwarnings | Max Warnings per Log File [ Default: 100 ] [ Disable: 0 ] | -| --melonloader.maxerrors | Max Errors per Log File [ Default: 100 ] [ Disable: 0 ] | -| --melonloader.devpluginsonly | Loads only Plugins with the "-dev.dll" extension | -| --melonloader.devmodsonly | Loads only Mods with the "-dev.dll" extension | +| --melonloader.maxlogs | Max Log Files [ Default: 10 ] [ Disable: 0 ] | +| --melonloader.maxwarnings | Max Warnings per Log File [ Default: 100 ] [ Disable: 0 ] | +| --melonloader.maxerrors | Max Errors per Log File [ Default: 100 ] [ Disable: 0 ] | +| --melonloader.loadmodeplugins | Load Mode for Plugins [ Default: 0 ] | +| --melonloader.loadmodemods | Load Mode for Mods [ Default: 0 ] | | --melonloader.agregenerate | Forces Assembly to be Regenerated on Il2Cpp Games | -| --melonloader.agfvunhollower | Forces the Assembly Generator to use a Specified Version of Il2CppAssemblyUnhollower | +| --melonloader.agfvunhollower | Forces use a Specified Version of Il2CppAssemblyUnhollower | +| --melonloader.consoleontop | Forces the Console over all other Applications | --- -### CREDITS+LICENSING: +### LOAD MODES: -MelonLoader is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/LICENSE.md) for the full License. -[MelonLoader.dll](MelonLoader) was adapted from [AtiRoNya](https://github.com/AtiLion/AtiRoNya) by [AtiLion](https://github.com/AtiLion), licensed under [MIT License](https://github.com/AtiLion/AtiRoNya/blob/e20e4a8fc47b37834c8284f9e6e937f04a84c510/LICENSE) +- Load Mode launch options are a way to dictate how you want Mods or Plugins to Load. + +| Value | Action | +| - | - | +| 0 | NORMAL - Load them only if they don't have the "-dev" Name Extension | +| 1 | DEV - Load them only if they have the "-dev" Name Extension | +| 2 | BOTH - Load All | + +--- + +### LICENSING & CREDITS: + +MelonLoader is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/LICENSE.md) for the full License. Third-party libraries used as source code or bundled in binary form: - [Research Detours Package](https://github.com/microsoft/Detours) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/Detours/LICENSE.md) for the full License. @@ -98,6 +106,7 @@ Third-party libraries used as source code or bundled in binary form: - [LightJson](https://github.com/MarcosLopezC/LightJson) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/MelonLoader.Installer/LightJson/LICENSE.txt) for the full License. - [Tomlyn](https://github.com/xoofx/Tomlyn) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/MelonLoader.ModHandler/Tomlyn/license.txt) for the full License. - [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) is licensed under the MIT License. See [LICENSE](https://github.com/HerpDerpinstine/MelonLoader/blob/master/MelonLoader.ModHandler/SharpZipLib/LICENSE.txt) for the full License. +- [AtiRoNya](https://github.com/AtiLion/AtiRoNya) is licensed under the MIT License. See [LICENSE](https://github.com/AtiLion/AtiRoNya/blob/e20e4a8fc47b37834c8284f9e6e937f04a84c510/LICENSE) for the full License. - Unity Runtime libraries (found in [Unity Dependencies](BaseLibs/Unity%20Dependencies)) are part of Unity Software. Their usage is subject to [Unity Terms of Service](https://unity3d.com/legal/terms-of-service), including [Unity Software Additional Terms](https://unity3d.com/legal/terms-of-service/software). @@ -105,9 +114,7 @@ External tools downloaded and used at runtime: - [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) is licensed under the MIT License. See [LICENSE](https://github.com/Perfare/Il2CppDumper/blob/master/LICENSE) for the full License. - [Il2CppAssemblyUnhollower](https://github.com/knah/Il2CppAssemblyUnhollower) is licensed under the GNU Lesser General Public License v3.0. See [LICENSE](https://github.com/knah/Il2CppAssemblyUnhollower/blob/master/LICENSE) for the full License. ---- - See [MelonLoader Wiki](https://melonwiki.xyz/#/credits) for the full Credits. MelonLoader is not sponsored by, affiliated with or endorsed by Unity Technologies or its affiliates. -"Unity" is a trademark or a registered trademark of Unity Technologies or its affiliates in the U.S. and elsewhere. \ No newline at end of file +"Unity" is a trademark or a registered trademark of Unity Technologies or its affiliates in the U.S. and elsewhere. diff --git a/TestMod/AssemblyInfo.cs b/TestMod/AssemblyInfo.cs index 55fa18241..3ca89fe0b 100644 --- a/TestMod/AssemblyInfo.cs +++ b/TestMod/AssemblyInfo.cs @@ -1,25 +1,18 @@ -using System.Resources; -using System.Reflection; -using System.Runtime.InteropServices; +using System.Reflection; using MelonLoader; [assembly: AssemblyTitle(TestMod.BuildInfo.Description)] [assembly: AssemblyDescription(TestMod.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(TestMod.BuildInfo.Company)] [assembly: AssemblyProduct(TestMod.BuildInfo.Name)] [assembly: AssemblyCopyright("Created by " + TestMod.BuildInfo.Author)] [assembly: AssemblyTrademark(TestMod.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] [assembly: AssemblyVersion(TestMod.BuildInfo.Version)] [assembly: AssemblyFileVersion(TestMod.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] -[assembly: MelonModInfo(typeof(TestMod.TestMod), TestMod.BuildInfo.Name, TestMod.BuildInfo.Version, TestMod.BuildInfo.Author, TestMod.BuildInfo.DownloadLink)] +[assembly: MelonInfo(typeof(TestMod.TestMod), TestMod.BuildInfo.Name, TestMod.BuildInfo.Version, TestMod.BuildInfo.Author, TestMod.BuildInfo.DownloadLink)] -// Create and Setup a MelonModGame to mark a Mod as Universal or Compatible with specific Games. -// If no MelonModGameAttribute is found or any of the Values for any MelonModGame on the Mod is null or empty it will be assumed the Mod is Universal. -// Values for MelonModGame can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version. -[assembly: MelonModGame(null, null)] \ No newline at end of file +// Create and Setup a MelonGame to mark a Mod as Universal or Compatible with specific Games. +// If no MelonGameAttribute is found or any of the Values for any MelonGame on the Mod is null or empty it will be assumed the Mod is Universal. +// Values for MelonMame can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version. +[assembly: MelonGame(null, null)] \ No newline at end of file diff --git a/TestMod/Main.cs b/TestMod/Main.cs index 7d8b0a033..ab27ebf5f 100644 --- a/TestMod/Main.cs +++ b/TestMod/Main.cs @@ -16,57 +16,57 @@ public class TestMod : MelonMod { public override void OnApplicationStart() // Runs after Game Initialization. { - MelonModLogger.Log("OnApplicationStart"); + MelonLogger.Log("OnApplicationStart"); } public override void OnLevelIsLoading() // Runs when a Scene is Loading or when a Loading Screen is Shown. Currently only runs if the Mod is used in BONEWORKS. { - MelonModLogger.Log("OnLevelIsLoading"); + MelonLogger.Log("OnLevelIsLoading"); } public override void OnLevelWasLoaded(int level) // Runs when a Scene has Loaded. { - MelonModLogger.Log("OnLevelWasLoaded: " + level.ToString()); + MelonLogger.Log("OnLevelWasLoaded: " + level.ToString()); } public override void OnLevelWasInitialized(int level) // Runs when a Scene has Initialized. { - MelonModLogger.Log("OnLevelWasInitialized: " + level.ToString()); + MelonLogger.Log("OnLevelWasInitialized: " + level.ToString()); } public override void OnUpdate() // Runs once per frame. { - MelonModLogger.Log("OnUpdate"); + MelonLogger.Log("OnUpdate"); } public override void OnFixedUpdate() // Can run multiple times per frame. Mostly used for Physics. { - MelonModLogger.Log("OnFixedUpdate"); + MelonLogger.Log("OnFixedUpdate"); } public override void OnLateUpdate() // Runs once per frame after OnUpdate and OnFixedUpdate have finished. { - MelonModLogger.Log("OnLateUpdate"); + MelonLogger.Log("OnLateUpdate"); } public override void OnGUI() // Can run multiple times per frame. Mostly used for Unity's IMGUI. { - MelonModLogger.Log("OnGUI"); + MelonLogger.Log("OnGUI"); } public override void OnApplicationQuit() // Runs when the Game is told to Close. { - MelonModLogger.Log("OnApplicationQuit"); + MelonLogger.Log("OnApplicationQuit"); } public override void OnModSettingsApplied() // Runs when Mod Preferences get saved to UserData/modprefs.ini. { - MelonModLogger.Log("OnModSettingsApplied"); + MelonLogger.Log("OnModSettingsApplied"); } public override void VRChat_OnUiManagerInit() // Runs upon VRChat's UiManager Initialization. Only runs if the Mod is used in VRChat. { - MelonModLogger.Log("VRChat_OnUiManagerInit"); + MelonLogger.Log("VRChat_OnUiManagerInit"); } } } \ No newline at end of file diff --git a/TestPlugin/AssemblyInfo.cs b/TestPlugin/AssemblyInfo.cs index 4d0d9814d..30282423e 100644 --- a/TestPlugin/AssemblyInfo.cs +++ b/TestPlugin/AssemblyInfo.cs @@ -1,25 +1,18 @@ -using System.Resources; -using System.Reflection; -using System.Runtime.InteropServices; +using System.Reflection; using MelonLoader; [assembly: AssemblyTitle(TestPlugin.BuildInfo.Description)] [assembly: AssemblyDescription(TestPlugin.BuildInfo.Description)] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(TestPlugin.BuildInfo.Company)] [assembly: AssemblyProduct(TestPlugin.BuildInfo.Name)] [assembly: AssemblyCopyright("Created by " + TestPlugin.BuildInfo.Author)] [assembly: AssemblyTrademark(TestPlugin.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] [assembly: AssemblyVersion(TestPlugin.BuildInfo.Version)] [assembly: AssemblyFileVersion(TestPlugin.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] -[assembly: MelonPluginInfo(typeof(TestPlugin.TestPlugin), TestPlugin.BuildInfo.Name, TestPlugin.BuildInfo.Version, TestPlugin.BuildInfo.Author, TestPlugin.BuildInfo.DownloadLink)] +[assembly: MelonInfo(typeof(TestPlugin.TestPlugin), TestPlugin.BuildInfo.Name, TestPlugin.BuildInfo.Version, TestPlugin.BuildInfo.Author, TestPlugin.BuildInfo.DownloadLink)] // Create and Setup a MelonPluginGame to mark a Plugin as Universal or Compatible with specific Games. // If no MelonPluginGameAttribute is found or any of the Values for any MelonPluginGame on the Mod is null or empty it will be assumed the Plugin is Universal. // Values for MelonPluginGame can be found in the Game's app.info file or printed at the top of every log directly beneath the Unity version. -[assembly: MelonPluginGame(null, null)] \ No newline at end of file +[assembly: MelonGame(null, null)] \ No newline at end of file diff --git a/TestPlugin/Main.cs b/TestPlugin/Main.cs index 4bda4a739..8cbc2df49 100644 --- a/TestPlugin/Main.cs +++ b/TestPlugin/Main.cs @@ -16,22 +16,22 @@ public class TestPlugin : MelonPlugin { public override void OnPreInitialization() // Runs before Game Initialization. { - MelonModLogger.Log("OnPreInitialization"); + MelonLogger.Log("OnPreInitialization"); } public override void OnApplicationStart() // Runs after Game Initialization. { - MelonModLogger.Log("OnApplicationStart"); + MelonLogger.Log("OnApplicationStart"); } public override void OnApplicationQuit() // Runs when the Game is told to Close. { - MelonModLogger.Log("OnApplicationQuit"); + MelonLogger.Log("OnApplicationQuit"); } public override void OnModSettingsApplied() // Runs when Mod Preferences get saved to UserData/modprefs.ini. { - MelonModLogger.Log("OnModSettingsApplied"); + MelonLogger.Log("OnModSettingsApplied"); } } } \ No newline at end of file diff --git a/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs b/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs index bd3058814..3dd9153de 100644 --- a/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs +++ b/UnityEngine.Il2CppAssetBundleManager/AssemblyInfo.cs @@ -1,17 +1,12 @@ -using System.Resources; -using System.Reflection; +using System.Reflection; using System.Runtime.InteropServices; [assembly: AssemblyTitle("AssetBundle extension for Il2Cpp Games")] [assembly: AssemblyDescription("AssetBundle extension for Il2Cpp Games")] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany(MelonLoader.BuildInfo.Company)] [assembly: AssemblyProduct("UnityEngine.Il2CppAssetBundleManager")] [assembly: AssemblyCopyright("Created by " + MelonLoader.BuildInfo.Author)] [assembly: AssemblyTrademark(MelonLoader.BuildInfo.Company)] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -//[assembly: Guid("")] +[assembly: Guid("338a5234-8f1d-491a-8fde-a3d27ba00f44")] [assembly: AssemblyVersion(MelonLoader.BuildInfo.Version)] -[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] -[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file +[assembly: AssemblyFileVersion(MelonLoader.BuildInfo.Version)] \ No newline at end of file