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();
}
}
}