using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WKDebugAccess")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+892f606ebe41243b9ce1bd5bc88cc2a227080e65")] [assembly: AssemblyProduct("WKDebugAccess")] [assembly: AssemblyTitle("WKDebugAccess")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace WKDebugAccess { [BepInPlugin("boiled.whiteknuckle.debugaccess", "WK Debug Access", "1.0.3")] public sealed class WKDebugAccessPlugin : BaseUnityPlugin { private sealed class WKDebugAccessRunner : MonoBehaviour { private void Update() { Heartbeat("runner update"); } } private struct ScoreSnapshot { public int Count; public float Bonus; public float Multiplier; public float Total; } private const string PluginGuid = "boiled.whiteknuckle.debugaccess"; private const string Version = "1.0.3"; private const string Prefix = "wkdebug_"; private const string DebugScoreId = "wkdebug-score"; private static ManualLogSource logSource; private static Harmony harmony; private static GameObject runnerObject; private static bool registered; private static bool hooksPatched; private static float nextRetryLogTime; private static float nextStatusWriteTime; private static DateTime lastCommandWriteTimeUtc; private static readonly List recentMessages = new List(); private void Awake() { logSource = ((BaseUnityPlugin)this).Logger; ((BaseUnityPlugin)this).Logger.LogInfo((object)"WK Debug Access loaded. Waiting for CommandConsole."); PatchConsoleHooks(); SceneManager.sceneLoaded += OnSceneLoaded; EnsureRunner(); EnsureCommandFiles(); TryRegister("plugin awake"); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; ManualLogSource obj = logSource; if (obj != null) { obj.LogInfo((object)"WK Debug Access plugin host destroyed; persistent runner and Harmony patches remain active."); } } private void Update() { Heartbeat("plugin update"); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { TryRegister("scene loaded: " + ((Scene)(ref scene)).name); } private static void PatchConsoleHooks() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown if (hooksPatched) { return; } try { harmony = new Harmony("boiled.whiteknuckle.debugaccess"); MethodInfo methodInfo = AccessTools.Method(typeof(CommandConsole), "Awake", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(CommandConsole), "ExecuteCommand", new Type[2] { typeof(string), typeof(bool) }, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(CommandConsole), "ExecuteCommand", new Type[3] { typeof(string), typeof(bool), typeof(bool) }, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(WKDebugAccessPlugin), "CommandConsoleAwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(WKDebugAccessPlugin), "CommandConsoleExecuteCommandTwoArgsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(WKDebugAccessPlugin), "CommandConsoleExecuteCommandThreeArgsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); hooksPatched = true; ManualLogSource obj = logSource; if (obj != null) { obj.LogInfo((object)("Patched CommandConsole hooks. Awake=" + (methodInfo != null) + ", ExecuteCommand2=" + (methodInfo2 != null) + ", ExecuteCommand3=" + (methodInfo3 != null))); } } catch (Exception ex) { ManualLogSource obj2 = logSource; if (obj2 != null) { obj2.LogError((object)("Failed to patch CommandConsole hooks: " + ex)); } } } private static void EnsureRunner() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)runnerObject != (Object)null)) { runnerObject = new GameObject("WKDebugAccessRunner"); Object.DontDestroyOnLoad((Object)(object)runnerObject); ((Object)runnerObject).hideFlags = (HideFlags)61; runnerObject.AddComponent(); ManualLogSource obj = logSource; if (obj != null) { obj.LogInfo((object)"Persistent WKDebugAccessRunner created."); } } } private static void Heartbeat(string reason) { TryRegister(reason); ProcessCommandFile(); CheckHotkeys(); WriteStatusThrottled(); } private static void TryRegister(string reason) { if (registered) { return; } CommandConsole val = FindConsole(); if ((Object)(object)val == (Object)null) { if (Time.unscaledTime >= nextRetryLogTime) { nextRetryLogTime = Time.unscaledTime + 10f; ManualLogSource obj = logSource; if (obj != null) { obj.LogInfo((object)("CommandConsole not available yet; retrying. Source=" + reason + ", found=" + CountConsoleObjects())); } } return; } try { if ((Object)(object)CommandConsole.instance == (Object)null) { CommandConsole.instance = val; } RegisterCommands(val); registered = true; ManualLogSource obj2 = logSource; if (obj2 != null) { obj2.LogInfo((object)("WK Debug Access commands registered. Source=" + reason)); } } catch (Exception ex) { ManualLogSource obj3 = logSource; if (obj3 != null) { obj3.LogError((object)("Failed to register WK Debug Access commands from " + reason + ": " + ex)); } } } private static CommandConsole FindConsole() { if ((Object)(object)CommandConsole.instance != (Object)null) { return CommandConsole.instance; } try { CommandConsole[] array = Resources.FindObjectsOfTypeAll(); if (array == null || array.Length == 0) { return null; } CommandConsole val = ((IEnumerable)array).FirstOrDefault((Func)((CommandConsole c) => (Object)(object)c != (Object)null && (Object)(object)c.inputField != (Object)null)); return (CommandConsole)(((Object)(object)val != (Object)null) ? ((object)val) : ((object)((IEnumerable)array).FirstOrDefault((Func)((CommandConsole c) => (Object)(object)c != (Object)null)))); } catch (Exception ex) { ManualLogSource obj = logSource; if (obj != null) { obj.LogWarning((object)("FindConsole failed: " + ex.Message)); } return null; } } private static int CountConsoleObjects() { try { CommandConsole[] array = Resources.FindObjectsOfTypeAll(); return (array != null) ? array.Length : 0; } catch { return -1; } } private static void RegisterCommands(CommandConsole console) { SafeCommandRaw(console, "wkdebug", RootCommand, "[command] WK Debug Access root command. Try: wkdebug help"); SafeCommandRaw(console, "wkhascheated", HasCheated, "Print CommandConsole.hasCheated."); SafeCommandRaw(console, "wksethascheated", SetHasCheated, "[true|false] Set CommandConsole.hasCheated."); SafeCommandRaw(console, "wksessioninfo", SessionInfo, "Print current gamemode/session summary."); SafeCommandRaw(console, "wkliststats", ListStats, "[filter] [limit=80] List StatManager.sessionStats entries."); SafeCommandRaw(console, "wkgetstat", GetStat, "[id] Print one session stat."); SafeCommandRaw(console, "wksetstat", SetStat, "[id] [value] [int|float|string optional] Set or create a session stat."); SafeCommandRaw(console, "wkaddstat", AddStat, "[id] [delta] [int|float optional] Add to a session stat."); SafeCommandRaw(console, "wkexportstats", ExportStats, "[filter optional] Export session stats to BepInEx/config/WKDebugAccess/session_stats.tsv."); SafeCommandRaw(console, "wksavesession", SaveSession, "[resetroom=true|false optional] Force SaveSession to campaign-save.session."); SafeCommandRaw(console, "wkgettime", GetTime, "Print current runtime/session/best time."); SafeCommandRaw(console, "wksettime", SetTime, "[seconds|mm:ss|hh:mm:ss] Set current run time."); SafeCommandRaw(console, "wkgetscore", GetScore, "Print runtime score entries and saved score stats."); SafeCommandRaw(console, "wklistscores", ListScores, "[filter] [limit=80] List CL_ScoreManager session score entries."); SafeCommandRaw(console, "wksetscore", SetScore, "[value] Set session score stat and wkdebug score entry."); SafeCommandRaw(console, "wkaddscore", AddScore, "[id] [bonus] [multiplier=0] [count=0] [title optional] Add/update runtime score entry."); SafeCommandRaw(console, "wkgethighscore", GetHighScore, "Print global and gamemode high score."); SafeCommandRaw(console, "wksethighscore", SetHighScore, "[value] Set global and gamemode high score."); SafeCommandRaw(console, "wkresetleaderboard", ResetLeaderboard, "[score=0] [steam leaderboard name optional] Reset local leaderboard/high score and optionally force-update Steam score."); SafeCommandRaw(console, "wkresetlb", ResetLeaderboard, "[score=0] [steam leaderboard name optional] Alias for wkresetleaderboard."); SafeCommandRaw(console, "wksetbesttime", SetBestTime, "[seconds|mm:ss|hh:mm:ss] Set global and gamemode best time."); SafeCommandRaw(console, "wkmarkwin", MarkWin, "[score optional] [time optional] Save win stats without opening end screen."); SafeCommandRaw(console, "wkcompletegame", CompleteGame, "[score optional] [time optional] Set score/time/win stats and save."); SafeCommandRaw(console, "wkfinishgame", FinishGame, "[win|lose] Start the game's end sequence."); SafeCommandRaw(console, "wkgetflag", GetFlag, "[id] Print a session/save flag."); SafeCommandRaw(console, "wksetflag", SetFlag, "[id] [true|false] [data optional] [save=true] Set a game flag."); SafeCommandRaw(console, "wklistachievements", ListAchievements, "[filter] [limit=80] List loaded achievements."); SafeCommandRaw(console, "wksetachievement", SetAchievement, "[id] [true|false] [save=true] Set an achievement through the game API."); SafeCommandRaw(console, "wksavestats", SaveStatsCommand, "[writeall=true] Save StatManager data."); SafeCommand(console, "help", Help, "List WK Debug Access commands."); SafeCommand(console, "hascheated", HasCheated, "Print CommandConsole.hasCheated."); SafeCommand(console, "sethascheated", SetHasCheated, "[true|false] Set CommandConsole.hasCheated without marking this command as a cheat."); SafeCommand(console, "sessioninfo", SessionInfo, "Print current gamemode/session summary."); SafeCommand(console, "liststats", ListStats, "[filter] [limit=80] List StatManager.sessionStats entries."); SafeCommand(console, "getstat", GetStat, "[id] Print one session stat."); SafeCommand(console, "setstat", SetStat, "[id] [value] [int|float|string optional] Set or create a session stat."); SafeCommand(console, "addstat", AddStat, "[id] [delta] [int|float optional] Add to a session stat."); SafeCommand(console, "exportstats", ExportStats, "[filter optional] Export session stats to BepInEx/config/WKDebugAccess/session_stats.tsv."); SafeCommand(console, "savesession", SaveSession, "[resetroom=true|false optional] Force SaveSession to campaign-save.session."); SafeCommand(console, "gettime", GetTime, "Print current runtime/session/best time."); SafeCommand(console, "settime", SetTime, "[seconds|mm:ss|hh:mm:ss] Set current run time."); SafeCommand(console, "getscore", GetScore, "Print runtime score entries and saved score stats."); SafeCommand(console, "listscores", ListScores, "[filter] [limit=80] List CL_ScoreManager session score entries."); SafeCommand(console, "setscore", SetScore, "[value] Set session score stat and wkdebug score entry."); SafeCommand(console, "addscore", AddScore, "[id] [bonus] [multiplier=0] [count=0] [title optional] Add/update runtime score entry."); SafeCommand(console, "gethighscore", GetHighScore, "Print global and gamemode high score."); SafeCommand(console, "sethighscore", SetHighScore, "[value] Set global and gamemode high score."); SafeCommand(console, "resetleaderboard", ResetLeaderboard, "[score=0] [steam leaderboard name optional] Reset local leaderboard/high score and optionally force-update Steam score."); SafeCommand(console, "resetlb", ResetLeaderboard, "[score=0] [steam leaderboard name optional] Alias for resetleaderboard."); SafeCommand(console, "setbesttime", SetBestTime, "[seconds|mm:ss|hh:mm:ss] Set global and gamemode best time."); SafeCommand(console, "markwin", MarkWin, "[score optional] [time optional] Save win stats without opening end screen."); SafeCommand(console, "completegame", CompleteGame, "[score optional] [time optional] Set score/time/win stats and save."); SafeCommand(console, "finishgame", FinishGame, "[win|lose] Start the game's end sequence."); SafeCommand(console, "getflag", GetFlag, "[id] Print a session/save flag."); SafeCommand(console, "setflag", SetFlag, "[id] [true|false] [data optional] [save=true] Set a game flag."); SafeCommand(console, "listachievements", ListAchievements, "[filter] [limit=80] List loaded achievements."); SafeCommand(console, "setachievement", SetAchievement, "[id] [true|false] [save=true] Set an achievement through the game API."); SafeCommand(console, "savestats", SaveStatsCommand, "[writeall=true] Save StatManager data."); } private static void SafeCommand(CommandConsole console, string name, Action callback, string description) { SafeCommandRaw(console, "wkdebug_" + name, callback, description); } private static void SafeCommandRaw(CommandConsole console, string name, Action callback, string description) { object obj = AccessTools.Method(typeof(CommandConsole), "RegisterCommand", new Type[3] { typeof(string), typeof(Action), typeof(bool) }, (Type[])null).Invoke(console, new object[3] { name, callback, false }); if (obj == null) { ManualLogSource obj2 = logSource; if (obj2 != null) { obj2.LogWarning((object)("RegisterCommand returned null for " + name)); } } else { Type type = obj.GetType(); type.GetField("cheat", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(obj, false); type.GetField("description", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(obj, description); } } private static void RootCommand(string[] args) { if (args.Length == 0) { Help(args); } else { RunSubcommand(args[0], args.Skip(1).ToArray()); } } private static void Help(string[] args) { Log("Commands:"); Log(" wkdebug help"); Log(" wkdebug hascheated"); Log(" wkdebug sethascheated false"); Log(" wkdebug liststats [filter] [limit]"); Log(" wkdebug getstat bank-roaches"); Log(" wkdebug setstat bank-roaches 999 int"); Log(" wkdebug gettime"); Log(" wkdebug settime 12:34"); Log(" wkdebug getscore"); Log(" wkdebug setscore 50000"); Log(" wkdebug addscore debug-bonus 50000 0 1 Debug Score"); Log(" wkdebug gethighscore"); Log(" wkdebug sethighscore 50000"); Log(" wkdebug resetleaderboard [score=0] [steam leaderboard name optional]"); Log(" wkdebug setbesttime 12:34"); Log(" wkdebug markwin [score] [time]"); Log(" wkdebug completegame [score] [time]"); Log(" wkdebug finishgame win|lose"); Log(" wkdebug getflag flag-id"); Log(" wkdebug setflag flag-id true data true"); Log(" wkdebug listachievements [filter] [limit]"); Log(" wkdebug setachievement ACH_ID true"); Log(" wkdebug savestats [writeall]"); Log(" wkdebug savesession [resetroom]"); Log(" wkdebug_hascheated"); Log(" wkdebug_sethascheated false"); Log(" wkdebug_sessioninfo"); Log(" wkdebug_liststats [filter] [limit]"); Log(" wkdebug_getstat bank-roaches"); Log(" wkdebug_setstat bank-roaches 999 int"); Log(" wkdebug_addstat bank-roaches 10 int"); Log(" wkdebug_exportstats [filter]"); Log(" wkdebug_savesession [resetroom]"); } private static void CommandConsoleAwakePostfix() { TryRegister("CommandConsole.Awake postfix"); } private static bool CommandConsoleExecuteCommandTwoArgsPrefix(string input) { return HandleExecuteCommandPrefix(input, "ExecuteCommand2"); } private static bool CommandConsoleExecuteCommandThreeArgsPrefix(string input) { return HandleExecuteCommandPrefix(input, "ExecuteCommand3"); } private static bool HandleExecuteCommandPrefix(string input, string source) { if (!string.IsNullOrWhiteSpace(input) && input.TrimStart().StartsWith("wk", StringComparison.OrdinalIgnoreCase)) { ManualLogSource obj = logSource; if (obj != null) { obj.LogInfo((object)("Intercept candidate from " + source + ": " + input)); } } return !TryHandleRawCommand(input); } private static bool TryHandleRawCommand(string input) { if (string.IsNullOrWhiteSpace(input)) { return false; } string text = input.Trim(); string[] array = SplitCommandLine(text); if (array.Length == 0) { return false; } string text2 = array[0].ToLowerInvariant(); string text3 = null; string[] array2 = Array.Empty(); if (text2 == "wkdebug") { text3 = ((array.Length > 1) ? array[1] : "help"); array2 = array.Skip(2).ToArray(); } else if (text2.StartsWith("wkdebug_", StringComparison.Ordinal)) { text3 = text2.Substring("wkdebug_".Length); array2 = array.Skip(1).ToArray(); } else { if (!text2.StartsWith("wk", StringComparison.Ordinal)) { return false; } text3 = text2.Substring("wk".Length); array2 = array.Skip(1).ToArray(); } ManualLogSource obj = logSource; if (obj != null) { obj.LogInfo((object)("Handling WK Debug Access raw command: " + text)); } RunSubcommand(text3, array2); return true; } private static void EnsureCommandFiles() { try { string configDir = GetConfigDir(); Directory.CreateDirectory(configDir); string path = Path.Combine(configDir, "command.txt"); if (!File.Exists(path)) { File.WriteAllText(path, "# Write one command, save the file, and the mod will run it.\n# Examples:\n# sethascheated false\n# getscore\n# settime 12:34\n# setscore 50000\n# sethighscore 50000\n# resetleaderboard\n# resetleaderboard 0 \"Steam Leaderboard Name\"\n# markwin 50000 12:34\n# finishgame win\n# exportstats\n", Encoding.UTF8); } WriteStatus(force: true); } catch (Exception ex) { ManualLogSource obj = logSource; if (obj != null) { obj.LogWarning((object)("Could not create command files: " + ex.Message)); } } } private static void ProcessCommandFile() { try { string path = Path.Combine(GetConfigDir(), "command.txt"); if (!File.Exists(path)) { EnsureCommandFiles(); return; } DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(path); if (!(lastWriteTimeUtc <= lastCommandWriteTimeUtc)) { lastCommandWriteTimeUtc = lastWriteTimeUtc; string text = (from l in File.ReadAllLines(path) select l.Trim()).FirstOrDefault((string l) => l.Length > 0 && !l.StartsWith("#", StringComparison.Ordinal)); if (!string.IsNullOrEmpty(text)) { Log("Running command file command: " + text); RunFileCommand(text); WriteStatus(force: true); } } } catch (Exception ex) { Log("Command file error: " + ex.Message); } } private static void RunFileCommand(string command) { if (!TryHandleRawCommand(command)) { string[] array = SplitCommandLine(command.Trim()); if (array.Length != 0) { RunSubcommand(array[0], array.Skip(1).ToArray()); } } } private static void CheckHotkeys() { try { if (Input.GetKeyDown((KeyCode)287)) { SetHasCheated(new string[1] { "false" }); WriteStatus(force: true); } if (Input.GetKeyDown((KeyCode)288)) { ExportStats(Array.Empty()); WriteStatus(force: true); } if (Input.GetKeyDown((KeyCode)289)) { SaveSession(Array.Empty()); WriteStatus(force: true); } } catch { } } private static void WriteStatusThrottled() { if (!(Time.unscaledTime < nextStatusWriteTime)) { nextStatusWriteTime = Time.unscaledTime + 2f; WriteStatus(force: false); } } private static void WriteStatus(bool force) { try { string configDir = GetConfigDir(); Directory.CreateDirectory(configDir); string path = Path.Combine(configDir, "live_status.txt"); GameStats sessionStats = GetSessionStats(); ScoreSnapshot scoreSnapshot = GetScoreSnapshot(); float? currentGameTime = GetCurrentGameTime(); float? statFloat = GetStatFloat(sessionStats, "score"); float? statFloat2 = GetStatFloat(sessionStats, "game-time"); float? statFloat3 = GetStatFloat(GetGlobalStats(), "score"); float? statFloat4 = GetStatFloat(GetGamemodeStats(), "score"); float? statFloat5 = GetStatFloat(GetGlobalStats(), "best-time"); float? statFloat6 = GetStatFloat(GetGamemodeStats(), "best-time"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("WKDebugAccess 1.0.3"); stringBuilder.AppendLine("updated=" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); stringBuilder.AppendLine("commandsRegistered=" + registered); stringBuilder.AppendLine("commandConsoleObjects=" + CountConsoleObjects()); stringBuilder.AppendLine("hasCheated=" + CommandConsole.hasCheated); stringBuilder.AppendLine("gamemode=" + SafeGamemodeName()); stringBuilder.AppendLine("runHasEnded=" + CL_GameManager.runHasEnded); stringBuilder.AppendLine("runtimeGameTime=" + FormatNullableSeconds(currentGameTime)); stringBuilder.AppendLine("sessionGameTime=" + FormatNullableSeconds(statFloat2)); stringBuilder.AppendLine("sessionScoreStat=" + FormatNullableFloat(statFloat)); stringBuilder.AppendLine("sessionScoreEntries=" + scoreSnapshot.Count); stringBuilder.AppendLine("sessionScoreBonus=" + FormatFloat(scoreSnapshot.Bonus)); stringBuilder.AppendLine("sessionScoreMultiplier=" + FormatFloat(scoreSnapshot.Multiplier)); stringBuilder.AppendLine("sessionScoreTotal=" + FormatFloat(scoreSnapshot.Total)); stringBuilder.AppendLine("globalHighScore=" + FormatNullableFloat(statFloat3)); stringBuilder.AppendLine("gamemodeHighScore=" + FormatNullableFloat(statFloat4)); stringBuilder.AppendLine("globalBestTime=" + FormatNullableSeconds(statFloat5)); stringBuilder.AppendLine("gamemodeBestTime=" + FormatNullableSeconds(statFloat6)); stringBuilder.AppendLine("sessionStatsCount=" + (sessionStats?.statistics?.Count).GetValueOrDefault()); stringBuilder.AppendLine("configDir=" + configDir); stringBuilder.AppendLine("hotkeys=F6 sethascheated false, F7 exportstats, F8 savesession"); stringBuilder.AppendLine("commandFile=" + Path.Combine(configDir, "command.txt")); stringBuilder.AppendLine("lastMessages="); foreach (string item in recentMessages.Take(20)) { stringBuilder.AppendLine(item); } File.WriteAllText(path, stringBuilder.ToString(), Encoding.UTF8); } catch (Exception ex) { if (force) { ManualLogSource obj = logSource; if (obj != null) { obj.LogWarning((object)("Could not write live_status.txt: " + ex.Message)); } } } } private static void RunSubcommand(string subcommand, string[] args) { switch ((subcommand ?? "help").ToLowerInvariant()) { case "help": Help(args); break; case "hascheated": HasCheated(args); break; case "sethascheated": SetHasCheated(args); break; case "sessioninfo": SessionInfo(args); break; case "liststats": ListStats(args); break; case "getstat": GetStat(args); break; case "setstat": SetStat(args); break; case "addstat": AddStat(args); break; case "exportstats": ExportStats(args); break; case "savesession": SaveSession(args); break; case "time": case "gettime": GetTime(args); break; case "settime": SetTime(args); break; case "getscore": case "score": GetScore(args); break; case "listscores": ListScores(args); break; case "setscore": SetScore(args); break; case "addscore": AddScore(args); break; case "highscore": if (args.Length == 0) { GetHighScore(args); } else { SetHighScore(args); } break; case "gethighscore": GetHighScore(args); break; case "sethighscore": SetHighScore(args); break; case "resetlb": case "resetleaderboard": ResetLeaderboard(args); break; case "besttime": if (args.Length == 0) { GetTime(args); } else { SetBestTime(args); } break; case "setbesttime": SetBestTime(args); break; case "markwin": MarkWin(args); break; case "completegame": CompleteGame(args); break; case "finishgame": case "finish": FinishGame(args); break; case "getflag": GetFlag(args); break; case "setflag": SetFlag(args); break; case "achievements": case "listachievements": ListAchievements(args); break; case "setachievement": SetAchievement(args); break; case "savestats": case "saveprogress": SaveStatsCommand(args); break; default: Log("Unknown WK Debug Access subcommand: " + subcommand); Help(args); break; } } private static void HasCheated(string[] args) { Log("CommandConsole.hasCheated = " + CommandConsole.hasCheated); } private static void SetHasCheated(string[] args) { if (args.Length < 1 || !bool.TryParse(args[0], out var result)) { Log("Usage: wkdebug_sethascheated true|false"); return; } CommandConsole.hasCheated = result; TrySetCheatTracker(result); Log("CommandConsole.hasCheated = " + CommandConsole.hasCheated); } private static void SessionInfo(string[] args) { string text = (((Object)(object)CL_GameManager.gamemode != (Object)null) ? CL_GameManager.GetGamemodeName(true, false) : "null"); int valueOrDefault = (GetSessionStats()?.statistics?.Count).GetValueOrDefault(); Log("Gamemode: " + text); Log("hasCheated: " + CommandConsole.hasCheated); Log("isSavedRun: " + CL_SaveManager.IsInSavedRun()); Log("sessionStats count: " + valueOrDefault); Log("roaches: " + CL_GameManager.GetRoaches(false)); Log("game-time: " + FormatNullableSeconds(GetCurrentGameTime())); Log("score: " + FormatNullableFloat(GetStatFloat(GetSessionStats(), "score")) + ", runtimeTotal=" + FormatFloat(GetScoreSnapshot().Total)); Log("high score: global=" + FormatNullableFloat(GetStatFloat(GetGlobalStats(), "score")) + ", gamemode=" + FormatNullableFloat(GetStatFloat(GetGamemodeStats(), "score"))); } private static void ListStats(string[] args) { GameStats sessionStats = GetSessionStats(); if (sessionStats == null) { Log("StatManager.sessionStats is null."); return; } string filter = ((args.Length != 0) ? args[0] : ""); int result = 80; if (args.Length > 1) { int.TryParse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out result); if (result <= 0) { result = 80; } } List list = sessionStats.statistics.Where((Statistic s) => s != null && (string.IsNullOrEmpty(filter) || s.id.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)).Take(result).ToList(); Log("sessionStats matches: " + list.Count + " / total " + sessionStats.statistics.Count); foreach (Statistic item in list) { Log(FormatStat(item)); } } private static void GetStat(string[] args) { if (args.Length < 1) { Log("Usage: wkdebug_getstat "); return; } Statistic val = FindSessionStat(args[0]); if (val == null) { Log("Session stat not found: " + args[0]); } else { Log(FormatStat(val)); } } private unsafe static void SetStat(string[] args) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if (args.Length < 2) { Log("Usage: wkdebug_setstat [int|float|string]"); return; } GameStats sessionStats = GetSessionStats(); if (sessionStats == null) { Log("StatManager.sessionStats is null."); return; } string text = args[0]; string text2 = args[1]; Statistic val = FindSessionStat(text); DataType val2 = ((args.Length > 2) ? ParseType(args[2]) : (val?.type ?? InferType(text2))); if (!TryParseValue(text2, val2, out var parsed)) { Log("Could not parse '" + text2 + "' as " + ((object)(*(DataType*)(&val2))/*cast due to .constrained prefix*/).ToString() + "."); } else { sessionStats.SetStatistic(text, parsed, val2); Log("Set " + FormatStat(FindSessionStat(text))); } } private unsafe static void AddStat(string[] args) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (args.Length < 2) { Log("Usage: wkdebug_addstat [int|float]"); return; } GameStats sessionStats = GetSessionStats(); if (sessionStats == null) { Log("StatManager.sessionStats is null."); return; } string text = args[0]; string text2 = args[1]; Statistic val = FindSessionStat(text); DataType val2 = ((args.Length > 2) ? ParseType(args[2]) : (val?.type ?? InferType(text2))); object parsed; if ((int)val2 == 1) { Log("wkdebug_addstat only supports int/float."); } else if (!TryParseValue(text2, val2, out parsed)) { Log("Could not parse '" + text2 + "' as " + ((object)(*(DataType*)(&val2))/*cast due to .constrained prefix*/).ToString() + "."); } else { sessionStats.UpdateStatistic(text, parsed, val2, (ModType)1, (DisplayType)0, (ModType)1); Log("Added. " + FormatStat(FindSessionStat(text))); } } private static void ExportStats(string[] args) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) GameStats sessionStats = GetSessionStats(); if (sessionStats == null) { Log("StatManager.sessionStats is null."); return; } string value = ((args.Length != 0) ? args[0] : ""); string text = Path.Combine(Paths.ConfigPath, "WKDebugAccess"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, "session_stats.tsv"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("id\tvalue\ttype\tdefaultModType\tdisplayType"); foreach (Statistic statistic in sessionStats.statistics) { if (statistic != null && (string.IsNullOrEmpty(value) || statistic.id.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)) { stringBuilder.Append(statistic.id).Append('\t').Append(statistic.value) .Append('\t') .Append(statistic.type) .Append('\t') .Append(statistic.defaultModType) .Append('\t') .Append(statistic.displayType) .AppendLine(); } } File.WriteAllText(text2, stringBuilder.ToString(), Encoding.UTF8); Log("Exported session stats to: " + text2); } private static void SaveSession(string[] args) { if ((Object)(object)CL_SaveManager.instance == (Object)null) { Log("CL_SaveManager.instance is null; cannot save session here."); return; } bool result = false; if (args.Length != 0) { bool.TryParse(args[0], out result); } CL_SaveManager.instance.SaveSession(false, true, false, "", result); Log("SaveSession wrote current run. resetRoom=" + result + ", hasCheated=" + CommandConsole.hasCheated); } private static void GetTime(string[] args) { float? currentGameTime = GetCurrentGameTime(); float? statFloat = GetStatFloat(GetSessionStats(), "game-time"); float? statFloat2 = GetStatFloat(GetGlobalStats(), "best-time"); float? statFloat3 = GetStatFloat(GetGamemodeStats(), "best-time"); Log("runtime game-time = " + FormatNullableSeconds(currentGameTime)); Log("session stat game-time = " + FormatNullableSeconds(statFloat)); Log("global best-time = " + FormatNullableSeconds(statFloat2)); Log("gamemode best-time = " + FormatNullableSeconds(statFloat3)); } private static void SetTime(string[] args) { if (args.Length < 1 || !TryParseTime(args[0], out var seconds)) { Log("Usage: wkdebug_settime "); } else if (SetCurrentGameTime(seconds)) { Log("Current run game-time set to " + FormatSeconds(seconds) + "."); } } private static void GetScore(string[] args) { ScoreSnapshot scoreSnapshot = GetScoreSnapshot(); Log("runtime score entries = " + scoreSnapshot.Count); Log("runtime score bonus = " + FormatFloat(scoreSnapshot.Bonus)); Log("runtime score multiplier = " + FormatFloat(scoreSnapshot.Multiplier)); Log("runtime score total = " + FormatFloat(scoreSnapshot.Total)); Log("session stat score = " + FormatNullableFloat(GetStatFloat(GetSessionStats(), "score"))); GetHighScore(args); } private static void ListScores(string[] args) { SessionScore sessionScore = CL_ScoreManager.sessionScore; if (sessionScore == null || sessionScore.scores == null) { Log("CL_ScoreManager.sessionScore is null or empty."); return; } string filter = ((args.Length != 0) ? args[0] : ""); int count = ParseOptionalInt(args, 1, 80); List list = sessionScore.scores.Where((Score s) => s != null && (string.IsNullOrEmpty(filter) || s.id.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 || (s.title ?? "").IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)).Take(count).ToList(); Log("score entries matches: " + list.Count + " / total " + sessionScore.scores.Count); foreach (Score item in list) { Log(FormatScoreEntry(item)); } } private static void SetScore(string[] args) { if (args.Length < 1 || !TryParseFloat(args[0], out var value)) { Log("Usage: wkdebug_setscore "); } else if (SetRuntimeScoreTarget(value)) { SetSessionScoreStat(value); ScoreSnapshot scoreSnapshot = GetScoreSnapshot(); Log("Session score set. stat=" + FormatFloat(value) + ", runtimeTotal=" + FormatFloat(scoreSnapshot.Total) + ", entry=wkdebug-score"); } } private static void AddScore(string[] args) { if (args.Length < 2 || !TryParseFloat(args[1], out var value)) { Log("Usage: wkdebug_addscore [multiplier=0] [count=0] [title optional]"); return; } string text = args[0]; float value2 = 0f; int result = 0; if (args.Length > 2 && !TryParseFloat(args[2], out value2)) { Log("Could not parse multiplier: " + args[2]); return; } if (args.Length > 3 && !int.TryParse(args[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { Log("Could not parse count: " + args[3]); return; } string text2 = ((args.Length > 4) ? string.Join(" ", args.Skip(4).ToArray()) : text); SessionScore val = EnsureSessionScore(); val.AddScore(text, text2, value, value2, result); ScoreSnapshot scoreSnapshot = GetScoreSnapshot(); SetSessionScoreStat(scoreSnapshot.Total); Log("Added score entry. " + FormatScoreEntry(val.scoreDict[text])); Log("Runtime score now bonus=" + FormatFloat(scoreSnapshot.Bonus) + ", multiplier=" + FormatFloat(scoreSnapshot.Multiplier) + ", total=" + FormatFloat(scoreSnapshot.Total)); } private static void GetHighScore(string[] args) { Log("global high score = " + FormatNullableFloat(GetStatFloat(GetGlobalStats(), "score"))); Log("gamemode high score = " + FormatNullableFloat(GetStatFloat(GetGamemodeStats(), "score")) + " (" + SafeGamemodeName() + ")"); } private static void SetHighScore(string[] args) { if (args.Length < 1 || !TryParseFloat(args[0], out var value)) { Log("Usage: wkdebug_sethighscore "); } else if (SetHighScoreValue(value, persist: true)) { Log("High score set to " + FormatFloat(value) + " globally and for gamemode " + SafeGamemodeName() + "."); } } private static void ResetLeaderboard(string[] args) { float num = 0f; string text = null; if (args.Length != 0) { if (TryParseFloat(args[0], out var value)) { num = value; if (args.Length > 1) { text = string.Join(" ", args.Skip(1).ToArray()); } } else { text = string.Join(" ", args); } } if (ResetLocalLeaderboardScore(num)) { if (string.IsNullOrWhiteSpace(text)) { Log("Local leaderboard/high score reset to " + FormatFloat(num) + ". To also force a Steam leaderboard score, use: resetleaderboard " + FormatFloat(num) + " \"Steam Leaderboard Name\""); } else { ResetSteamLeaderboardScoreAsync(text.Trim(), Mathf.RoundToInt(num)); } } } private static void SetBestTime(string[] args) { if (args.Length < 1 || !TryParseTime(args[0], out var seconds)) { Log("Usage: wkdebug_setbesttime "); } else if (SetBestTimeValue(seconds, persist: true)) { Log("Best time set to " + FormatSeconds(seconds) + " globally and for gamemode " + SafeGamemodeName() + "."); } } private static void MarkWin(string[] args) { float? scoreOverride = null; float? timeOverride = null; if (args.Length != 0) { if (!TryParseFloat(args[0], out var value)) { Log("Could not parse score: " + args[0]); return; } scoreOverride = value; } if (args.Length > 1) { if (!TryParseTime(args[1], out var seconds)) { Log("Could not parse time: " + args[1]); return; } timeOverride = seconds; } if (ApplyFinishStats(win: true, scoreOverride, timeOverride, save: true)) { Log("Win stats saved without opening the end screen."); } } private static void CompleteGame(string[] args) { float? scoreOverride = null; float? timeOverride = null; if (args.Length != 0) { if (!TryParseFloat(args[0], out var value)) { Log("Could not parse score: " + args[0]); return; } scoreOverride = value; } if (args.Length > 1) { if (!TryParseTime(args[1], out var seconds)) { Log("Could not parse time: " + args[1]); return; } timeOverride = seconds; } if (scoreOverride.HasValue) { SetRuntimeScoreTarget(scoreOverride.Value); SetHighScoreValue(scoreOverride.Value, persist: false); } if (timeOverride.HasValue) { SetCurrentGameTime(timeOverride.Value); SetBestTimeValue(timeOverride.Value, persist: false); } if (ApplyFinishStats(win: true, scoreOverride, timeOverride, save: true)) { TryUpdateUnlocks(); Log("Complete-game stats saved. Use 'finishgame win' if you also want the in-game end sequence."); } } private static void FinishGame(string[] args) { bool flag = true; if (args.Length != 0) { switch (args[0].ToLowerInvariant()) { case "win": case "won": case "true": case "1": flag = true; break; case "lose": case "loss": case "dead": case "false": case "0": flag = false; break; default: Log("Usage: wkdebug_finishgame win|lose"); return; } } if ((Object)(object)CL_GameManager.gMan == (Object)null) { Log("CL_GameManager.gMan is null; cannot finish the game from here."); return; } CommandConsole.hasCheated = false; TrySetCheatTracker(active: false); try { if (flag) { CL_GameManager.gMan.Win(); } else { MethodInfo methodInfo = AccessTools.Method(typeof(CL_GameManager), "EndGameSequence", new Type[1] { typeof(bool) }, (Type[])null); if (methodInfo == null) { Log("Could not find CL_GameManager.EndGameSequence(bool)."); return; } object obj = methodInfo.Invoke(CL_GameManager.gMan, new object[1] { false }); ((MonoBehaviour)CL_GameManager.gMan).StartCoroutine((IEnumerator)obj); } Log("Started end-game sequence. win=" + flag + ", hasCheated=" + CommandConsole.hasCheated); } catch (Exception ex) { Log("finishgame failed: " + ex.Message); } } private static void GetFlag(string[] args) { if (args.Length < 1) { Log("Usage: wkdebug_getflag "); return; } string text = args[0]; bool flag = false; if ((Object)(object)CL_GameManager.gMan != (Object)null) { SessionFlag gameFlag = CL_GameManager.GetGameFlag(text); if (gameFlag != null) { Log("session flag " + text + " = " + gameFlag.state + ", data=" + gameFlag.data); flag = true; } } SaveFlags val = FindSaveFlag(text); if (val != null) { Log("save flag " + text + " = " + val.value + ", data=" + val.data); flag = true; } if (!flag) { Log("Flag not found: " + text); } } private static void SetFlag(string[] args) { if (args.Length < 2 || !TryParseBool(args[1], out var value)) { Log("Usage: wkdebug_setflag [data optional] [save=true]"); return; } if ((Object)(object)CL_GameManager.gMan == (Object)null) { Log("CL_GameManager.gMan is null; cannot set runtime game flag."); return; } string text = args[0]; string text2 = ""; bool value2 = true; if (args.Length == 3) { if (TryParseBool(args[2], out var value3)) { value2 = value3; } else { text2 = args[2]; } } else if (args.Length > 3) { text2 = args[2]; if (!TryParseBool(args[3], out value2)) { Log("Could not parse save flag: " + args[3]); return; } } CL_GameManager.SetGameFlag(text, value, text2, value2, false); if (value2) { SaveStatsToDisk(writeAll: false); } Log("Set flag " + text + " = " + value + ", data=" + text2 + ", save=" + value2); } private static void ListAchievements(string[] args) { IEnumerable achievementsEnumerable = GetAchievementsEnumerable(); if (achievementsEnumerable == null) { Log("CL_AchievementManager achievements are not available."); return; } string value = ((args.Length != 0) ? args[0] : ""); int num = ParseOptionalInt(args, 1, 80); int num2 = 0; int num3 = 0; foreach (object item in achievementsEnumerable) { num2++; string fieldString = GetFieldString(item, "id"); string fieldString2 = GetFieldString(item, "name"); if ((string.IsNullOrEmpty(value) || fieldString.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0 || fieldString2.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) && num3 < num) { num3++; Log(fieldString + " = " + GetFieldBool(item, "flagged") + " :: " + fieldString2); } } Log("achievement matches shown: " + num3 + " / total " + num2); } private static void SetAchievement(string[] args) { if (args.Length < 2 || !TryParseBool(args[1], out var value)) { Log("Usage: wkdebug_setachievement [save=true]"); return; } bool value2 = true; if (args.Length > 2 && !TryParseBool(args[2], out value2)) { Log("Could not parse save flag: " + args[2]); return; } CL_AchievementManager.SetAchievementValue(args[0], value); if (value2) { CL_AchievementManager.SaveAchievements(); SaveStatsToDisk(writeAll: false); } Log("Set achievement " + args[0] + " = " + value + ", save=" + value2); } private static void SaveStatsCommand(string[] args) { bool value = true; if (args.Length != 0 && !TryParseBool(args[0], out value)) { Log("Usage: wkdebug_savestats [writeall=true|false]"); } else { SaveStatsToDisk(value); } } private static bool ApplyFinishStats(bool win, float? scoreOverride, float? timeOverride, bool save) { GameStats sessionStats = GetSessionStats(); if (sessionStats == null) { Log("StatManager.sessionStats is null; cannot write finish stats."); return false; } CommandConsole.hasCheated = false; TrySetCheatTracker(active: false); float num = timeOverride ?? GetCurrentGameTime() ?? GetStatFloat(sessionStats, "game-time").GetValueOrDefault(); float num2 = scoreOverride ?? GetGamemodePlayerScore(win) ?? GetScoreSnapshot().Total; if (timeOverride.HasValue) { SetCurrentGameTime(num); } if (scoreOverride.HasValue) { SetRuntimeScoreTarget(num2); } SetSessionScoreStat(num2); sessionStats.UpdateStatistic("game-time", (object)num, (DataType)2, (ModType)0, (DisplayType)4, (ModType)1); if (win) { sessionStats.UpdateStatistic("wins", (object)1, (DataType)0, (ModType)0, (DisplayType)0, (ModType)1); sessionStats.UpdateStatistic("best-time", (object)num, (DataType)2, (ModType)3, (DisplayType)4, (ModType)3); } if (save) { SaveStatsToDisk(writeAll: true); } SetPreviousHighScore(Math.Max(GetStatFloat(GetGamemodeStats(), "score") ?? num2, num2)); Log("Finish stats prepared. win=" + win + ", score=" + FormatFloat(num2) + ", game-time=" + FormatSeconds(num) + ", hasCheated=" + CommandConsole.hasCheated); return true; } private static bool ResetLocalLeaderboardScore(float value) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown CommandConsole.hasCheated = false; TrySetCheatTracker(active: false); try { CL_ScoreManager.sessionScore = new SessionScore(); CL_ScoreManager.sessionScore.Initialize(); SetScoreEntry("wkdebug-score", "WK Debug Leaderboard Reset", value, 0f, 0); SetSessionScoreStat(value); if (!SetHighScoreValue(value, persist: false)) { return false; } SetPreviousHighScore(value); SaveStatsToDisk(writeAll: false); TryUpdateStatUi(); Log("Reset local leaderboard/high score. score=" + FormatFloat(value) + ", gamemode=" + SafeGamemodeName() + ", hasCheated=" + CommandConsole.hasCheated); return true; } catch (Exception ex) { Log("Local leaderboard reset failed: " + ex.Message); return false; } } private static async Task ResetSteamLeaderboardScoreAsync(string leaderboardName, int score) { _ = 1; try { if (!SteamClient.IsValid || !SteamClient.IsLoggedOn) { Log("Steam client is not connected; local leaderboard reset is done, but Steam leaderboard was not updated."); return; } Log("Finding Steam leaderboard '" + leaderboardName + "'..."); Leaderboard? val = await SteamUserStats.FindLeaderboardAsync(leaderboardName); if (!val.HasValue) { Log("Steam leaderboard not found: " + leaderboardName); return; } Leaderboard value = val.Value; LeaderboardUpdate? val2 = await ((Leaderboard)(ref value)).ReplaceScore(score, (int[])null); if (!val2.HasValue) { Log("Steam leaderboard ReplaceScore failed for: " + leaderboardName); return; } bool flag = SteamUserStats.StoreStats(); Log("Steam leaderboard score forced. name=" + leaderboardName + ", score=" + val2.Value.Score + ", changed=" + val2.Value.Changed + ", oldRank=" + val2.Value.OldGlobalRank + ", newRank=" + val2.Value.NewGlobalRank + ", StoreStats=" + flag); } catch (Exception ex) { Log("Steam leaderboard reset failed: " + ex.Message); } } private static bool SetHighScoreValue(float value, bool persist) { bool flag = false; GameStats globalStats = GetGlobalStats(); if (globalStats != null) { globalStats.UpdateStatistic("score", (object)value, (DataType)2, (ModType)0, (DisplayType)0, (ModType)2); flag = true; } GameStats gamemodeStats = GetGamemodeStats(); if (gamemodeStats != null) { gamemodeStats.UpdateStatistic("score", (object)value, (DataType)2, (ModType)0, (DisplayType)0, (ModType)2); flag = true; } if (!flag) { Log("StatManager.saveData is not available; cannot set high score."); return false; } SetPreviousHighScore(value); if (persist) { SaveStatsToDisk(writeAll: false); } return true; } private static bool SetBestTimeValue(float seconds, bool persist) { bool flag = false; GameStats globalStats = GetGlobalStats(); if (globalStats != null) { globalStats.UpdateStatistic("best-time", (object)seconds, (DataType)2, (ModType)0, (DisplayType)4, (ModType)3); flag = true; } GameStats gamemodeStats = GetGamemodeStats(); if (gamemodeStats != null) { gamemodeStats.UpdateStatistic("best-time", (object)seconds, (DataType)2, (ModType)0, (DisplayType)4, (ModType)3); flag = true; } if (!flag) { Log("StatManager.saveData is not available; cannot set best time."); return false; } if (persist) { SaveStatsToDisk(writeAll: false); } return true; } private static bool SaveStatsToDisk(bool writeAll) { if ((Object)(object)StatManager.instance == (Object)null) { Log("StatManager.instance is null; cannot save stats to disk yet."); return false; } CommandConsole.hasCheated = false; TrySetCheatTracker(active: false); try { StatManager.instance.SaveStats(writeAll); Log("StatManager.SaveStats(" + writeAll + ") complete. hasCheated=" + CommandConsole.hasCheated); return true; } catch (Exception ex) { Log("SaveStats failed: " + ex.Message); return false; } } private static bool SetCurrentGameTime(float seconds) { if (seconds < 0f) { seconds = 0f; } if ((Object)(object)CL_GameManager.gMan == (Object)null) { Log("CL_GameManager.gMan is null; cannot set runtime game-time."); return false; } try { MethodInfo methodInfo = AccessTools.Method(typeof(CL_GameManager), "SetGameTime", new Type[1] { typeof(float) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(CL_GameManager.gMan, new object[1] { seconds }); } else { FieldInfo fieldInfo = AccessTools.Field(typeof(CL_GameManager), "gameTime"); if (fieldInfo == null) { Log("Could not find CL_GameManager.SetGameTime or gameTime field."); return false; } fieldInfo.SetValue(CL_GameManager.gMan, seconds); } GameStats sessionStats = GetSessionStats(); if (sessionStats != null) { sessionStats.UpdateStatistic("game-time", (object)seconds, (DataType)2, (ModType)0, (DisplayType)4, (ModType)1); } return true; } catch (Exception ex) { Log("Set game-time failed: " + ex.Message); return false; } } private static float? GetCurrentGameTime() { try { if ((Object)(object)CL_GameManager.gMan != (Object)null) { return CL_GameManager.gMan.GetGameTime(); } } catch { } return GetStatFloat(GetSessionStats(), "game-time"); } private static float? GetGamemodePlayerScore(bool win) { try { object gamemode = CL_GameManager.gamemode; if (gamemode == null) { return null; } MethodInfo methodInfo = AccessTools.Method(gamemode.GetType(), "GetPlayerScore", new Type[1] { typeof(bool) }, (Type[])null); if (methodInfo == null) { return null; } object obj = methodInfo.Invoke(gamemode, new object[1] { win }); if (obj == null) { return null; } return Convert.ToSingle(obj, CultureInfo.InvariantCulture); } catch { return null; } } private static bool SetRuntimeScoreTarget(float target) { SessionScore val = EnsureSessionScore(); float num = 0f; float num2 = 0f; if (val.scores != null) { foreach (Score score in val.scores) { if (score != null && !string.Equals(score.id, "wkdebug-score", StringComparison.OrdinalIgnoreCase)) { num += score.bonus; num2 += score.multiplier; } } } SetScoreEntry("wkdebug-score", "WK Debug Score", target - num, 0f - num2, 1); return true; } private static void SetSessionScoreStat(float value) { GameStats sessionStats = GetSessionStats(); if (sessionStats != null) { sessionStats.UpdateStatistic("score", (object)value, (DataType)2, (ModType)0, (DisplayType)0, (ModType)2); } } private static SessionScore EnsureSessionScore() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown if (CL_ScoreManager.sessionScore == null) { CL_ScoreManager.sessionScore = new SessionScore(); } if (CL_ScoreManager.sessionScore.scores == null || CL_ScoreManager.sessionScore.scoreDict == null) { CL_ScoreManager.sessionScore.Initialize(); } return CL_ScoreManager.sessionScore; } private static void SetScoreEntry(string id, string title, float bonus, float multiplier, int count) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown SessionScore val = EnsureSessionScore(); if (val.scoreDict == null) { val.Initialize(); } if (!val.scoreDict.TryGetValue(id, out var value)) { value = new Score { id = id }; val.scores.Add(value); val.scoreDict[id] = value; } value.title = title; value.bonus = bonus; value.multiplier = multiplier; value.count = count; } private static ScoreSnapshot GetScoreSnapshot() { ScoreSnapshot result = new ScoreSnapshot { Multiplier = 1f }; SessionScore sessionScore = CL_ScoreManager.sessionScore; if (sessionScore == null || sessionScore.scores == null) { result.Total = 0f; return result; } foreach (Score score in sessionScore.scores) { if (score != null) { result.Count++; result.Bonus += score.bonus; result.Multiplier += score.multiplier; } } result.Multiplier = Mathf.Max(result.Multiplier, 0f); result.Total = result.Bonus * result.Multiplier; return result; } private static string FormatScoreEntry(Score entry) { if (entry == null) { return ""; } return entry.id + " = bonus " + FormatFloat(entry.bonus) + ", multiplier " + FormatFloat(entry.multiplier) + ", count " + entry.count + ", title=" + entry.title; } private static void SetPreviousHighScore(float value) { try { if ((Object)(object)CL_GameManager.gMan != (Object)null) { AccessTools.Field(typeof(CL_GameManager), "previousHighScore")?.SetValue(CL_GameManager.gMan, value); } } catch { } } private static void TryUpdateUnlocks() { try { Type type = AccessTools.TypeByName("CL_ProgressionManager"); ((type != null) ? AccessTools.Method(type, "UpdateUnlocks", new Type[1] { typeof(bool) }, (Type[])null) : null)?.Invoke(null, new object[1] { true }); } catch { } } private static void TryUpdateStatUi() { try { StatManager instance = StatManager.instance; if (instance != null) { instance.UpdateUI(); } } catch { } } private static GameStats GetGlobalStats() { try { if (StatManager.saveData == null || StatManager.saveData.gameStats == null) { return null; } EnsureStatsDictionary(StatManager.saveData.gameStats); return StatManager.saveData.gameStats; } catch { return null; } } private static GameStats GetGamemodeStats() { try { if (StatManager.saveData == null) { return null; } string text = SafeGamemodeName(); if (string.IsNullOrEmpty(text) || text == "null") { return null; } GameModeData gameMode = StatManager.saveData.GetGameMode(text); if (gameMode == null || gameMode.stats == null) { return null; } EnsureStatsDictionary(gameMode.stats); return gameMode.stats; } catch { return null; } } private static void EnsureStatsDictionary(GameStats stats) { if (stats != null && (stats.statsDictionary == null || stats.statsDictionary.Count == 0)) { stats.InitializeDictionary(); } } private static float? GetStatFloat(GameStats stats, string id) { try { if (stats == null || string.IsNullOrEmpty(id)) { return null; } EnsureStatsDictionary(stats); if (!stats.HasStatistic(id)) { return null; } object value = stats.GetStatistic(id).GetValue(); if (value is float value2) { return value2; } if (value is int num) { return num; } if (value is string raw && TryParseFloat(raw, out var value3)) { return value3; } } catch { return null; } return null; } private static SaveFlags FindSaveFlag(string id) { if (StatManager.saveData?.flags == null) { return null; } return ((IEnumerable)StatManager.saveData.flags).FirstOrDefault((Func)((SaveFlags f) => f != null && string.Equals(f.name, id, StringComparison.OrdinalIgnoreCase))); } private static IEnumerable GetAchievementsEnumerable() { if ((Object)(object)CL_AchievementManager.instance == (Object)null) { return null; } return AccessTools.Field(typeof(CL_AchievementManager), "achievements")?.GetValue(CL_AchievementManager.instance) as IEnumerable; } private static string GetFieldString(object obj, string fieldName) { return (obj?.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj))?.ToString() ?? ""; } private static bool GetFieldBool(object obj, string fieldName) { object obj2 = obj?.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); bool flag = default(bool); int num; if (obj2 is bool) { flag = (bool)obj2; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static string SafeGamemodeName() { try { return ((Object)(object)CL_GameManager.gamemode != (Object)null) ? CL_GameManager.GetGamemodeName(true, false) : "null"; } catch { return "unknown"; } } private static int ParseOptionalInt(string[] args, int index, int defaultValue) { if (args.Length <= index || !int.TryParse(args[index], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { return defaultValue; } return result; } private static bool TryParseFloat(string raw, out float value) { value = 0f; if (!string.IsNullOrWhiteSpace(raw)) { return float.TryParse(raw.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out value); } return false; } private static bool TryParseBool(string raw, out bool value) { value = false; if (bool.TryParse(raw, out value)) { return true; } if (string.Equals(raw, "1", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "yes", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "on", StringComparison.OrdinalIgnoreCase)) { value = true; return true; } if (string.Equals(raw, "0", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "no", StringComparison.OrdinalIgnoreCase) || string.Equals(raw, "off", StringComparison.OrdinalIgnoreCase)) { value = false; return true; } return false; } private static bool TryParseTime(string raw, out float seconds) { seconds = 0f; if (string.IsNullOrWhiteSpace(raw)) { return false; } raw = raw.Trim(); if (!raw.Contains(":")) { return TryParseFloat(raw, out seconds); } string[] array = raw.Split(':'); float num = 1f; for (int num2 = array.Length - 1; num2 >= 0; num2--) { if (!TryParseFloat(array[num2], out var value)) { seconds = 0f; return false; } seconds += value * num; num *= 60f; } return true; } private static string FormatNullableFloat(float? value) { if (!value.HasValue) { return "null"; } return FormatFloat(value.Value); } private static string FormatFloat(float value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static string FormatNullableSeconds(float? seconds) { if (!seconds.HasValue) { return "null"; } return FormatSeconds(seconds.Value); } private static string FormatSeconds(float seconds) { if (seconds < 0f) { seconds = 0f; } TimeSpan timeSpan = TimeSpan.FromSeconds(seconds); string text = ((timeSpan.TotalHours >= 1.0) ? timeSpan.ToString("hh\\:mm\\:ss\\.ff", CultureInfo.InvariantCulture) : timeSpan.ToString("mm\\:ss\\.ff", CultureInfo.InvariantCulture)); return FormatFloat(seconds) + "s (" + text + ")"; } private static string[] SplitCommandLine(string command) { List list = new List(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; foreach (char c in command) { if (c == '"') { flag = !flag; } else if (!flag && char.IsWhiteSpace(c)) { if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); stringBuilder.Length = 0; } } else { stringBuilder.Append(c); } } if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); } return list.ToArray(); } private static GameStats GetSessionStats() { if (StatManager.sessionStats == null) { return null; } if (StatManager.sessionStats.statsDictionary == null || StatManager.sessionStats.statsDictionary.Count == 0) { StatManager.sessionStats.InitializeDictionary(); } return StatManager.sessionStats; } private static Statistic FindSessionStat(string id) { GameStats sessionStats = GetSessionStats(); if (sessionStats == null || string.IsNullOrEmpty(id)) { return null; } if (sessionStats.statsDictionary != null && sessionStats.statsDictionary.TryGetValue(id, out var value)) { return value; } return ((IEnumerable)sessionStats.statistics).FirstOrDefault((Func)((Statistic s) => s != null && string.Equals(s.id, id, StringComparison.OrdinalIgnoreCase))); } private static string FormatStat(Statistic stat) { if (stat == null) { return ""; } return stat.id + " = " + stat.value + " (" + ((object)Unsafe.As(ref stat.type)/*cast due to .constrained prefix*/).ToString() + ", defaultMod=" + ((object)Unsafe.As(ref stat.defaultModType)/*cast due to .constrained prefix*/).ToString() + ", display=" + ((object)Unsafe.As(ref stat.displayType)/*cast due to .constrained prefix*/).ToString() + ")"; } private static DataType ParseType(string type) { if (!string.Equals(type, "int", StringComparison.OrdinalIgnoreCase) && !string.Equals(type, "0", StringComparison.OrdinalIgnoreCase)) { if (!string.Equals(type, "string", StringComparison.OrdinalIgnoreCase) && !string.Equals(type, "str", StringComparison.OrdinalIgnoreCase) && !string.Equals(type, "1", StringComparison.OrdinalIgnoreCase)) { if (!string.Equals(type, "float", StringComparison.OrdinalIgnoreCase) && !string.Equals(type, "single", StringComparison.OrdinalIgnoreCase)) { string.Equals(type, "2", StringComparison.OrdinalIgnoreCase); } return (DataType)2; } return (DataType)1; } return (DataType)0; } private static DataType InferType(string value) { if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _)) { if (!float.TryParse(value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var _)) { return (DataType)1; } return (DataType)2; } return (DataType)0; } private static bool TryParseValue(string rawValue, DataType type, out object parsed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown switch ((int)type) { case 0: { if (int.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { parsed = result2; return true; } parsed = null; return false; } case 2: { if (float.TryParse(rawValue.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { parsed = result; return true; } parsed = null; return false; } case 1: parsed = rawValue; return true; default: parsed = null; return false; } } private static void TrySetCheatTracker(bool active) { try { if ((Object)(object)CL_UIManager.instance != (Object)null && (Object)(object)CL_UIManager.instance.cheatTracker != (Object)null) { CL_UIManager.instance.cheatTracker.SetActive(active); } } catch { } } private static void Log(string message) { string text = "[WKDebugAccess] " + message; recentMessages.Insert(0, DateTime.Now.ToString("HH:mm:ss", CultureInfo.InvariantCulture) + " " + message); while (recentMessages.Count > 40) { recentMessages.RemoveAt(recentMessages.Count - 1); } try { CommandConsole.Log(text); } catch { } Debug.Log((object)text); ManualLogSource obj2 = logSource; if (obj2 != null) { obj2.LogInfo((object)message); } try { string configDir = GetConfigDir(); Directory.CreateDirectory(configDir); File.AppendAllText(Path.Combine(configDir, "WKDebugAccess.log"), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture) + " " + message + Environment.NewLine, Encoding.UTF8); } catch { } } private static string GetConfigDir() { return Path.Combine(Paths.ConfigPath, "WKDebugAccess"); } } }