using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Marioalexsan.Multitool")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+73def33ce42faeb1228b16d483cce8ca8c476ae4")]
[assembly: AssemblyProduct("Multitool")]
[assembly: AssemblyTitle("Marioalexsan.Multitool")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 Marioalexsan.Multitool
{
internal static class Logging
{
private static ManualLogSource InternalLogger => MultitoolPlugin.Plugin.Logger;
public static void LogFatal(object data, ConfigEntry<bool>? toggle = null)
{
Log(data, (LogLevel)1, toggle);
}
public static void LogError(object data, ConfigEntry<bool>? toggle = null)
{
Log(data, (LogLevel)2, toggle);
}
public static void LogWarning(object data, ConfigEntry<bool>? toggle = null)
{
Log(data, (LogLevel)4, toggle);
}
public static void LogMessage(object data, ConfigEntry<bool>? toggle = null)
{
Log(data, (LogLevel)8, toggle);
}
public static void LogInfo(object data, ConfigEntry<bool>? toggle = null)
{
Log(data, (LogLevel)16, toggle);
}
public static void LogDebug(object data, ConfigEntry<bool>? toggle = null)
{
Log(data, (LogLevel)32, toggle);
}
private static void Log(object data, LogLevel level = 16, ConfigEntry<bool>? toggle = null)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
if (toggle == null || toggle.Value)
{
ManualLogSource internalLogger = InternalLogger;
if (internalLogger != null)
{
internalLogger.Log(level, data);
}
}
}
}
[BepInPlugin("Marioalexsan.Multitool", "Multitool", "1.0.1")]
public class MultitoolPlugin : BaseUnityPlugin
{
private static MultitoolPlugin? _plugin;
private readonly Harmony _harmony;
public static MultitoolPlugin Plugin => _plugin ?? throw new InvalidOperationException("MultitoolPlugin hasn't been initialized yet. Either wait until initialization, or check via ChainLoader instead.");
internal ManualLogSource Logger { get; private set; }
public MultitoolPlugin()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
_plugin = this;
Logger = ((BaseUnityPlugin)this).Logger;
_harmony = new Harmony("Marioalexsan.Multitool");
}
public void Awake()
{
_harmony.PatchAll();
}
}
internal static class PluginUtils
{
public static bool ValidatePlugin(BaseUnityPlugin plugin, out string guid)
{
BaseUnityPlugin plugin2 = plugin;
KeyValuePair<string, PluginInfo> keyValuePair = Chainloader.PluginInfos.FirstOrDefault((KeyValuePair<string, PluginInfo> x) => (Object)(object)x.Value.Instance == (Object)(object)plugin2);
if (keyValuePair.Value == null)
{
Logging.LogWarning("Couldn't validate that calling mod exists in the Chainloader.");
guid = "";
return false;
}
guid = keyValuePair.Key;
return true;
}
}
internal static class ModInfo
{
public const string GUID = "Marioalexsan.Multitool";
public const string NAME = "Multitool";
public const string VERSION = "1.0.1";
}
}
namespace Marioalexsan.Multitool.Utils
{
public static class HierarchyTraverse
{
public static GameObject ForChild(this GameObject obj, string path, Action<GameObject> action)
{
action(obj.GoToChild(path));
return obj;
}
public static GameObject GoToChild(this GameObject obj, string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("Path is empty", "path");
}
string[] array = path.Split("/");
if (array.Length == 0)
{
throw new ArgumentException("Path is empty", "path");
}
GameObject val = obj;
for (int i = 0; i < array.Length; i++)
{
Transform val2 = val.transform.Find(array[i]);
if (!Object.op_Implicit((Object)(object)val2))
{
throw new InvalidOperationException("Child " + array[i] + " in path " + path + " does not exist for object " + ((Object)obj).name + ".");
}
val = ((Component)val2).gameObject;
}
return val;
}
public static GameObject ForEachChild(this GameObject obj, Action<GameObject, int> action)
{
Transform transform = obj.transform;
for (int i = 0; i < transform.childCount; i++)
{
action(((Component)transform.GetChild(i)).gameObject, i);
}
return obj;
}
public static GameObject ForParent(this GameObject obj, Action<GameObject> action)
{
action(obj.GoToParent());
return obj;
}
public static GameObject ForParent(this GameObject obj, int levels, Action<GameObject> action)
{
action(obj.GoToParent(levels));
return obj;
}
public static GameObject GoToParent(this GameObject obj)
{
return obj.GoToParent(1);
}
public static GameObject GoToParent(this GameObject obj, int levels)
{
if (levels <= 0)
{
throw new ArgumentException("Levels must be a positive number", "levels");
}
int num = 0;
while (++num <= levels)
{
Transform parent = obj.transform.parent;
if (!Object.op_Implicit((Object)(object)parent))
{
throw new InvalidOperationException($"Parent at level {num} does not exist for object {((Object)obj).name}.");
}
obj = ((Component)parent).gameObject;
}
return obj;
}
public static GameObject ForComponent<T>(this GameObject obj, Action<T> action) where T : Component
{
T component = obj.GetComponent<T>();
if (!Object.op_Implicit((Object)(object)component))
{
throw new InvalidOperationException("Component " + typeof(T).Name + " does not exist on object " + ((Object)obj).name + ".");
}
action(component);
return obj;
}
public static GameObject ForComponentIfExists<T>(this GameObject obj, Action<T> action) where T : Component
{
T component = obj.GetComponent<T>();
if (Object.op_Implicit((Object)(object)component))
{
action(component);
}
return obj;
}
public static GameObject ForChildComponent<T>(this GameObject obj, Action<T> action) where T : Component
{
T componentInChildren = obj.GetComponentInChildren<T>();
if (!Object.op_Implicit((Object)(object)componentInChildren))
{
throw new InvalidOperationException("Component " + typeof(T).Name + " does not exist on object " + ((Object)obj).name + ".");
}
action(componentInChildren);
return obj;
}
public static GameObject ForChildComponentIfExists<T>(this GameObject obj, Action<T> action) where T : Component
{
T componentInChildren = obj.GetComponentInChildren<T>();
if (Object.op_Implicit((Object)(object)componentInChildren))
{
action(componentInChildren);
}
return obj;
}
public static GameObject ForEachComponent<T>(this GameObject obj, Action<T, int> action) where T : Component
{
T[] components = obj.GetComponents<T>();
for (int i = 0; i < components.Length; i++)
{
action(components[i], i);
}
return obj;
}
public static GameObject SaveComponentRef<T>(this GameObject obj, ref T? field) where T : Component
{
T component = obj.GetComponent<T>();
if (!Object.op_Implicit((Object)(object)component))
{
throw new InvalidOperationException("Component " + typeof(T).Name + " does not exist on object " + ((Object)obj).name + ".");
}
field = component;
return obj;
}
public static GameObject Rename(this GameObject obj, string name)
{
((Object)obj).name = name;
return obj;
}
}
}
namespace Marioalexsan.Multitool.SaveUtils
{
public delegate void SaveProfileData<T>(CharacterFile file, int slotIndex, out T? data) where T : class;
public delegate void LoadProfileData<T>(CharacterFile file, int slotIndex, T? data) where T : class;
public delegate void DeleteProfileData(CharacterFile file, int slotIndex);
internal delegate void SaveRawProfileData(CharacterFile file, int slotIndex, out JToken? data);
internal delegate void LoadRawProfileData(CharacterFile file, int slotIndex, JToken? data);
internal class PluginSaveConfig
{
public SaveRawProfileData OnSave { get; set; }
public LoadRawProfileData OnLoad { get; set; }
public DeleteProfileData OnDelete { get; set; }
public PluginSaveConfig(SaveRawProfileData onSave, LoadRawProfileData onLoad, DeleteProfileData onDelete)
{
OnSave = onSave;
OnLoad = onLoad;
OnDelete = onDelete;
base..ctor();
}
}
internal class ProfileDataStore
{
public CharacterFile AssociatedFile { get; set; }
public Dictionary<string, JToken> StoredData { get; set; }
public ProfileDataStore(CharacterFile associatedFile, Dictionary<string, JToken> storedData)
{
AssociatedFile = associatedFile;
StoredData = storedData;
base..ctor();
}
}
public static class SaveUtilsAPI
{
internal static readonly Dictionary<string, PluginSaveConfig> PluginConfiguration = new Dictionary<string, PluginSaveConfig>();
internal static Dictionary<int, ProfileDataStore> ProfileDataStores { get; } = new Dictionary<int, ProfileDataStore>();
public static string SaveDirectory => Path.Combine(Path.GetDirectoryName(Paths.ExecutablePath), "ATLYSS_Data", "profileCollections", "Marioalexsan_Multitool");
public static string GetModdedCharacterProfilePath(int index)
{
return Path.Combine(SaveDirectory, $"atl_characterProfile_{index}_mods");
}
public static void RegisterProfileData<T>(string guid, SaveProfileData<T> onSave, LoadProfileData<T> onLoad, DeleteProfileData onDelete) where T : class
{
SaveProfileData<T> onSave2 = onSave;
string guid2 = guid;
LoadProfileData<T> onLoad2 = onLoad;
if (string.IsNullOrWhiteSpace(guid2))
{
Logging.LogWarning("GUID " + guid2 + " is invalid!");
}
else if (PluginConfiguration.ContainsKey(guid2))
{
Logging.LogWarning("Mod " + guid2 + " already registered profile save data!");
}
else
{
PluginConfiguration[guid2] = new PluginSaveConfig(WrappedOnSave, WrappedOnLoad, onDelete);
}
void WrappedOnLoad(CharacterFile file, int slotIndex, JToken? data)
{
T data2;
try
{
data2 = ((data != null) ? data.ToObject<T>() : null);
}
catch (Exception data3)
{
Logging.LogWarning("Failed to deserialize modded data of type " + typeof(T).Name + " for " + guid2 + "! This may cause issues with saved mod data.");
Logging.LogWarning("Will try to load without any modded save data available.");
Logging.LogWarning(data3);
data2 = null;
}
onLoad2(file, slotIndex, data2);
}
void WrappedOnSave(CharacterFile file, int slotIndex, out JToken? data)
{
onSave2(file, slotIndex, out var data4);
data = ((data4 != null) ? JToken.FromObject((object)data4) : null);
}
}
}
}
namespace Marioalexsan.Multitool.SaveUtils.Patches
{
[HarmonyPatch(typeof(ProfileDataManager), "Delete_ProfileData")]
internal static class DeleteProfileData
{
private static void Postfix(ProfileDataManager __instance, int _index)
{
CharacterFile file = __instance._characterFiles[_index];
try
{
ExecuteDeleteCallbacks(file, _index);
DeleteModdedData(_index);
}
catch (Exception data)
{
Logging.LogError("Failed to execute DeleteProfileData logic for mooded saves! Please report this error to the mod author!");
Logging.LogError(data);
}
}
private static void DeleteModdedData(int index)
{
SaveUtilsAPI.ProfileDataStores.Remove(index);
if (Directory.Exists(SaveUtilsAPI.SaveDirectory))
{
string moddedCharacterProfilePath = SaveUtilsAPI.GetModdedCharacterProfilePath(index);
if (File.Exists(moddedCharacterProfilePath))
{
File.Delete(moddedCharacterProfilePath);
}
}
}
private static void ExecuteDeleteCallbacks(CharacterFile file, int index)
{
foreach (KeyValuePair<string, PluginSaveConfig> item in SaveUtilsAPI.PluginConfiguration)
{
try
{
item.Value.OnDelete(file, index);
}
catch (Exception data)
{
Logging.LogWarning("Failed to execute DeleteProfileData callback for " + item.Key + "! This may cause issues with saved mod data.");
Logging.LogWarning(data);
}
}
}
}
[HarmonyPatch(typeof(ProfileDataManager), "Load_ProfileData")]
[HarmonyPriority(800)]
internal static class LoadProfileData
{
private static readonly object CallbackLock = new object();
private static void Postfix(ProfileDataManager __instance, int _index)
{
CharacterFile val = __instance._characterFiles[_index];
if (val._isEmptySlot)
{
return;
}
try
{
lock (CallbackLock)
{
LoadModdedData(val, _index);
ExecuteLoadCallbacks(val, _index);
}
}
catch (Exception data)
{
Logging.LogError("Failed to execute LoadProfileData logic for mooded saves! Please report this error to the mod author!");
Logging.LogError(data);
}
}
private static void LoadModdedData(CharacterFile file, int index)
{
if (!Directory.Exists(SaveUtilsAPI.SaveDirectory))
{
Directory.CreateDirectory(SaveUtilsAPI.SaveDirectory);
}
string moddedCharacterProfilePath = SaveUtilsAPI.GetModdedCharacterProfilePath(index);
Dictionary<string, JToken> dictionary = new Dictionary<string, JToken>();
SaveUtilsAPI.ProfileDataStores[index] = new ProfileDataStore(file, dictionary);
if (!File.Exists(moddedCharacterProfilePath))
{
return;
}
JToken val = JToken.Parse(File.ReadAllText(moddedCharacterProfilePath));
JObject val2 = (JObject)(object)((val is JObject) ? val : null);
if (val2 == null)
{
Logging.LogWarning("Couldn't parse character file plugin profile data.");
return;
}
JToken obj = val2["mods"];
JObject val3 = (JObject)(object)((obj is JObject) ? obj : null);
if (val3 == null)
{
Logging.LogWarning("Couldn't parse character file plugin profile data.");
return;
}
foreach (KeyValuePair<string, JToken> item in val3)
{
JToken value = item.Value;
JObject val4 = (JObject)(object)((value is JObject) ? value : null);
if (val4 != null)
{
dictionary[item.Key] = (JToken)(object)val4;
}
else
{
Logging.LogWarning("Profile data key " + item.Key + " is not an object. This may cause issues with saved mod data.");
}
}
}
private static void ExecuteLoadCallbacks(CharacterFile file, int index)
{
ProfileDataStore profileDataStore = SaveUtilsAPI.ProfileDataStores[index];
foreach (KeyValuePair<string, PluginSaveConfig> item in SaveUtilsAPI.PluginConfiguration)
{
JToken valueOrDefault = profileDataStore.StoredData.GetValueOrDefault(item.Key);
try
{
item.Value.OnLoad(file, index, valueOrDefault);
}
catch (Exception data)
{
Logging.LogWarning("Failed to execute LoadProfileData callback for " + item.Key + "! This may cause issues with saved mod data.");
Logging.LogWarning(data);
}
}
}
}
[HarmonyPatch(typeof(ProfileDataManager), "Save_ProfileData")]
[HarmonyPriority(800)]
internal static class SaveProfileData
{
private static void Postfix(ProfileDataManager __instance)
{
int selectedFileIndex = __instance._selectedFileIndex;
CharacterFile characterFile = __instance._characterFile;
try
{
ExecuteSaveCallbacks(characterFile, selectedFileIndex);
SaveModdedData(characterFile, selectedFileIndex);
}
catch (Exception data)
{
Logging.LogError("Failed to execute SaveProfileData logic for mooded saves! Please report this error to the mod author!");
Logging.LogError(data);
}
}
private static void SaveModdedData(CharacterFile file, int index)
{
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Expected O, but got Unknown
if (!Directory.Exists(SaveUtilsAPI.SaveDirectory))
{
Directory.CreateDirectory(SaveUtilsAPI.SaveDirectory);
}
ProfileDataStore value;
Dictionary<string, JToken> dictionary = (SaveUtilsAPI.ProfileDataStores.TryGetValue(index, out value) ? value.StoredData : new Dictionary<string, JToken>());
JToken val = JToken.FromObject((object)dictionary);
JObject val2 = new JObject
{
["multitoolVersion"] = JToken.op_Implicit("1.0.1"),
["mods"] = val
};
string moddedCharacterProfilePath = SaveUtilsAPI.GetModdedCharacterProfilePath(index);
File.WriteAllText(moddedCharacterProfilePath, ((JToken)val2).ToString((Formatting)1, Array.Empty<JsonConverter>()));
}
private static void ExecuteSaveCallbacks(CharacterFile file, int index)
{
if (!SaveUtilsAPI.ProfileDataStores.TryGetValue(index, out ProfileDataStore value))
{
value = (SaveUtilsAPI.ProfileDataStores[index] = new ProfileDataStore(file, new Dictionary<string, JToken>()));
}
foreach (KeyValuePair<string, PluginSaveConfig> item in SaveUtilsAPI.PluginConfiguration)
{
try
{
item.Value.OnSave(file, index, out var data);
if (data == null)
{
value.StoredData.Remove(item.Key);
}
else
{
value.StoredData[item.Key] = data;
}
}
catch (Exception data2)
{
Logging.LogWarning("Failed to execute SaveProfileData callback for " + item.Key + "! This may cause issues with saved mod data.");
Logging.LogWarning(data2);
}
}
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}