Decompiled source of OdinSaves v1.5.0

OdinSaves.dll

Decompiled 3 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("OdinSaves")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OdinSaves")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("649fad21-6342-4434-8747-28db2fe311bc")]
[assembly: AssemblyFileVersion("1.5.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace OdinSaves
{
	public static class PluginConfig
	{
		public static ConfigFile CurrentConfig { get; private set; }

		public static ConfigEntry<bool> IsModEnabled { get; private set; }

		public static ConfigEntry<int> SavePlayerProfileInterval { get; private set; }

		public static ConfigEntry<bool> SetLogoutPointOnSave { get; private set; }

		public static ConfigEntry<bool> ShowMessageOnModSave { get; private set; }

		public static ConfigEntry<bool> EnableMapDataCompression { get; private set; }

		public static void BindConfig(ConfigFile config)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			CurrentConfig = config;
			IsModEnabled = config.Bind<bool>("Global", "isModEnabled", true, "Globally enable or disable this mod.");
			SavePlayerProfileInterval = config.Bind<int>("Global", "savePlayerProfileInterval", 300, new ConfigDescription("Interval (seconds) for how often to save the player profile. Game default (and maximum) is 1200s.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 1200), Array.Empty<object>()));
			SetLogoutPointOnSave = config.Bind<bool>("Global", "setLogoutPointOnSave", true, "Sets your logout point to your current position when the mod performs a save.");
			ShowMessageOnModSave = config.Bind<bool>("Global", "saveMessageOnModSave", true, "Show a message (in the middle of your screen) when the mod tries to save.");
			EnableMapDataCompression = config.Bind<bool>("MapData.Compression", "enableMapDataCompression", false, "Enables the MapData compression feature on the character select screen (restart required).");
		}
	}
	public static class SaveManager
	{
		[CompilerGenerated]
		private sealed class <CompressProfileMapDataCoroutine>d__14 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public PlayerProfile profile;

			public FejdStartup fejdStartup;

			private Selectable[] <selectables>5__2;

			private long <count>5__3;

			private TMP_Text <buttonText>5__4;

			private Dictionary<long, WorldPlayerData>.ValueCollection.Enumerator <>7__wrap4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CompressProfileMapDataCoroutine>d__14(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<selectables>5__2 = null;
				<buttonText>5__4 = null;
				<>7__wrap4 = default(Dictionary<long, WorldPlayerData>.ValueCollection.Enumerator);
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					Selectable[] array;
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						<selectables>5__2 = Object.FindObjectsOfType<Selectable>();
						array = <selectables>5__2;
						for (int i = 0; i < array.Length; i++)
						{
							array[i].interactable = false;
						}
						<count>5__3 = 0L;
						<buttonText>5__4 = ((Component)_compressMapDataButton).GetComponentInChildren<TMP_Text>();
						<>7__wrap4 = profile.m_worldData.Values.GetEnumerator();
						<>1__state = -3;
						break;
					}
					case 1:
						<>1__state = -3;
						break;
					}
					if (<>7__wrap4.MoveNext())
					{
						WorldPlayerData current = <>7__wrap4.Current;
						<buttonText>5__4.text = $"Compressing... {++<count>5__3}/{profile.m_worldData.Count}";
						current.m_mapData = CompressMapData(ref current.m_mapData);
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					<>m__Finally1();
					<>7__wrap4 = default(Dictionary<long, WorldPlayerData>.ValueCollection.Enumerator);
					array = <selectables>5__2;
					for (int i = 0; i < array.Length; i++)
					{
						array[i].interactable = true;
					}
					profile.SavePlayerToDisk();
					fejdStartup.UpdateCharacterList();
					_mapPackage.Clear();
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				((IDisposable)<>7__wrap4).Dispose();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly ZPackage _mapPackage = new ZPackage();

		private static GameObject _profileCompressionRoot;

		private static Button _compressMapDataButton;

		private static TMP_Text _profileCompressionText;

		private static byte[] CompressMapData(ref byte[] mapData)
		{
			if (mapData == null || IsCompressedMapData(mapData))
			{
				return mapData;
			}
			_mapPackage.Clear();
			_mapPackage.Write(Minimap.MAPVERSION);
			_mapPackage.Write(Utils.Compress(mapData));
			return _mapPackage.GetArray();
		}

		private static bool IsCompressedMapData(byte[] data)
		{
			if (data != null && data.Length >= 4)
			{
				return BitConverter.ToInt32(data, 0) >= 7;
			}
			return false;
		}

		private static bool HasUncompressedData(PlayerProfile profile)
		{
			return profile.m_worldData.Values.Any((WorldPlayerData value) => value.m_mapData != null && !IsCompressedMapData(value.m_mapData));
		}

		public static void CreateProfileCompressionUI(FejdStartup fejdStartup)
		{
			_profileCompressionRoot = CreateCompressionRoot(fejdStartup.m_selectCharacterPanel.transform);
			_compressMapDataButton = CreateCompressMapDataButton(fejdStartup, _profileCompressionRoot.transform);
			_profileCompressionText = CreateProfileCompressionText(fejdStartup, _profileCompressionRoot.transform);
		}

		private static GameObject CreateCompressionRoot(Transform parent)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			GameObject val = new GameObject("CompressionRoot", new Type[2]
			{
				typeof(RectTransform),
				typeof(VerticalLayoutGroup)
			});
			val.transform.SetParent(parent);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0f);
			component.anchorMax = new Vector2(0.5f, 0f);
			component.pivot = new Vector2(0.5f, 0f);
			component.anchoredPosition = new Vector2(410f, 74f);
			VerticalLayoutGroup component2 = val.GetComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = false;
			((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = false;
			((LayoutGroup)component2).childAlignment = (TextAnchor)1;
			val.SetActive(true);
			return val;
		}

		private static Button CreateCompressMapDataButton(FejdStartup fejdStartup, Transform parent)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			Button obj = Object.Instantiate<Button>(fejdStartup.m_csNewButton, parent);
			((UnityEventBase)obj.onClick).RemoveAllListeners();
			obj.onClick = new ButtonClickedEvent();
			((Component)obj).GetComponent<RectTransform>().SetSizeWithCurrentAnchors((Axis)0, 200f);
			TMP_Text componentInChildren = ((Component)obj).GetComponentInChildren<TMP_Text>();
			((Component)componentInChildren).GetComponent<RectTransform>().SetSizeWithCurrentAnchors((Axis)0, 200f);
			componentInChildren.text = "Compression";
			((Object)((Component)obj).gameObject).name = "CompressMapData.Button";
			((Component)obj).gameObject.SetActive(false);
			return obj;
		}

		private static TMP_Text CreateProfileCompressionText(FejdStartup fejdStartup, Transform parent)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text obj = Object.Instantiate<TMP_Text>(fejdStartup.m_csName, parent);
			obj.fontSize = 20f;
			((Object)((Component)obj).gameObject).name = "ProfileCompression.Text";
			obj.text = "Profile Compression Text";
			RectTransform component = ((Component)obj).GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.zero;
			component.pivot = Vector2.zero;
			component.anchoredPosition = new Vector2(-146f, -89f);
			component.ForceUpdateRectTransforms();
			return obj;
		}

		public static void UpdateProfileCompressionUI(FejdStartup fejdStartup)
		{
			int profileIndex = fejdStartup.m_profileIndex;
			if (Object.op_Implicit((Object)(object)_profileCompressionRoot) && profileIndex >= 0 && profileIndex < fejdStartup.m_profiles.Count)
			{
				PlayerProfile profile = fejdStartup.m_profiles[profileIndex];
				UpdateCompressMapDataButton(fejdStartup, profile);
				UpdateProfileCompressionText(profile);
			}
		}

		private static void UpdateCompressMapDataButton(FejdStartup fejdStartup, PlayerProfile profile)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			bool flag = HasUncompressedData(profile);
			((Component)_compressMapDataButton).GetComponentInChildren<TMP_Text>().text = (flag ? "Compress MapData" : "Compressed!");
			((UnityEventBase)_compressMapDataButton.onClick).RemoveAllListeners();
			((UnityEvent)_compressMapDataButton.onClick).AddListener((UnityAction)delegate
			{
				((MonoBehaviour)fejdStartup).StartCoroutine(CompressProfileMapDataCoroutine(fejdStartup, profile));
			});
			((Selectable)_compressMapDataButton).interactable = flag;
			((Component)_compressMapDataButton).gameObject.SetActive(true);
		}

		private static void UpdateProfileCompressionText(PlayerProfile profile)
		{
			float num = profile.m_worldData.Values.Select(delegate(WorldPlayerData value)
			{
				byte[] mapData = value.m_mapData;
				return (mapData != null) ? mapData.Length : 0;
			}).Sum();
			int num2 = profile.m_worldData.Values.Select((WorldPlayerData value) => (value.m_mapData == null || IsCompressedMapData(value.m_mapData)) ? 1 : 0).Sum();
			_profileCompressionText.text = string.Format("Worlds: <color={0}>{1}</color>/<color={0}>{2}</color> compressed   MapData: <color={0}>{3}</color> KB", "orange", profile.m_worldData.Count, num2, (num / 1024f).ToString("N0"));
		}

		[IteratorStateMachine(typeof(<CompressProfileMapDataCoroutine>d__14))]
		private static IEnumerator CompressProfileMapDataCoroutine(FejdStartup fejdStartup, PlayerProfile profile)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CompressProfileMapDataCoroutine>d__14(0)
			{
				fejdStartup = fejdStartup,
				profile = profile
			};
		}
	}
	[BepInPlugin("redseiko.valheim.odinsaves", "OdinSaves", "1.5.0")]
	public sealed class OdinSaves : BaseUnityPlugin
	{
		public const string PluginGuid = "redseiko.valheim.odinsaves";

		public const string PluginName = "OdinSaves";

		public const string PluginVersion = "1.5.0";

		private static ManualLogSource _logger;

		private void Awake()
		{
			_logger = ((BaseUnityPlugin)this).Logger;
			PluginConfig.BindConfig(((BaseUnityPlugin)this).Config);
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "redseiko.valheim.odinsaves");
		}

		public static void LogInfo(object obj)
		{
			_logger.LogInfo((object)$"[{DateTime.Now.ToString(DateTimeFormatInfo.InvariantInfo)}] {obj}");
		}
	}
	[HarmonyPatch(typeof(FejdStartup))]
	internal static class FejdStartupPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("Awake")]
		private static void AwakePostfix(FejdStartup __instance)
		{
			if (PluginConfig.IsModEnabled.Value && PluginConfig.EnableMapDataCompression.Value)
			{
				SaveManager.CreateProfileCompressionUI(__instance);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("UpdateCharacterList")]
		private static void UpdateCharacterListPostfix(FejdStartup __instance)
		{
			if (PluginConfig.IsModEnabled.Value && PluginConfig.EnableMapDataCompression.Value)
			{
				SaveManager.UpdateProfileCompressionUI(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Game))]
	internal static class GamePatch
	{
		private static float _savePlayerProfileTimer;

		[HarmonyPostfix]
		[HarmonyPatch("SavePlayerProfile")]
		private static void SavePlayerProfilePostfix()
		{
			_savePlayerProfileTimer = 0f;
		}

		[HarmonyPrefix]
		[HarmonyPatch("UpdateSaving")]
		private static void UpdateSavingPrefix(float dt)
		{
			_savePlayerProfileTimer += dt;
		}

		[HarmonyPostfix]
		[HarmonyPatch("UpdateSaving")]
		private static void UpdateSavingPostfix(Game __instance)
		{
			if (PluginConfig.IsModEnabled.Value && !(_savePlayerProfileTimer <= 0f) && !(_savePlayerProfileTimer < (float)PluginConfig.SavePlayerProfileInterval.Value))
			{
				if (PluginConfig.ShowMessageOnModSave.Value)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "Saving player profile...", 0, (Sprite)null, false);
				}
				_savePlayerProfileTimer = 0f;
				__instance.SavePlayerProfile(PluginConfig.SetLogoutPointOnSave.Value);
			}
		}
	}
	[HarmonyPatch(typeof(Player))]
	internal static class PlayerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("OnDeath")]
		private static void OnDeathPostfix(ref Player __instance)
		{
			Game.instance.m_playerProfile.ClearLoguoutPoint();
		}
	}
}