using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Local Map Markers")]
[assembly: AssemblyDescription("Local custom map markers with per-world persistence for Muck")]
[assembly: AssemblyCompany("PigeonsMods")]
[assembly: AssemblyProduct("Local Map Markers")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: ComVisible(false)]
[assembly: TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("0.0.4.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 PigeonsMods.LocalMapMarkers
{
[BepInPlugin("com.pigeonsmods.localmapmarkers", "Local Map Markers", "1.0.2")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class LocalMapMarkersPlugin : BaseUnityPlugin
{
private enum MarkerKind
{
House,
Cave,
Village,
Revive,
Boss,
Generic
}
private sealed class LocalMarker
{
public string RecordId { get; private set; }
public GameObject Visual { get; private set; }
public RectTransform RectTransform { get; private set; }
public LocalMarker(string recordId, GameObject visual, RectTransform rectTransform)
{
RecordId = recordId;
Visual = visual;
RectTransform = rectTransform;
}
}
public const string PluginGuid = "com.pigeonsmods.localmapmarkers";
public const string PluginName = "Local Map Markers";
public const string PluginVersion = "1.0.2";
private TMP_Text[] allTexts;
private const float DragThresholdPixels = 8f;
private const float RemovalRadiusPixels = 28f;
private const float MarkerVisualScale = 0.68f;
private readonly List<LocalMarker> markers = new List<LocalMarker>();
private readonly List<MarkerRecord> markerRecords = new List<MarkerRecord>();
private readonly Texture2D[] markerTextures = (Texture2D[])(object)new Texture2D[6];
private MarkerStorage markerStorage;
private Map currentMap;
private int currentMapInstanceId;
private int currentMapTextureInstanceId;
private string currentWorldKey;
private bool markerViewsHydrated;
private bool persistenceDirty;
private MarkerKind selectedKind;
private bool leftClickArmed;
private Vector2 leftButtonDownPosition;
private GameObject selectorRoot;
private RectTransform selectorRect;
private RawImage selectorIcon;
private GameObject hintTextPanel;
private TMP_Text selectorText;
private GameObject clonedHudText;
public static bool sessionWasLoaded;
private Harmony harmony;
private bool persistenceEnabled;
private string currentSavePath;
private bool checkedFirstSave;
private DateTime sessionStartTime;
private float nextSaveCheckTime;
private bool baselineFileExists;
private DateTime baselineLastWriteTime;
private long baselineLength;
private void Awake()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
try
{
MuckSaveGameIntegration.Initialize(((BaseUnityPlugin)this).Logger);
harmony = new Harmony("com.pigeonsmods.localmapmarkers");
harmony.PatchAll(Assembly.GetExecutingAssembly());
markerTextures[0] = LoadEmbeddedTexture("house");
markerTextures[1] = LoadEmbeddedTexture("cave");
markerTextures[2] = LoadEmbeddedTexture("village");
markerTextures[3] = LoadEmbeddedTexture("revive");
markerTextures[4] = LoadEmbeddedTexture("boss");
markerTextures[5] = LoadEmbeddedTexture("generic");
markerStorage = new MarkerStorage(Path.Combine(Paths.BepInExRootPath, "LocalMapMarkersData", "v2"), ((BaseUnityPlugin)this).Logger);
RunOrphanCleanup();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Local Map Markers 1.0.2 loaded with Harmony patches. No networking is active.");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Could not load local map markers: " + ex));
((Behaviour)this).enabled = false;
}
}
private void LateUpdate()
{
Map instance = Map.Instance;
int num = (((Object)(object)instance != (Object)null) ? ((Object)instance).GetInstanceID() : 0);
if (num != currentMapInstanceId)
{
SwitchToMap(instance, num);
}
if ((Object)(object)currentMap == (Object)null)
{
CancelPendingClick();
return;
}
DetectWorldMapChange();
TryBindCurrentWorld();
if (MuckSaveGameIntegration.IsInstalled && !persistenceEnabled && !checkedFirstSave && !string.IsNullOrEmpty(currentSavePath) && Time.time >= nextSaveCheckTime)
{
nextSaveCheckTime = Time.time + 3f;
CheckForFirstSave();
}
bool active = currentMap.active;
if ((Object)(object)selectorRoot != (Object)null)
{
selectorRoot.SetActive(active);
}
if (!active)
{
CancelPendingClick();
return;
}
HydrateMarkerViews();
HandleSelectionKeys();
HandlePointerInput();
RemoveDestroyedMarkerReferences();
}
private void OnDestroy()
{
CancelPendingClick();
SaveMarkersIfDirty();
DestroySelector();
ClearRuntimeMarkers();
markerRecords.Clear();
if (markerStorage != null)
{
markerStorage.Unbind();
}
if (harmony != null)
{
harmony.UnpatchSelf();
}
for (int i = 0; i < markerTextures.Length; i++)
{
if ((Object)(object)markerTextures[i] != (Object)null)
{
Object.Destroy((Object)(object)markerTextures[i]);
markerTextures[i] = null;
}
}
}
private void SwitchToMap(Map map, int mapInstanceId)
{
CancelPendingClick();
RunFinalSaveCheck();
SaveMarkersIfDirty();
DestroySelector();
ClearRuntimeMarkers();
markerRecords.Clear();
currentWorldKey = null;
persistenceDirty = false;
if (markerStorage != null)
{
markerStorage.Unbind();
}
currentMap = map;
currentMapInstanceId = mapInstanceId;
currentMapTextureInstanceId = GetCurrentMapTextureInstanceId();
if ((Object)(object)currentMap != (Object)null)
{
CreateSelector();
}
}
private void DetectWorldMapChange()
{
int num = GetCurrentMapTextureInstanceId();
if (num != 0 && currentMapTextureInstanceId != 0 && num != currentMapTextureInstanceId)
{
CancelPendingClick();
ClearRuntimeMarkers();
markerViewsHydrated = false;
}
if (num != 0)
{
currentMapTextureInstanceId = num;
}
}
private int GetCurrentMapTextureInstanceId()
{
if ((Object)(object)currentMap == (Object)null || (Object)(object)currentMap.mapTextureMaterial == (Object)null)
{
return 0;
}
Texture mainTexture = currentMap.mapTextureMaterial.mainTexture;
if (!((Object)(object)mainTexture != (Object)null))
{
return 0;
}
return ((Object)mainTexture).GetInstanceID();
}
private void TryBindCurrentWorld()
{
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: 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_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Expected I4, but got Unknown
if (markerStorage == null || (Object)(object)currentMap == (Object)null || (Object)(object)currentMap.map == (Object)null || GetCurrentMapTextureInstanceId() == 0)
{
return;
}
Rect rect = currentMap.map.rect;
if (((Rect)(ref rect)).width <= 1f)
{
return;
}
rect = currentMap.map.rect;
if (((Rect)(ref rect)).height <= 1f || GameManager.gameSettings == null)
{
return;
}
int seed = GameManager.GetSeed();
string text = string.Format(CultureInfo.InvariantCulture, "seed={0}|mode={1}|chunk={2}|scale={3}", seed, (int)GameManager.gameSettings.gameMode, MapGenerator.mapChunkSize, MapGenerator.worldScale);
string text2;
if (MuckSaveGameIntegration.IsInstalled)
{
if (sessionWasLoaded)
{
persistenceEnabled = true;
currentSavePath = MuckSaveGameIntegration.GetSelectedSavePath();
text2 = "v2|" + text + "|save=persistent";
}
else
{
persistenceEnabled = false;
currentSavePath = MuckSaveGameIntegration.GetPathForSeed(seed);
text2 = "v2|" + text + "|save=session";
}
}
else
{
persistenceEnabled = false;
currentSavePath = null;
text2 = "v2|" + text + "|save=session";
}
if (string.Equals(text2, currentWorldKey, StringComparison.Ordinal))
{
return;
}
SaveMarkersIfDirty();
ClearRuntimeMarkers();
markerRecords.Clear();
IList<MarkerRecord> list;
if (persistenceEnabled)
{
if (!markerStorage.PersistentExists(text2, seed))
{
string text3 = Path.Combine(Paths.BepInExRootPath, "LocalMapMarkersData", "v1");
string text4 = "v1|" + text;
if (Directory.Exists(text3))
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Attempting v1 to v2 migration for key: " + text4));
list = markerStorage.MigrateFromV1(text3, text4, text2, seed);
}
else
{
list = markerStorage.Bind(text2, seed);
}
}
else
{
list = markerStorage.Bind(text2, seed);
}
}
else
{
list = markerStorage.BindSession(text2);
InitializeFirstSaveBaseline();
}
for (int i = 0; i < list.Count; i++)
{
markerRecords.Add(list[i]);
}
currentWorldKey = text2;
markerViewsHydrated = false;
persistenceDirty = false;
if (markerRecords.Count > 0)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded " + markerRecords.Count + " map marker(s) for the current session."));
}
}
private void HydrateMarkerViews()
{
if (markerViewsHydrated || string.IsNullOrEmpty(currentWorldKey) || !CanCreateMarkerViews())
{
return;
}
for (int i = 0; i < markerRecords.Count; i++)
{
MarkerRecord markerRecord = markerRecords[i];
if (!HasVisualForRecord(markerRecord.Id))
{
CreateMarkerView(markerRecord);
}
}
markerViewsHydrated = true;
}
private bool HasVisualForRecord(string recordId)
{
for (int i = 0; i < markers.Count; i++)
{
if (markers[i] != null && markers[i].RecordId == recordId && (Object)(object)markers[i].Visual != (Object)null)
{
return true;
}
}
return false;
}
private bool CanCreateMarkerViews()
{
//IL_0052: 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_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)currentMap != (Object)null && (Object)(object)currentMap.map != (Object)null && (Object)(object)currentMap.markerParent != (Object)null && (Object)(object)currentMap.mapMarkerPrefab != (Object)null)
{
Rect rect = currentMap.map.rect;
if (((Rect)(ref rect)).width > 1f)
{
rect = currentMap.map.rect;
return ((Rect)(ref rect)).height > 1f;
}
}
return false;
}
private void HandleSelectionKeys()
{
if (Input.GetKeyDown((KeyCode)49) || Input.GetKeyDown((KeyCode)257))
{
SelectMarker(MarkerKind.House);
}
else if (Input.GetKeyDown((KeyCode)50) || Input.GetKeyDown((KeyCode)258))
{
SelectMarker(MarkerKind.Cave);
}
else if (Input.GetKeyDown((KeyCode)51) || Input.GetKeyDown((KeyCode)259))
{
SelectMarker(MarkerKind.Village);
}
else if (Input.GetKeyDown((KeyCode)52) || Input.GetKeyDown((KeyCode)260))
{
SelectMarker(MarkerKind.Revive);
}
else if (Input.GetKeyDown((KeyCode)53) || Input.GetKeyDown((KeyCode)261))
{
SelectMarker(MarkerKind.Boss);
}
else if (Input.GetKeyDown((KeyCode)54) || Input.GetKeyDown((KeyCode)262))
{
SelectMarker(MarkerKind.Generic);
}
}
private void SelectMarker(MarkerKind kind)
{
selectedKind = kind;
if ((Object)(object)selectorIcon != (Object)null)
{
selectorIcon.texture = (Texture)(object)markerTextures[(int)selectedKind];
}
}
private void HandlePointerInput()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: 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_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
Vector2 val = Vector2.op_Implicit(Input.mousePosition);
if (Input.GetMouseButtonDown(0))
{
leftClickArmed = IsPointerInsideMap(val) && !IsPointerInsideSelector(val);
leftButtonDownPosition = val;
}
if (Input.GetMouseButtonUp(0))
{
bool num = leftClickArmed && Vector2.Distance(leftButtonDownPosition, val) <= 8f && IsPointerInsideMap(val) && !IsPointerInsideSelector(val);
leftClickArmed = false;
if (num)
{
PlaceMarker(val);
}
}
if (Input.GetMouseButtonDown(1) && IsPointerInsideMap(val) && !IsPointerInsideSelector(val))
{
RemoveNearestMarker(val);
}
}
private bool IsPointerInsideMap(Vector2 screenPosition)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)currentMap != (Object)null && (Object)(object)currentMap.map != (Object)null)
{
return RectTransformUtility.RectangleContainsScreenPoint(currentMap.map, screenPosition, (Camera)null);
}
return false;
}
private bool IsPointerInsideSelector(Vector2 screenPosition)
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)selectorRect != (Object)null && (Object)(object)selectorRoot != (Object)null && selectorRoot.activeInHierarchy)
{
return RectTransformUtility.RectangleContainsScreenPoint(selectorRect, screenPosition, (Camera)null);
}
return false;
}
private void PlaceMarker(Vector2 screenPosition)
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: 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_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
Vector2 val = default(Vector2);
if (CanCreateMarkerViews() && !string.IsNullOrEmpty(currentWorldKey) && markerRecords.Count < 500 && RectTransformUtility.ScreenPointToLocalPointInRectangle(currentMap.map, screenPosition, (Camera)null, ref val))
{
Rect rect = currentMap.map.rect;
float normalizedX = Mathf.Clamp01((val.x - ((Rect)(ref rect)).xMin) / ((Rect)(ref rect)).width);
float normalizedY = Mathf.Clamp01((val.y - ((Rect)(ref rect)).yMin) / ((Rect)(ref rect)).height);
MarkerRecord markerRecord = new MarkerRecord(Guid.NewGuid().ToString("N"), (int)selectedKind, normalizedX, normalizedY);
if (CreateMarkerView(markerRecord))
{
markerRecords.Add(markerRecord);
MarkPersistenceDirtyAndSave();
}
}
}
private bool CreateMarkerView(MarkerRecord record)
{
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: 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)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_014a: Unknown result type (might be due to invalid IL or missing references)
if (!CanCreateMarkerViews() || record == null || record.Kind < 0 || record.Kind >= markerTextures.Length || (Object)(object)markerTextures[record.Kind] == (Object)null)
{
return false;
}
Rect rect = currentMap.map.rect;
Vector2 val = default(Vector2);
((Vector2)(ref val))..ctor(Mathf.Lerp(((Rect)(ref rect)).xMin, ((Rect)(ref rect)).xMax, record.NormalizedX), Mathf.Lerp(((Rect)(ref rect)).yMin, ((Rect)(ref rect)).yMax, record.NormalizedY));
GameObject val2 = Object.Instantiate<GameObject>(currentMap.mapMarkerPrefab, currentMap.markerParent);
((Object)val2).name = "LocalMapMarker." + (MarkerKind)record.Kind/*cast due to .constrained prefix*/;
Transform transform = val2.transform;
transform.localPosition = new Vector3(val.x, val.y, 0f);
transform.localRotation = Quaternion.identity;
transform.localScale *= 0.68f;
RawImage component = val2.GetComponent<RawImage>();
if ((Object)(object)component == (Object)null)
{
Object.Destroy((Object)(object)val2);
((BaseUnityPlugin)this).Logger.LogWarning((object)"The game's map marker prefab no longer contains a RawImage; marker was not created.");
return false;
}
component.texture = (Texture)(object)markerTextures[record.Kind];
((Graphic)component).color = Color.white;
((Graphic)component).raycastTarget = false;
for (int i = 0; i < transform.childCount; i++)
{
((Component)transform.GetChild(i)).gameObject.SetActive(false);
}
val2.SetActive(true);
markers.Add(new LocalMarker(record.Id, val2, ((Graphic)component).rectTransform));
return true;
}
private void RemoveNearestMarker(Vector2 screenPosition)
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: 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)
int num = -1;
float num2 = 28f;
for (int i = 0; i < markers.Count; i++)
{
LocalMarker localMarker = markers[i];
if (localMarker != null && !((Object)(object)localMarker.RectTransform == (Object)null))
{
Vector2 val = RectTransformUtility.WorldToScreenPoint((Camera)null, ((Transform)localMarker.RectTransform).position);
float num3 = Vector2.Distance(screenPosition, val);
if (num3 <= num2)
{
num2 = num3;
num = i;
}
}
}
if (num < 0)
{
return;
}
LocalMarker localMarker2 = markers[num];
markers.RemoveAt(num);
if (localMarker2 != null && (Object)(object)localMarker2.Visual != (Object)null)
{
Object.Destroy((Object)(object)localMarker2.Visual);
}
bool flag = false;
if (localMarker2 != null)
{
for (int num4 = markerRecords.Count - 1; num4 >= 0; num4--)
{
if (string.Equals(markerRecords[num4].Id, localMarker2.RecordId, StringComparison.Ordinal))
{
markerRecords.RemoveAt(num4);
flag = true;
break;
}
}
}
if (flag)
{
MarkPersistenceDirtyAndSave();
}
}
private void RemoveDestroyedMarkerReferences()
{
bool flag = false;
for (int num = markers.Count - 1; num >= 0; num--)
{
LocalMarker localMarker = markers[num];
if (localMarker == null || (Object)(object)localMarker.Visual == (Object)null)
{
markers.RemoveAt(num);
flag = true;
}
}
if (flag && markers.Count < markerRecords.Count)
{
markerViewsHydrated = false;
}
}
private void ClearRuntimeMarkers()
{
for (int i = 0; i < markers.Count; i++)
{
LocalMarker localMarker = markers[i];
if (localMarker != null && (Object)(object)localMarker.Visual != (Object)null)
{
Object.Destroy((Object)(object)localMarker.Visual);
}
}
markers.Clear();
markerViewsHydrated = false;
}
private void MarkPersistenceDirtyAndSave()
{
persistenceDirty = true;
SaveMarkersIfDirty();
}
private void SaveMarkersIfDirty()
{
if (persistenceDirty && markerStorage != null && markerStorage.IsBound && !string.IsNullOrEmpty(currentWorldKey) && markerStorage.Save(markerRecords))
{
persistenceDirty = false;
}
}
private void CancelPendingClick()
{
leftClickArmed = false;
}
private void CreateSelector()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected O, but got Unknown
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0156: Unknown result type (might be due to invalid IL or missing references)
//IL_018b: Unknown result type (might be due to invalid IL or missing references)
//IL_0191: Expected O, but got Unknown
//IL_01af: Unknown result type (might be due to invalid IL or missing references)
//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
//IL_022c: Unknown result type (might be due to invalid IL or missing references)
//IL_0267: Unknown result type (might be due to invalid IL or missing references)
//IL_026d: Expected O, but got Unknown
//IL_028c: Unknown result type (might be due to invalid IL or missing references)
//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0326: Unknown result type (might be due to invalid IL or missing references)
//IL_03af: Unknown result type (might be due to invalid IL or missing references)
//IL_0481: Unknown result type (might be due to invalid IL or missing references)
//IL_04c5: Unknown result type (might be due to invalid IL or missing references)
//IL_04da: Unknown result type (might be due to invalid IL or missing references)
//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
//IL_0504: Unknown result type (might be due to invalid IL or missing references)
//IL_050f: Unknown result type (might be due to invalid IL or missing references)
//IL_051a: Unknown result type (might be due to invalid IL or missing references)
//IL_0547: Unknown result type (might be due to invalid IL or missing references)
//IL_0565: Unknown result type (might be due to invalid IL or missing references)
//IL_0576: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)currentMap == (Object)null || (Object)(object)currentMap.mapParent == (Object)null)
{
return;
}
selectorRoot = new GameObject("LocalMapMarkers.SelectedIcon", new Type[2]
{
typeof(RectTransform),
typeof(RawImage)
});
selectorRect = selectorRoot.GetComponent<RectTransform>();
if ((Object)(object)selectorRect == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogError((object)"selectorRect is null after creating GameObject with RectTransform!");
return;
}
((Transform)selectorRect).SetParent(currentMap.mapParent, false);
selectorRect.anchorMin = new Vector2(0f, 1f);
selectorRect.anchorMax = new Vector2(0f, 1f);
selectorRect.pivot = new Vector2(0f, 1f);
selectorRect.anchoredPosition = new Vector2(18f, -18f);
selectorRect.sizeDelta = new Vector2(54f, 54f);
((Transform)selectorRect).localScale = Vector3.one;
RawImage component = selectorRoot.GetComponent<RawImage>();
component.texture = (Texture)(object)Texture2D.whiteTexture;
((Graphic)component).color = new Color(0.08f, 0.05f, 0.025f, 0.78f);
((Graphic)component).raycastTarget = false;
GameObject val = new GameObject("Icon", new Type[2]
{
typeof(RectTransform),
typeof(RawImage)
});
RectTransform component2 = val.GetComponent<RectTransform>();
((Transform)component2).SetParent((Transform)(object)selectorRect, false);
component2.anchorMin = new Vector2(0.5f, 0.5f);
component2.anchorMax = new Vector2(0.5f, 0.5f);
component2.pivot = new Vector2(0.5f, 0.5f);
component2.anchoredPosition = Vector2.zero;
component2.sizeDelta = new Vector2(44f, 44f);
selectorIcon = val.GetComponent<RawImage>();
selectorIcon.texture = (Texture)(object)markerTextures[(int)selectedKind];
((Graphic)selectorIcon).color = Color.white;
((Graphic)selectorIcon).raycastTarget = false;
GameObject val2 = new GameObject("TextPanel", new Type[2]
{
typeof(RectTransform),
typeof(RawImage)
});
RectTransform component3 = val2.GetComponent<RectTransform>();
((Transform)component3).SetParent((Transform)(object)selectorRect, false);
component3.anchorMin = new Vector2(0f, 0.5f);
component3.anchorMax = new Vector2(0f, 0.5f);
component3.pivot = new Vector2(0f, 0.5f);
component3.anchoredPosition = new Vector2(53f, 0f);
component3.sizeDelta = new Vector2(200f, 44f);
((Transform)component3).localScale = Vector3.one;
((Transform)component3).localRotation = Quaternion.identity;
RawImage component4 = val2.GetComponent<RawImage>();
component4.texture = (Texture)(object)Texture2D.whiteTexture;
((Graphic)component4).color = new Color(0.08f, 0.05f, 0.025f, 0.78f);
((Graphic)component4).raycastTarget = false;
TMP_Text val3 = null;
GameObject val4 = null;
allTexts = (TMP_Text[])(object)Object.FindObjectsOfTypeAll(typeof(TMP_Text));
TMP_Text[] array = allTexts;
foreach (TMP_Text val5 in array)
{
GameObject gameObject = ((Component)val5).gameObject;
Canvas component5 = gameObject.GetComponent<Canvas>();
if ((Object)(object)component5 == (Object)null)
{
if ((Object)(object)gameObject.GetComponent<RectTransform>() != (Object)null)
{
gameObject.GetComponent<CanvasGroup>();
_ = ((Object)gameObject).name;
val3 = val5;
val4 = gameObject;
break;
}
}
else if ((int)component5.renderMode == 0 && (Object)(object)gameObject.GetComponent<RectTransform>() != (Object)null)
{
_ = ((Object)gameObject).name;
val3 = val5;
val4 = gameObject;
break;
}
}
if ((Object)(object)val3 != (Object)null && (Object)(object)val4 != (Object)null)
{
if ((Object)(object)selectorRect != (Object)null && (Object)(object)val2 != (Object)null)
{
clonedHudText = Object.Instantiate<GameObject>(val4, val2.transform, false);
((Object)clonedHudText).name = "ControlHints_Clone";
}
else
{
((BaseUnityPlugin)this).Logger.LogError((object)"ERROR: selectorRect or hintTextPanel is null at Instantiate! Cannot clone HUD text.");
}
TMP_Text component6 = clonedHudText.GetComponent<TMP_Text>();
if ((Object)(object)component6 != (Object)null)
{
component6.text = "1–6 Select • LMB Place • LMB Drag Map • RMB Remove • Wheel Zoom";
component6.fontSize = 16f;
((Graphic)component6).color = Color.white;
component6.alignment = (TextAlignmentOptions)513;
component6.enableWordWrapping = false;
component6.overflowMode = (TextOverflowModes)0;
((Behaviour)component6).enabled = true;
RectTransform component7 = clonedHudText.GetComponent<RectTransform>();
component7.anchorMin = new Vector2(0f, 0.5f);
component7.anchorMax = new Vector2(0f, 0.5f);
component7.pivot = new Vector2(0f, 0.5f);
component7.anchoredPosition = new Vector2(14f, 1f);
((Transform)component7).localScale = Vector3.one;
((Transform)component7).localRotation = Quaternion.identity;
clonedHudText.transform.SetAsLastSibling();
float x = component6.GetPreferredValues(component6.text, float.PositiveInfinity, 44f).x;
float num = 28f;
component3.sizeDelta = new Vector2(x + num, 44f);
component7.sizeDelta = new Vector2(x, 44f);
}
else
{
((BaseUnityPlugin)this).Logger.LogError((object)"Cloned GameObject has no TMP_Text component!");
}
}
else
{
((BaseUnityPlugin)this).Logger.LogError((object)"No HUD text found to clone!");
}
selectorRoot.transform.SetAsLastSibling();
selectorRoot.SetActive(currentMap.active);
}
private void DestroySelector()
{
selectorIcon = null;
selectorText = null;
selectorRect = null;
hintTextPanel = null;
if ((Object)(object)clonedHudText != (Object)null)
{
Object.Destroy((Object)(object)clonedHudText);
clonedHudText = null;
}
if ((Object)(object)selectorRoot != (Object)null)
{
Object.Destroy((Object)(object)selectorRoot);
selectorRoot = null;
}
}
private static Texture2D LoadEmbeddedTexture(string assetName)
{
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
string text = "LocalMapMarkers.Assets." + assetName + ".png";
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(text);
if (stream == null)
{
throw new InvalidOperationException("Embedded resource not found: " + text);
}
byte[] array = new byte[stream.Length];
int num;
for (int i = 0; i < array.Length; i += num)
{
num = stream.Read(array, i, array.Length - i);
if (num <= 0)
{
throw new EndOfStreamException("Could not read embedded resource: " + text);
}
}
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
((Object)val).name = "LocalMapMarkers." + assetName;
((Texture)val).wrapMode = (TextureWrapMode)1;
((Texture)val).filterMode = (FilterMode)1;
if (!DecodePng(val, array))
{
Object.Destroy((Object)(object)val);
throw new InvalidOperationException("Unity could not decode embedded texture: " + text);
}
return val;
}
private static bool DecodePng(Texture2D texture, byte[] data)
{
Type? type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule");
if (type == null)
{
throw new InvalidOperationException("Unity's ImageConversion module is unavailable.");
}
MethodInfo? method = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
{
typeof(Texture2D),
typeof(byte[]),
typeof(bool)
}, null);
if (method == null)
{
throw new MissingMethodException("UnityEngine.ImageConversion.LoadImage was not found.");
}
object obj = method.Invoke(null, new object[3] { texture, data, true });
if (obj is bool)
{
return (bool)obj;
}
return false;
}
private void InitializeFirstSaveBaseline()
{
checkedFirstSave = false;
sessionStartTime = DateTime.UtcNow;
nextSaveCheckTime = 0f;
if (currentSavePath == null)
{
return;
}
try
{
baselineFileExists = File.Exists(currentSavePath);
if (baselineFileExists)
{
baselineLastWriteTime = File.GetLastWriteTimeUtc(currentSavePath);
baselineLength = new FileInfo(currentSavePath).Length;
}
else
{
baselineLastWriteTime = DateTime.MinValue;
baselineLength = 0L;
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to read baseline for " + currentSavePath + ": " + ex.Message));
baselineFileExists = false;
baselineLastWriteTime = DateTime.MinValue;
baselineLength = 0L;
}
}
private void CheckForFirstSave()
{
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Expected I4, but got Unknown
if (checkedFirstSave || persistenceEnabled || string.IsNullOrEmpty(currentSavePath))
{
return;
}
try
{
bool num = File.Exists(currentSavePath);
bool flag = false;
if (num)
{
if (!baselineFileExists)
{
flag = true;
}
else
{
DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(currentSavePath);
long length = new FileInfo(currentSavePath).Length;
if (lastWriteTimeUtc != baselineLastWriteTime || length != baselineLength)
{
flag = true;
}
}
}
if (flag)
{
int seed = GameManager.GetSeed();
if (MuckSaveGameIntegration.IsSaveFileValid(currentSavePath, seed))
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("First save detected and validated! Transitioning to persistent mode for seed: " + seed));
string newWorldKey = string.Format(CultureInfo.InvariantCulture, "v2|seed={0}|mode={1}|chunk={2}|scale={3}|save=persistent", seed, (int)GameManager.gameSettings.gameMode, MapGenerator.mapChunkSize, MapGenerator.worldScale);
markerStorage.BindNewPersistent(newWorldKey, seed);
persistenceEnabled = true;
checkedFirstSave = true;
MarkPersistenceDirtyAndSave();
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Error in CheckForFirstSave (will retry): " + ex.Message));
}
}
private void RunFinalSaveCheck()
{
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Expected I4, but got Unknown
if (!MuckSaveGameIntegration.IsInstalled || persistenceEnabled || checkedFirstSave || string.IsNullOrEmpty(currentSavePath) || GameManager.gameSettings == null)
{
return;
}
try
{
bool num = File.Exists(currentSavePath);
bool flag = false;
if (num)
{
if (!baselineFileExists)
{
flag = true;
}
else
{
DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(currentSavePath);
long length = new FileInfo(currentSavePath).Length;
if (lastWriteTimeUtc != baselineLastWriteTime || length != baselineLength)
{
flag = true;
}
}
}
if (flag)
{
int seed = GameManager.GetSeed();
if (MuckSaveGameIntegration.IsSaveFileValid(currentSavePath, seed))
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Final exit check: Save detected! Persisting markers for seed: " + seed));
string newWorldKey = string.Format(CultureInfo.InvariantCulture, "v2|seed={0}|mode={1}|chunk={2}|scale={3}|save=persistent", seed, (int)GameManager.gameSettings.gameMode, MapGenerator.mapChunkSize, MapGenerator.worldScale);
markerStorage.BindNewPersistent(newWorldKey, seed);
persistenceEnabled = true;
checkedFirstSave = true;
markerStorage.Save(markerRecords);
}
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Error in final save check: " + ex.Message));
}
}
private void RunOrphanCleanup()
{
if (!MuckSaveGameIntegration.IsInstalled)
{
return;
}
try
{
string path = Path.Combine(Paths.BepInExRootPath, "LocalMapMarkersData", "v2");
if (!Directory.Exists(path))
{
return;
}
string savesBasePath = MuckSaveGameIntegration.GetSavesBasePath();
if (string.IsNullOrEmpty(savesBasePath) || !Directory.Exists(savesBasePath))
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Saves directory not found or unreadable. Skipping orphan cleanup.");
return;
}
HashSet<int> existingSaveSeeds = MuckSaveGameIntegration.GetExistingSaveSeeds(((BaseUnityPlugin)this).Logger);
int cleanedCount = 0;
string[] files = Directory.GetFiles(path, "world_*.a.xml");
foreach (string filePath in files)
{
CleanOrphanPair(filePath, existingSaveSeeds, ref cleanedCount);
}
files = Directory.GetFiles(path, "world_*.b.xml");
foreach (string filePath2 in files)
{
CleanOrphanPair(filePath2, existingSaveSeeds, ref cleanedCount);
}
if (cleanedCount > 0)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("Cleaned up " + cleanedCount + " orphaned marker file(s) for deleted saves."));
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to run orphan cleanup: " + ex.Message));
}
}
private void CleanOrphanPair(string filePath, HashSet<int> activeSeeds, ref int cleanedCount)
{
try
{
string[] array = Path.GetFileName(filePath).Split(new char[1] { '_' });
if (array.Length >= 2 && int.TryParse(array[1], out var result) && !activeSeeds.Contains(result))
{
File.Delete(filePath);
string path = filePath + ".tmp";
if (File.Exists(path))
{
File.Delete(path);
}
cleanedCount++;
((BaseUnityPlugin)this).Logger.LogInfo((object)("Removed LocalMapMarkers data for deleted save seed: " + result));
}
}
catch
{
}
}
}
[HarmonyPatch]
internal static class GameLoopStartLoopPatch
{
[HarmonyPatch(typeof(GameLoop), "StartLoop")]
[HarmonyPrefix]
public static void Prefix()
{
if (MuckSaveGameIntegration.IsInstalled)
{
LocalMapMarkersPlugin.sessionWasLoaded = !string.IsNullOrEmpty(MuckSaveGameIntegration.GetSelectedSavePath());
}
else
{
LocalMapMarkersPlugin.sessionWasLoaded = false;
}
}
}
internal sealed class MarkerRecord
{
public string Id { get; private set; }
public int Kind { get; private set; }
public float NormalizedX { get; private set; }
public float NormalizedY { get; private set; }
public MarkerRecord(string id, int kind, float normalizedX, float normalizedY)
{
Id = id;
Kind = kind;
NormalizedX = normalizedX;
NormalizedY = normalizedY;
}
}
internal sealed class MarkerStorage
{
private enum StorageSlot
{
None,
A,
B
}
private enum CandidateReadStatus
{
Missing,
Valid,
Invalid,
FutureVersion
}
private sealed class Candidate
{
public StorageSlot Slot { get; private set; }
public bool IsTemporary { get; private set; }
public long Revision { get; private set; }
public IList<MarkerRecord> Records { get; private set; }
public Candidate(StorageSlot slot, bool isTemporary, long revision, IList<MarkerRecord> records)
{
Slot = slot;
IsTemporary = isTemporary;
Revision = revision;
Records = records;
}
}
public const int MaxMarkers = 500;
private const int CurrentFormatVersion = 1;
private readonly string dataDirectory;
private readonly ManualLogSource logger;
private string worldKey;
private string slotAPath;
private string slotBPath;
private long revision;
private StorageSlot activeSlot;
private bool readOnly;
private bool readOnlyWarningLogged;
private bool diskless;
public bool IsBound => !string.IsNullOrEmpty(worldKey);
public MarkerStorage(string dataDirectory, ManualLogSource logger)
{
this.dataDirectory = dataDirectory;
this.logger = logger;
}
public IList<MarkerRecord> Bind(string newWorldKey, int seed)
{
Unbind();
worldKey = newWorldKey;
string text = "world_" + seed.ToString(CultureInfo.InvariantCulture) + "_" + ComputeSha256(newWorldKey).Substring(0, 16).ToLowerInvariant();
slotAPath = Path.Combine(dataDirectory, text + ".a.xml");
slotBPath = Path.Combine(dataDirectory, text + ".b.xml");
Candidate best = null;
bool futureVersionFound = false;
ReadAndConsider(slotAPath, StorageSlot.A, isTemporary: false, ref best, ref futureVersionFound);
ReadAndConsider(slotBPath, StorageSlot.B, isTemporary: false, ref best, ref futureVersionFound);
ReadAndConsider(slotAPath + ".tmp", StorageSlot.A, isTemporary: true, ref best, ref futureVersionFound);
ReadAndConsider(slotBPath + ".tmp", StorageSlot.B, isTemporary: true, ref best, ref futureVersionFound);
if (futureVersionFound)
{
readOnly = true;
logger.LogWarning((object)"Local Map Markers found data written by a newer mod version. Persistence is read-only for this world so that data is not overwritten.");
}
if (best == null)
{
return new List<MarkerRecord>();
}
revision = best.Revision;
activeSlot = best.Slot;
if (best.IsTemporary)
{
logger.LogWarning((object)"Recovered Local Map Markers data from an interrupted save.");
}
return CloneRecords(best.Records);
}
public bool Save(IList<MarkerRecord> records)
{
if (!IsBound)
{
return false;
}
if (diskless)
{
return true;
}
if (readOnly)
{
if (!readOnlyWarningLogged)
{
logger.LogWarning((object)"Marker changes will remain in memory because this world's data is read-only.");
readOnlyWarningLogged = true;
}
return false;
}
try
{
List<MarkerRecord> list = ValidateAndCloneRecords(records);
long num = checked(revision + 1);
StorageSlot storageSlot = ((activeSlot != StorageSlot.A) ? StorageSlot.A : StorageSlot.B);
string text = ((storageSlot == StorageSlot.A) ? slotAPath : slotBPath);
string text2 = text + ".tmp";
Directory.CreateDirectory(dataDirectory);
WriteCandidate(text2, num, list);
if (TryReadCandidate(text2, storageSlot, isTemporary: true, out var candidate, out var _) != CandidateReadStatus.Valid || candidate == null || candidate.Revision != num || !RecordsEqual(list, candidate.Records))
{
throw new InvalidDataException("The temporary marker file did not pass verification.");
}
CommitTemporaryFile(text2, text);
revision = num;
activeSlot = storageSlot;
return true;
}
catch (Exception ex)
{
logger.LogWarning((object)("Could not persist Local Map Markers data; markers remain active for the current world session. " + ex.Message));
return false;
}
}
public void Unbind()
{
worldKey = null;
slotAPath = null;
slotBPath = null;
revision = 0L;
activeSlot = StorageSlot.None;
readOnly = false;
readOnlyWarningLogged = false;
diskless = false;
}
public IList<MarkerRecord> BindSession(string newWorldKey)
{
Unbind();
worldKey = newWorldKey;
diskless = true;
return new List<MarkerRecord>();
}
public void BindNewPersistent(string newWorldKey, int seed)
{
Unbind();
worldKey = newWorldKey;
string text = "world_" + seed.ToString(CultureInfo.InvariantCulture) + "_" + ComputeSha256(newWorldKey).Substring(0, 16).ToLowerInvariant();
slotAPath = Path.Combine(dataDirectory, text + ".a.xml");
slotBPath = Path.Combine(dataDirectory, text + ".b.xml");
try
{
DeleteFileIfExists(slotAPath);
}
catch
{
}
try
{
DeleteFileIfExists(slotBPath);
}
catch
{
}
try
{
DeleteFileIfExists(slotAPath + ".tmp");
}
catch
{
}
try
{
DeleteFileIfExists(slotBPath + ".tmp");
}
catch
{
}
revision = 0L;
activeSlot = StorageSlot.None;
readOnly = false;
}
public IList<MarkerRecord> MigrateFromV1(string v1Directory, string v1WorldKey, string newWorldKey, int seed)
{
IList<MarkerRecord> list = new MarkerStorage(v1Directory, logger).Bind(v1WorldKey, seed);
BindNewPersistent(newWorldKey, seed);
if (list != null && list.Count > 0)
{
Save(list);
logger.LogInfo((object)("Successfully migrated " + list.Count + " markers from legacy v1 persistence for seed " + seed));
}
return list;
}
public bool PersistentExists(string persistentWorldKey, int seed)
{
string text = "world_" + seed.ToString(CultureInfo.InvariantCulture) + "_" + ComputeSha256(persistentWorldKey).Substring(0, 16).ToLowerInvariant();
string path = Path.Combine(dataDirectory, text + ".a.xml");
string path2 = Path.Combine(dataDirectory, text + ".b.xml");
if (!File.Exists(path))
{
return File.Exists(path2);
}
return true;
}
private static void DeleteFileIfExists(string path)
{
if (File.Exists(path))
{
File.Delete(path);
}
}
private void ReadAndConsider(string path, StorageSlot slot, bool isTemporary, ref Candidate best, ref bool futureVersionFound)
{
Candidate candidate;
string failureReason;
switch (TryReadCandidate(path, slot, isTemporary, out candidate, out failureReason))
{
case CandidateReadStatus.Missing:
return;
case CandidateReadStatus.FutureVersion:
futureVersionFound = true;
return;
case CandidateReadStatus.Invalid:
logger.LogWarning((object)("Ignored invalid marker data file '" + Path.GetFileName(path) + "': " + failureReason));
return;
}
if (best == null || candidate.Revision > best.Revision || (candidate.Revision == best.Revision && best.IsTemporary && !candidate.IsTemporary))
{
best = candidate;
}
}
private CandidateReadStatus TryReadCandidate(string path, StorageSlot slot, bool isTemporary, out Candidate candidate, out string failureReason)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Expected O, but got Unknown
//IL_0041: 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_004e: Expected O, but got Unknown
//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
//IL_01b0: Expected O, but got Unknown
candidate = null;
failureReason = null;
if (!File.Exists(path))
{
return CandidateReadStatus.Missing;
}
try
{
XmlReaderSettings val = new XmlReaderSettings
{
DtdProcessing = (DtdProcessing)0,
XmlResolver = null,
IgnoreComments = true,
IgnoreWhitespace = true,
MaxCharactersInDocument = 1048576L
};
XmlDocument val2 = new XmlDocument
{
XmlResolver = null
};
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
XmlReader val3 = XmlReader.Create((Stream)fileStream, val);
try
{
val2.Load(val3);
}
finally
{
((IDisposable)val3)?.Dispose();
}
}
XmlElement documentElement = val2.DocumentElement;
if (documentElement == null || ((XmlNode)documentElement).Name != "LocalMapMarkers")
{
failureReason = "unexpected root element";
return CandidateReadStatus.Invalid;
}
if (!int.TryParse(documentElement.GetAttribute("formatVersion"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
{
failureReason = "invalid format version";
return CandidateReadStatus.Invalid;
}
if (result > 1)
{
return CandidateReadStatus.FutureVersion;
}
if (!string.Equals(documentElement.GetAttribute("worldKey"), worldKey, StringComparison.Ordinal))
{
failureReason = "world identity does not match";
return CandidateReadStatus.Invalid;
}
if (result != 1)
{
failureReason = "unsupported format version";
return CandidateReadStatus.Invalid;
}
if (!long.TryParse(documentElement.GetAttribute("revision"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) || result2 < 0)
{
failureReason = "invalid revision";
return CandidateReadStatus.Invalid;
}
string attribute = documentElement.GetAttribute("checksum");
if (attribute.Length != 64)
{
failureReason = "invalid checksum";
return CandidateReadStatus.Invalid;
}
List<MarkerRecord> list = new List<MarkerRecord>();
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
foreach (XmlNode childNode in ((XmlNode)documentElement).ChildNodes)
{
object obj = (object)childNode;
XmlElement val4 = (XmlElement)((obj is XmlElement) ? obj : null);
if (val4 != null)
{
if (((XmlNode)val4).Name != "Marker" || list.Count >= 500)
{
failureReason = ((((XmlNode)val4).Name != "Marker") ? "unexpected child element" : "too many markers");
return CandidateReadStatus.Invalid;
}
if (!Guid.TryParseExact(val4.GetAttribute("id"), "N", out var result3))
{
failureReason = "invalid marker id";
return CandidateReadStatus.Invalid;
}
string text = result3.ToString("N");
if (!hashSet.Add(text))
{
failureReason = "duplicate marker id";
return CandidateReadStatus.Invalid;
}
if (!int.TryParse(val4.GetAttribute("kind"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result4) || result4 < 0 || result4 > 5 || !float.TryParse(val4.GetAttribute("x"), NumberStyles.Float, CultureInfo.InvariantCulture, out var result5) || !float.TryParse(val4.GetAttribute("y"), NumberStyles.Float, CultureInfo.InvariantCulture, out var result6) || !IsFiniteUnitValue(result5) || !IsFiniteUnitValue(result6))
{
failureReason = "invalid marker values";
return CandidateReadStatus.Invalid;
}
list.Add(new MarkerRecord(text, result4, result5, result6));
}
}
string b = ComputePayloadChecksum(result2, worldKey, list);
if (!string.Equals(attribute, b, StringComparison.OrdinalIgnoreCase))
{
failureReason = "checksum mismatch";
return CandidateReadStatus.Invalid;
}
candidate = new Candidate(slot, isTemporary, result2, list);
return CandidateReadStatus.Valid;
}
catch (Exception ex)
{
failureReason = ex.GetType().Name + ": " + ex.Message;
return CandidateReadStatus.Invalid;
}
}
private void WriteCandidate(string path, long candidateRevision, IList<MarkerRecord> records)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Expected O, but got Unknown
string text = ComputePayloadChecksum(candidateRevision, worldKey, records);
XmlWriterSettings val = new XmlWriterSettings
{
Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
Indent = true,
NewLineChars = "\n",
NewLineHandling = (NewLineHandling)0,
CloseOutput = false
};
using FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
XmlWriter val2 = XmlWriter.Create((Stream)fileStream, val);
try
{
val2.WriteStartDocument();
val2.WriteStartElement("LocalMapMarkers");
val2.WriteAttributeString("formatVersion", 1.ToString(CultureInfo.InvariantCulture));
val2.WriteAttributeString("revision", candidateRevision.ToString(CultureInfo.InvariantCulture));
val2.WriteAttributeString("worldKey", worldKey);
val2.WriteAttributeString("checksum", text);
for (int i = 0; i < records.Count; i++)
{
MarkerRecord markerRecord = records[i];
val2.WriteStartElement("Marker");
val2.WriteAttributeString("id", markerRecord.Id);
val2.WriteAttributeString("kind", markerRecord.Kind.ToString(CultureInfo.InvariantCulture));
val2.WriteAttributeString("x", markerRecord.NormalizedX.ToString("R", CultureInfo.InvariantCulture));
val2.WriteAttributeString("y", markerRecord.NormalizedY.ToString("R", CultureInfo.InvariantCulture));
val2.WriteEndElement();
}
val2.WriteEndElement();
val2.WriteEndDocument();
val2.Flush();
fileStream.Flush(flushToDisk: true);
}
finally
{
((IDisposable)val2)?.Dispose();
}
}
private static void CommitTemporaryFile(string temporaryPath, string targetPath)
{
if (!File.Exists(targetPath))
{
File.Move(temporaryPath, targetPath);
return;
}
try
{
File.Replace(temporaryPath, targetPath, null, ignoreMetadataErrors: true);
}
catch (PlatformNotSupportedException)
{
ReplaceInactiveSlotByMove(temporaryPath, targetPath);
}
catch (IOException)
{
ReplaceInactiveSlotByMove(temporaryPath, targetPath);
}
}
private static void ReplaceInactiveSlotByMove(string temporaryPath, string targetPath)
{
File.Delete(targetPath);
File.Move(temporaryPath, targetPath);
}
private List<MarkerRecord> ValidateAndCloneRecords(IList<MarkerRecord> records)
{
if (records == null || records.Count > 500)
{
throw new InvalidDataException("Marker count is outside the supported range.");
}
List<MarkerRecord> list = new List<MarkerRecord>(records.Count);
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
for (int i = 0; i < records.Count; i++)
{
MarkerRecord markerRecord = records[i];
if (markerRecord == null || !Guid.TryParseExact(markerRecord.Id, "N", out var result) || markerRecord.Kind < 0 || markerRecord.Kind > 5 || !IsFiniteUnitValue(markerRecord.NormalizedX) || !IsFiniteUnitValue(markerRecord.NormalizedY))
{
throw new InvalidDataException("A marker contains unsupported values.");
}
string text = result.ToString("N");
if (!hashSet.Add(text))
{
throw new InvalidDataException("Duplicate marker id.");
}
list.Add(new MarkerRecord(text, markerRecord.Kind, markerRecord.NormalizedX, markerRecord.NormalizedY));
}
return list;
}
private static IList<MarkerRecord> CloneRecords(IList<MarkerRecord> records)
{
List<MarkerRecord> list = new List<MarkerRecord>(records.Count);
for (int i = 0; i < records.Count; i++)
{
MarkerRecord markerRecord = records[i];
list.Add(new MarkerRecord(markerRecord.Id, markerRecord.Kind, markerRecord.NormalizedX, markerRecord.NormalizedY));
}
return list;
}
private static bool RecordsEqual(IList<MarkerRecord> left, IList<MarkerRecord> right)
{
if (left == null || right == null || left.Count != right.Count)
{
return false;
}
for (int i = 0; i < left.Count; i++)
{
MarkerRecord markerRecord = left[i];
MarkerRecord markerRecord2 = right[i];
if (!string.Equals(markerRecord.Id, markerRecord2.Id, StringComparison.Ordinal) || markerRecord.Kind != markerRecord2.Kind || markerRecord.NormalizedX != markerRecord2.NormalizedX || markerRecord.NormalizedY != markerRecord2.NormalizedY)
{
return false;
}
}
return true;
}
private static bool IsFiniteUnitValue(float value)
{
if (!float.IsNaN(value) && !float.IsInfinity(value) && value >= 0f)
{
return value <= 1f;
}
return false;
}
private static string ComputePayloadChecksum(long candidateRevision, string candidateWorldKey, IList<MarkerRecord> records)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(1).Append('\n');
stringBuilder.Append(candidateRevision.ToString(CultureInfo.InvariantCulture)).Append('\n');
stringBuilder.Append(candidateWorldKey.Length.ToString(CultureInfo.InvariantCulture)).Append(':').Append(candidateWorldKey)
.Append('\n');
stringBuilder.Append(records.Count.ToString(CultureInfo.InvariantCulture)).Append('\n');
for (int i = 0; i < records.Count; i++)
{
MarkerRecord markerRecord = records[i];
stringBuilder.Append(markerRecord.Id).Append('|');
stringBuilder.Append(markerRecord.Kind.ToString(CultureInfo.InvariantCulture)).Append('|');
stringBuilder.Append(markerRecord.NormalizedX.ToString("R", CultureInfo.InvariantCulture)).Append('|');
stringBuilder.Append(markerRecord.NormalizedY.ToString("R", CultureInfo.InvariantCulture)).Append('\n');
}
return ComputeSha256(stringBuilder.ToString());
}
private static string ComputeSha256(string value)
{
byte[] bytes = Encoding.UTF8.GetBytes(value);
byte[] array;
using (SHA256 sHA = SHA256.Create())
{
array = sHA.ComputeHash(bytes);
}
StringBuilder stringBuilder = new StringBuilder(array.Length * 2);
for (int i = 0; i < array.Length; i++)
{
stringBuilder.Append(array[i].ToString("X2", CultureInfo.InvariantCulture));
}
return stringBuilder.ToString();
}
}
internal static class MuckSaveGameIntegration
{
public const string MuckSaveGameGuid = "MuckSaveGame.MichMcb";
private static FieldInfo selectedSavePathField;
private static MethodInfo getPathForSeedMethod;
private static MethodInfo getSavesBasePathMethod;
private static bool reflectionFailed;
public static bool IsInstalled => Chainloader.PluginInfos.ContainsKey("MuckSaveGame.MichMcb");
public static void Initialize(ManualLogSource logger)
{
if (!IsInstalled)
{
return;
}
try
{
Assembly assembly = null;
foreach (PluginInfo value in Chainloader.PluginInfos.Values)
{
if (value.Metadata.GUID == "MuckSaveGame.MichMcb")
{
assembly = ((object)value.Instance).GetType().Assembly;
break;
}
}
if (assembly == null)
{
throw new InvalidOperationException("MuckSaveGame assembly not found in Chainloader.");
}
Type type = assembly.GetType("MuckSaveGame.LoadManager");
if (type == null)
{
throw new TypeLoadException("Could not find MuckSaveGame.LoadManager");
}
Type type2 = assembly.GetType("MuckSaveGame.SaveSystem");
if (type2 == null)
{
throw new TypeLoadException("Could not find MuckSaveGame.SaveSystem");
}
selectedSavePathField = type.GetField("selectedSavePath", BindingFlags.Static | BindingFlags.Public);
if (selectedSavePathField == null)
{
throw new MissingFieldException("Could not find selectedSavePath field");
}
getPathForSeedMethod = type2.GetMethod("GetPathForSeed", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(int) }, null);
if (getPathForSeedMethod == null)
{
throw new MissingMethodException("Could not find GetPathForSeed method");
}
getSavesBasePathMethod = type2.GetMethod("GetSavesBasePath", BindingFlags.Static | BindingFlags.Public);
if (getSavesBasePathMethod == null)
{
throw new MissingMethodException("Could not find GetSavesBasePath method");
}
logger.LogInfo((object)"MuckSaveGame integration successfully initialized via reflection.");
}
catch (Exception ex)
{
reflectionFailed = true;
logger.LogError((object)("Failed to initialize MuckSaveGame integration: " + ex));
}
}
public static string GetSelectedSavePath()
{
if (!IsInstalled || reflectionFailed || selectedSavePathField == null)
{
return null;
}
try
{
return selectedSavePathField.GetValue(null) as string;
}
catch
{
return null;
}
}
public static string GetPathForSeed(int seed)
{
if (!IsInstalled || reflectionFailed || getPathForSeedMethod == null)
{
return null;
}
try
{
return getPathForSeedMethod.Invoke(null, new object[1] { seed }) as string;
}
catch
{
return null;
}
}
public static string GetSavesBasePath()
{
if (!IsInstalled || reflectionFailed || getSavesBasePathMethod == null)
{
return null;
}
try
{
return getSavesBasePathMethod.Invoke(null, null) as string;
}
catch
{
return null;
}
}
public static bool IsSaveFileValid(string path, int expectedSeed)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
if (string.IsNullOrEmpty(path) || !File.Exists(path))
{
return false;
}
try
{
using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
XmlDocument val = new XmlDocument();
val.Load((Stream)fileStream);
XmlNode val2 = ((XmlNode)val).SelectSingleNode("/MuckSaveGame/Data[@type='main']/WorldData/Seed");
if (val2 != null && int.TryParse(val2.InnerText, out var result))
{
return result == expectedSeed;
}
}
catch
{
}
return false;
}
public static HashSet<int> GetExistingSaveSeeds(ManualLogSource logger)
{
HashSet<int> hashSet = new HashSet<int>();
if (!IsInstalled || reflectionFailed)
{
return hashSet;
}
try
{
string savesBasePath = GetSavesBasePath();
if (string.IsNullOrEmpty(savesBasePath) || !Directory.Exists(savesBasePath))
{
return hashSet;
}
string[] files = Directory.GetFiles(savesBasePath, "*.mucksave");
for (int i = 0; i < files.Length; i++)
{
if (int.TryParse(Path.GetFileNameWithoutExtension(files[i]), out var result))
{
hashSet.Add(result);
}
}
}
catch (Exception ex)
{
logger.LogWarning((object)("Failed to read MuckSaveGame saves folder: " + ex.Message));
}
return hashSet;
}
}
}