using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using HarmonyLib; using JetBrains.Annotations; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using NewSafetyHelp; using NewSafetyHelp.ARG; using NewSafetyHelp.Audio; using NewSafetyHelp.Audio.AudioPatches; using NewSafetyHelp.Audio.Music.Data; using NewSafetyHelp.Audio.Music.Intermission; using NewSafetyHelp.Callers.CallerHelpers; using NewSafetyHelp.Callers.CallerModel; using NewSafetyHelp.Callers.UI.AnimatedEntry; using NewSafetyHelp.CustomCampaignSystem; using NewSafetyHelp.CustomCampaignSystem.Abstract; using NewSafetyHelp.CustomCampaignSystem.CustomCampaignModel; using NewSafetyHelp.CustomCampaignSystem.CustomComputer3DScreen; using NewSafetyHelp.CustomCampaignSystem.CustomRingtone; using NewSafetyHelp.CustomCampaignSystem.CustomTextFiles; using NewSafetyHelp.CustomCampaignSystem.CutsceneLogic; using NewSafetyHelp.CustomCampaignSystem.Helper; using NewSafetyHelp.CustomCampaignSystem.Helper.AccuracyHelpers; using NewSafetyHelp.CustomCampaignSystem.Helper.AccuracyModel; using NewSafetyHelp.CustomCampaignSystem.Helper.CallerRequirementHelper; using NewSafetyHelp.CustomCampaignSystem.LinkApps; using NewSafetyHelp.CustomCampaignSystem.Modifier.Data; using NewSafetyHelp.CustomCampaignSystem.Saving; using NewSafetyHelp.CustomCampaignSystem.Themes; using NewSafetyHelp.CustomCampaignSystem.TimedCaller; using NewSafetyHelp.CustomDesktop; using NewSafetyHelp.CustomDesktop.CustomDoubleClickButton; using NewSafetyHelp.CustomDesktop.Utils; using NewSafetyHelp.CustomThemes; using NewSafetyHelp.CustomVideos; using NewSafetyHelp.Emails; using NewSafetyHelp.EntryManager; using NewSafetyHelp.EntryManager.EntryData; using NewSafetyHelp.EntryManager.EntryUnlocker; using NewSafetyHelp.ErrorDebugging; using NewSafetyHelp.HelperFunctions; using NewSafetyHelp.ImportFiles; using NewSafetyHelp.InGameSettings; using NewSafetyHelp.JSONParsing; using NewSafetyHelp.JSONParsing.CCParsing; using NewSafetyHelp.JSONParsing.EntryParsing; using NewSafetyHelp.JSONParsing.ParsingHelpers; using NewSafetyHelp.LoggingSystem; using NewSafetyHelp.VersionChecker; using Newtonsoft.Json.Linq; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.Profiling; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("NewSafetyHelp")] [assembly: AssemblyDescription("A mod for Home Safety Hotline, which allows adding new features and campaigns.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("NewSafetyHelp")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("9e2a6c17-9bee-4db1-a42f-f7c09a8601d6")] [assembly: AssemblyFileVersion("2.6.0")] [assembly: MelonInfo(typeof(NewSafetyHelpMainClass), "NewSafetyHelp", "2.6.0", "Gasterbuzzer", "https://github.com/Gasterbuzzer/NewSafetyHelp/releases/")] [assembly: MelonAuthorColor(1, 208, 12, 252)] [assembly: MelonGame("Night Signal Entertainment", "Home Safety Hotline")] [assembly: MelonPriority(99)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("2.6.0.0")] namespace NewSafetyHelp { public class NewSafetyHelpMainClass : MelonMod { public override void OnInitializeMelon() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown GlobalPreferences.InitializeMelonPreferences(); Application.logMessageReceived += new LogCallback(UnityLogHook.HandleUnityLog); AsyncVersionChecker.CheckForUpdates(); } public override void OnLateInitializeMelon() { EmbedHelpers.DeleteTempFiles(); if (GlobalPreferences.SkipComputerScene.Value) { SceneManager.LoadScene("MainMenuScene"); } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { LoggingHelper.DebugLog(() => $"Scene {sceneName} with build index {buildIndex} has been loaded!"); MelonPreferences.Save(); } } [HarmonyPatch(typeof(EntryUnlockController), "Awake")] public static class MainClassForMonsterEntries { public static bool ShowUpdateMessage = false; private static bool isInitializedMainOnce = false; public static bool AddedEntriesToCustomCampaign = false; public static MonsterProfile[] CopyMonsterProfiles; private static int monsterProfileSize = 0; public static readonly List CopyTierUnlocks = new List(); public static readonly List CopyXmasTier = new List(); private static MonsterProfile[] copyMonsterProfilesAfterAdding; private static int monsterProfileSizeAfterAdding = 0; private static void Postfix(EntryUnlockController __instance) { if (!CustomCampaignGlobal.InCustomCampaign) { if (AddedEntriesToCustomCampaign) { AddedEntriesToCustomCampaign = false; __instance.allEntries.monsterProfiles = copyMonsterProfilesAfterAdding; } if (isInitializedMainOnce) { LoggingHelper.DebugLog("Custom Entries were already added. Skipping adding them again. (This happens on scene reload)."); return; } CopyMonsterProfiles = __instance.allEntries.monsterProfiles; monsterProfileSize = CopyMonsterProfiles.Length; CopyTierUnlocks.Add(__instance.firstTierUnlocks.monsterProfiles); CopyTierUnlocks.Add(__instance.secondTierUnlocks.monsterProfiles); CopyTierUnlocks.Add(__instance.thirdTierUnlocks.monsterProfiles); CopyTierUnlocks.Add(__instance.fourthTierUnlocks.monsterProfiles); CopyTierUnlocks.Add(__instance.fifthTierUnlocks.monsterProfiles); CopyTierUnlocks.Add(__instance.sixthTierUnlocks.monsterProfiles); CopyXmasTier.Add(__instance.xmastFirstTier.monsterProfiles); CopyXmasTier.Add(__instance.xmasSecondTier.monsterProfiles); CopyXmasTier.Add(__instance.xmasThirdTier.monsterProfiles); CopyXmasTier.Add(__instance.xmasFourthTier.monsterProfiles); StartingJSONParsing(__instance); } else { CustomCampaignInitialization(__instance); } } private static void CustomCampaignInitialization(EntryUnlockController __instance) { if (AddedEntriesToCustomCampaign) { return; } if (CopyMonsterProfiles.Length == 0 || monsterProfileSize <= 0) { LoggingHelper.CriticalErrorLog("Loading of old values to add the entries to failed! (Count == 0)"); return; } CustomCampaign activeCustomCampaign = CustomCampaignGlobal.GetActiveCustomCampaign(); if (activeCustomCampaign != null) { if (activeCustomCampaign.RemoveExistingEntries) { __instance.allEntries.monsterProfiles = Array.Empty(); } else if (activeCustomCampaign.UseDLCEntries) { LoggingHelper.DebugLog("Using DLC monster profiles."); __instance.allEntries.monsterProfiles = __instance.allXmasEntries.monsterProfiles; } else { __instance.allEntries.monsterProfiles = CopyMonsterProfiles; } LoggingHelper.InfoLog("Entries are now being added... (Custom Campaign)", LoggingHelper.LoggingCategory.NONE, ConsoleColor.Green); CustomCampaignGlobal.ReplaceAllProvidedCampaignEntries(ref __instance.allEntries); CustomCampaignGlobal.AddAllCustomCampaignEntriesToArray(ref __instance.allEntries); AddedEntriesToCustomCampaign = true; LoggingHelper.InfoLog("Added/Modified all custom entries successfully! (Custom Campaign)", LoggingHelper.LoggingCategory.NONE, ConsoleColor.Green); } } public static void StartingJSONParsing(EntryUnlockController __instance) { LoggingHelper.InfoLog("Now parsing all '.json' files...", LoggingHelper.LoggingCategory.NONE, ConsoleColor.Green); ParseJSONFiles.LoadAllJSON(__instance); copyMonsterProfilesAfterAdding = __instance.allEntries.monsterProfiles; monsterProfileSizeAfterAdding = copyMonsterProfilesAfterAdding.Length; isInitializedMainOnce = true; LoggingHelper.InfoLog("Loaded all '.json' files successfully!", LoggingHelper.LoggingCategory.NONE, ConsoleColor.Green); } } } namespace NewSafetyHelp.VersionChecker { public static class AsyncVersionChecker { private static Version GetCurrentVersion() { return Assembly.GetExecutingAssembly().GetName().Version; } private static bool IsOutDatedVersion(Version currentVersion, Version newVersion) { LoggingHelper.DebugLog($"Checking for outdated version with current version '{currentVersion}' and the new version '{newVersion}'. " + $"(Equal?: {currentVersion == newVersion}) " + $"(Newer available? {currentVersion < newVersion}) " + $"(Current version newer?: {currentVersion > newVersion})"); return currentVersion < newVersion; } [CanBeNull] private static Version ParseVersionTag(string tag) { if (string.IsNullOrEmpty(tag) || string.IsNullOrWhiteSpace(tag) || tag == "NO_VERSION_FOUND") { return null; } tag = tag.Trim(); if (tag.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { tag = tag.Substring(1); } Version.TryParse(tag, out Version result); return result; } private static async Task GetLatestReleaseVersionAsync() { HttpClient http = new HttpClient(); try { http.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("NewSafetyHelp-Mod", GetCurrentVersion().ToString())); http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json")); HttpResponseMessage resp = await http.GetAsync("https://api.github.com/repos/Gasterbuzzer/NewSafetyHelp/releases/latest"); if (!resp.IsSuccessStatusCode) { LoggingHelper.WarningLog($"GitHub API returned error code {resp.StatusCode}. " + "Could not check for updates."); return null; } JObject jsonObjectParsed = JObject.Parse(await resp.Content.ReadAsStringAsync()); string tag = "NO_VERSION_FOUND"; if (jsonObjectParsed["tag_name"] != null) { tag = (string)jsonObjectParsed["tag_name"]; } else if (jsonObjectParsed["name"] != null) { tag = (string)jsonObjectParsed["name"]; } Version parsedVersion = ParseVersionTag(tag); if (parsedVersion == null) { LoggingHelper.WarningLog("Was unable of parsing version number. Failed check."); return null; } return parsedVersion; } finally { ((IDisposable)http)?.Dispose(); } } public static void ShowUpdateMessage() { string text = "NEW VERSION AVAILABLE! Download through your preferred method."; MelonLogger.Msg(ConsoleColor.White, "------------------------"); MelonLogger.Msg(ConsoleColor.Blue, "------------------------"); MelonLogger.Msg(ConsoleColor.Yellow, "------------------------"); MelonLogger.Msg(ConsoleColor.Magenta, "\n\n\n"); MelonLogger.Msg(ConsoleColor.Magenta, text); MelonLogger.Msg(ConsoleColor.Red, text); MelonLogger.Msg(ConsoleColor.Magenta, text); MelonLogger.Msg(ConsoleColor.Magenta, "\n\n\n"); MelonLogger.Msg(ConsoleColor.Yellow, "------------------------"); MelonLogger.Msg(ConsoleColor.Blue, "------------------------"); MelonLogger.Msg(ConsoleColor.White, "------------------------"); } public static async Task CheckForUpdates() { Version newestVersion = await GetLatestReleaseVersionAsync(); if (newestVersion == null) { LoggingHelper.WarningLog("Unable of checking if there is a new version available. Check your internet connection."); } else if (IsOutDatedVersion(GetCurrentVersion(), newestVersion)) { MainClassForMonsterEntries.ShowUpdateMessage = true; ShowUpdateMessage(); } } } } namespace NewSafetyHelp.MainGameBugFixes { public static class CloseErrorPopupFix { [HarmonyPatch(typeof(GenericErrorPopupBehavior), "ExitButton")] public static class ExitButtonFix { private static bool Prefix(GenericErrorPopupBehavior __instance) { __instance.ConfirmButton(); return false; } } [HarmonyPatch(typeof(GenericErrorPopupBehavior), "OnEnable")] public static class OnEnableFix { private static void Prefix(GenericErrorPopupBehavior __instance) { GameObject gameObject = ((Component)((Component)__instance).transform.Find("WindowsBar").Find("CloseButton")).gameObject; if ((Object)(object)gameObject != (Object)null && gameObject.GetComponents