Decompiled source of WorldDumper v0.3.3

WorldDumper.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.SceneManagement;
using WorldDumper.Dumpers;
using WorldDumper.Formats;
using WorldDumper.Jsonl;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("WorldDumper")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Log everything in White Knuckle world.")]
[assembly: AssemblyFileVersion("0.3.3.0")]
[assembly: AssemblyInformationalVersion("0.3.3+18613ea1425ed91ea1b5352fc07f89875bdee887")]
[assembly: AssemblyProduct("WorldDumper")]
[assembly: AssemblyTitle("WorldDumper")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.3.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;
		}
	}
}
[Serializable]
public class Position3
{
	public float x = tr.position.x;

	public float y = tr.position.y;

	public float z = tr.position.z;

	public Position3(Transform tr)
	{
	}//IL_0002: 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_0024: Unknown result type (might be due to invalid IL or missing references)

}
namespace WorldDumper
{
	[BepInPlugin("shishyando.WK.WorldDumper", "WorldDumper", "0.3.3")]
	public class WorldDumperPlugin : BaseUnityPlugin
	{
		internal static WorldDumperPlugin Instance;

		internal static ManualLogSource Beep;

		private Harmony _harmony;

		public static bool Playing;

		public static ConfigEntry<string> LogsDir;

		public static ConfigEntry<bool> LogGameObjectIds;

		private void Awake()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			Instance = this;
			Beep = ((BaseUnityPlugin)this).Logger;
			LogsDir = ((BaseUnityPlugin)this).Config.Bind<string>("Logging", "Directory", Path.Combine(Paths.BepInExRootPath, "WorldDumperOutput"), "Directory for WorldDumper logs");
			LogGameObjectIds = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "LogGameObjectIds", false, "If true, WorldDumper will dump GameObject.InstanceID and GameObject.SiblingIdx for game object formats");
			_harmony = new Harmony("shishyando.WK.WorldDumper");
			Beep.LogInfo((object)"shishyando.WK.WorldDumper is loaded");
			SceneManager.sceneUnloaded += OnSceneUnloaded;
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		public void OnSceneLoaded(Scene s, LoadSceneMode m)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			Beep.LogInfo((object)$"OnSceneLoaded: {((Scene)(ref s)).name} (mode: {m})");
			if (((Scene)(ref s)).name == "Game-Main")
			{
				Start();
			}
		}

		public void OnSceneUnloaded(Scene s)
		{
			Beep.LogInfo((object)("OnSceneUnloaded: " + ((Scene)(ref s)).name));
			if (((Scene)(ref s)).name == "Game-Main")
			{
				Stop();
			}
		}

		public static void Start()
		{
			if (TryToRotateLogs())
			{
				Playing = true;
				Instance._harmony.PatchAll();
			}
		}

		public static void Stop()
		{
			Playing = false;
			Instance._harmony.UnpatchSelf();
			Jsonler.DisposeWriters();
		}

		public static bool TryToRotateLogs()
		{
			Jsonler.DisposeWriters();
			try
			{
				string text = Path.Combine(Paths.BepInExRootPath, LogsDir.Value + "_prev");
				if (Directory.Exists(text))
				{
					Directory.Delete(text, recursive: true);
				}
				if (!Directory.Exists(text))
				{
					Directory.CreateDirectory(text);
				}
				DirectoryInfo directoryInfo = Directory.CreateDirectory(LogsDir.Value);
				foreach (FileInfo item in directoryInfo.EnumerateFiles())
				{
					item.MoveTo(Path.Combine(text, item.Name));
				}
				return true;
			}
			catch (Exception arg)
			{
				Beep.LogError((object)$"Recreating logs directory failed: {arg}");
				Stop();
				return false;
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "shishyando.WK.WorldDumper";

		public const string PLUGIN_NAME = "WorldDumper";

		public const string PLUGIN_VERSION = "0.3.3";
	}
}
namespace WorldDumper.Patches
{
	[HarmonyPatch(typeof(App_PerkPage), "Start")]
	public static class App_PerkPage_Start_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(App_PerkPage __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				PerkPageDumper.Dump(__instance, "App_PerkPage_Start");
			}
		}
	}
	[HarmonyPatch(typeof(ENV_VendingMachine), "Start")]
	public static class ENV_VendingMachine_Start_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(ENV_VendingMachine __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				VendingMachineDumper.Dump(__instance, "ENV_VendingMachine_Start");
			}
		}
	}
	[HarmonyPatch(typeof(GameEntity), "Start")]
	public static class GameEntity_Start_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(GameEntity __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				GameEntityDumper.Dump(__instance, "GameEntity_Start");
			}
		}
	}
	[HarmonyPatch(typeof(Item_Object), "Start")]
	public static class Item_Object_Start_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(Item_Object __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				ItemObjectDumper.Dump(__instance, "Item_Object_Start");
			}
		}
	}
	[HarmonyPatch(typeof(M_Level), "Initialize")]
	public static class M_Level_Initialize_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(M_Level __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				LevelDumper.Dump(__instance, "M_Level_Initialize");
			}
		}
	}
	[HarmonyPatch(typeof(SessionEvent), "StartEvent")]
	public static class SessionEvent_StartEvent_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(SessionEvent __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				SessionEventDumper.Dump(__instance, "SessionEvent_StartEvent");
			}
		}
	}
	[HarmonyPatch(typeof(UT_SpawnChance), "Start")]
	public static class UT_SpawnChance_Start_Patcher
	{
		[HarmonyPostfix]
		public static void Dump(UT_SpawnChance __instance)
		{
			if (WorldDumperPlugin.Playing)
			{
				GameObjectDumper.Dump(((Component)__instance).gameObject, "UT_SpawnChance_Start");
			}
		}
	}
}
namespace WorldDumper.Jsonl
{
	public static class Jsonler
	{
		private static readonly ConcurrentDictionary<string, Lazy<TextWriter>> Writers = new ConcurrentDictionary<string, Lazy<TextWriter>>();

		private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
		{
			ReferenceLoopHandling = (ReferenceLoopHandling)1,
			Formatting = (Formatting)0,
			NullValueHandling = (NullValueHandling)0
		};

		public static void Dump<T>(T data, string prefix)
		{
			if (data != null)
			{
				string path = Path.Combine(WorldDumperPlugin.LogsDir.Value, prefix + "_" + typeof(T).Name + ".jsonl");
				string line = JsonConvert.SerializeObject((object)data, SerializerSettings);
				WriteLine(path, line);
			}
		}

		public static void DisposeWriters()
		{
			foreach (KeyValuePair<string, Lazy<TextWriter>> writer in Writers)
			{
				if (writer.Value.IsValueCreated)
				{
					writer.Value.Value.Dispose();
				}
			}
			Writers.Clear();
		}

		private static void WriteLine(string path, string line)
		{
			TextWriter value = Writers.GetOrAdd(path, CreateLazy).Value;
			value.WriteLine(line);
		}

		private static Lazy<TextWriter> CreateLazy(string path)
		{
			return new Lazy<TextWriter>(delegate
			{
				FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
				StreamWriter writer = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), 65536)
				{
					AutoFlush = true,
					NewLine = "\n"
				};
				return TextWriter.Synchronized(writer);
			}, LazyThreadSafetyMode.ExecutionAndPublication);
		}
	}
}
namespace WorldDumper.Formats
{
	[Serializable]
	public class GameEntityFormat
	{
		public string EntityID;

		public string EntityType;

		public string Tag;

		public Position3 Position;

		public LevelFormat Level;

		public GameObjectFormat GameObject;
	}
	[Serializable]
	public class GameObjectFormat
	{
		public int InstanceId;

		public string Name;

		public bool Active;

		public string ParentName;

		public string Path;

		public Position3 Position;

		public int SiblingIdx;
	}
	[Serializable]
	public class ItemObjectFormat
	{
		public string PrefabName;

		public string ItemName;

		public string ItemTag;

		public LevelFormat Level;

		public GameObjectFormat GameObject;
	}
	[Serializable]
	public class LevelFormat
	{
		public int InstanceId;

		public string LevelName;

		public string RegionName;

		public string SubregionName;

		public bool IsLastLevel;

		public bool Flipped;

		public int Seed;

		public bool Active;
	}
	[Serializable]
	public class PerkPageFormat
	{
		public string PerkPageType;

		public List<PerkCardFormat> PerkCards;

		public string Id;

		public GameObjectFormat GameObject;
	}
	[Serializable]
	public class PerkCardFormat
	{
		public string Name;

		public PerkFormat PerkInfo;
	}
	[Serializable]
	public class PerkFormat
	{
		public string Title;

		public string Description;

		public int Cost;

		public string SpawnPool;
	}
	[Serializable]
	public class SessionEventFormat
	{
		public string Id;

		public string StartCheck;

		public List<string> EventModules;

		public LevelFormat StartLevel;
	}
	[Serializable]
	public class VendingPurchaseFormat
	{
		public string PrefabName;

		public float Chance;

		public int Price;
	}
	[Serializable]
	public class VendingMachineFormat
	{
		public string VendorId;

		public VendingPurchaseFormat[] PurchaseArray;

		public int LocalSeed;

		public bool RandomGeneration;

		public LevelFormat Level;

		public GameObjectFormat GameObject;
	}
}
namespace WorldDumper.Dumpers
{
	public static class AsIsDumper
	{
		public static void Dump<T>(T obj, string preifx)
		{
			Jsonler.Dump(obj, preifx);
		}
	}
	public static class GameEntityDumper
	{
		public static void Dump(GameEntity e, string prefix)
		{
			GameEntityFormat data = new GameEntityFormat
			{
				EntityID = e.entityPrefabID,
				EntityType = e.objectType,
				Tag = ((Component)e).tag,
				Position = new Position3(((Component)e).gameObject.transform),
				Level = LevelDumper.LevelOf(((Component)e).transform),
				GameObject = GameObjectDumper.Get(((Component)e).gameObject)
			};
			Jsonler.Dump(data, prefix + "_" + ((Component)e).tag);
		}
	}
	public static class GameObjectDumper
	{
		public static void Dump(GameObject obj, string prefix)
		{
			Jsonler.Dump(Get(obj), prefix);
		}

		public static GameObjectFormat Get(GameObject obj)
		{
			GameObjectFormat obj2 = new GameObjectFormat
			{
				InstanceId = (WorldDumperPlugin.LogGameObjectIds.Value ? ((Object)obj).GetInstanceID() : 0),
				Name = ((Object)obj).name,
				Active = obj.activeSelf
			};
			Transform parent = obj.transform.parent;
			object obj3;
			if (parent == null)
			{
				obj3 = null;
			}
			else
			{
				GameObject gameObject = ((Component)parent).gameObject;
				obj3 = ((gameObject != null) ? ((Object)gameObject).name : null);
			}
			if (obj3 == null)
			{
				obj3 = "<root>";
			}
			obj2.ParentName = (string)obj3;
			obj2.Path = GetPath(obj.transform);
			obj2.Position = new Position3(obj.transform);
			obj2.SiblingIdx = (WorldDumperPlugin.LogGameObjectIds.Value ? obj.transform.GetSiblingIndex() : 0);
			return obj2;
		}

		private static string GetPath(Transform t)
		{
			StringBuilder stringBuilder = new StringBuilder(((Object)t).name);
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				stringBuilder.Insert(0, ((Object)parent).name + "/");
				parent = parent.parent;
			}
			return stringBuilder.ToString();
		}
	}
	public static class ItemObjectDumper
	{
		public static void Dump(Item_Object obj, string prefix)
		{
			Jsonler.Dump(Get(obj), prefix);
		}

		public static ItemObjectFormat Get(Item_Object it)
		{
			return new ItemObjectFormat
			{
				ItemName = it.itemData.itemName,
				ItemTag = it.itemData.itemTag,
				PrefabName = it.itemData.prefabName,
				GameObject = GameObjectDumper.Get(((Component)it).gameObject),
				Level = LevelDumper.LevelOf(((Component)it).transform)
			};
		}
	}
	public static class LevelDumper
	{
		public static readonly FieldRef<M_Level, bool> flippedRef = AccessTools.FieldRefAccess<M_Level, bool>("flipped");

		public static void Dump(M_Level lvl, string prefix)
		{
			Jsonler.Dump(Get(lvl), prefix);
		}

		public static LevelFormat Get(M_Level lvl)
		{
			return new LevelFormat
			{
				InstanceId = (WorldDumperPlugin.LogGameObjectIds.Value ? ((Object)lvl).GetInstanceID() : 0),
				LevelName = lvl.levelName,
				IsLastLevel = lvl.lastLevel,
				RegionName = (lvl.region?.regionName ?? "no_region_name"),
				SubregionName = (lvl.subRegion?.subregionName ?? "no_subregion_name"),
				Flipped = flippedRef.Invoke(lvl),
				Seed = lvl.GetLevelSeed(),
				Active = ((Component)lvl).gameObject.activeSelf
			};
		}

		public static LevelFormat LevelOf(Transform tr)
		{
			if ((Object)(object)tr == (Object)null)
			{
				return new LevelFormat();
			}
			M_Level componentInParent = ((Component)tr).GetComponentInParent<M_Level>(true);
			return ((Object)(object)componentInParent != (Object)null) ? Get(componentInParent) : new LevelFormat();
		}
	}
	public static class PerkPageDumper
	{
		public static readonly FieldRef<App_PerkPage, List<App_PerkPage_Card>> cardsRef = AccessTools.FieldRefAccess<App_PerkPage, List<App_PerkPage_Card>>("cards");

		public static readonly FieldRef<App_PerkPage, string> idRef = AccessTools.FieldRefAccess<App_PerkPage, string>("id");

		public static void Dump(App_PerkPage page, string prefix)
		{
			PerkPageFormat perkPageFormat = new PerkPageFormat();
			perkPageFormat.PerkPageType = "perkPageType";
			perkPageFormat.PerkCards = cardsRef.Invoke(page).ConvertAll(ConvertPerkCard);
			perkPageFormat.Id = idRef.Invoke(page);
			perkPageFormat.GameObject = GameObjectDumper.Get(((Component)page).gameObject);
			PerkPageFormat data = perkPageFormat;
			Jsonler.Dump(data, prefix);
		}

		public static PerkCardFormat ConvertPerkCard(App_PerkPage_Card card)
		{
			return new PerkCardFormat
			{
				Name = ((Object)card).name,
				PerkInfo = GetPerkInfo(card.perk)
			};
		}

		public static PerkFormat GetPerkInfo(Perk perk)
		{
			return new PerkFormat
			{
				Title = perk.title,
				Description = perk.description,
				Cost = perk.cost,
				SpawnPool = "spawnPool"
			};
		}
	}
	public static class SessionEventDumper
	{
		public static readonly FieldRef<SessionEvent, M_Level> startLevelRef = AccessTools.FieldRefAccess<SessionEvent, M_Level>("startLevel");

		public static void Dump(SessionEvent e, string prefix)
		{
			SessionEventFormat data = new SessionEventFormat
			{
				Id = e.id,
				StartCheck = "startCheck",
				EventModules = e.modules.ConvertAll((SessionEventModule x) => x.name),
				StartLevel = LevelDumper.Get(startLevelRef.Invoke(e))
			};
			Jsonler.Dump(data, prefix);
		}
	}
	public static class VendingMachineDumper
	{
		public static readonly FieldRef<ENV_VendingMachine, int> localSeedRef = AccessTools.FieldRefAccess<ENV_VendingMachine, int>("localSeed");

		public static void Dump(ENV_VendingMachine vendo, string prefix)
		{
			VendingMachineFormat vendingMachineFormat = new VendingMachineFormat();
			vendingMachineFormat.VendorId = vendo.vendorId;
			vendingMachineFormat.PurchaseArray = Array.ConvertAll(vendo.buttons, GetPurchase);
			vendingMachineFormat.LocalSeed = localSeedRef.Invoke(vendo);
			vendingMachineFormat.RandomGeneration = vendo.randomGeneration;
			vendingMachineFormat.Level = LevelDumper.LevelOf(((Component)vendo).transform);
			vendingMachineFormat.GameObject = GameObjectDumper.Get(((Component)vendo).gameObject);
			VendingMachineFormat data = vendingMachineFormat;
			Jsonler.Dump(data, prefix);
		}

		public static VendingPurchaseFormat GetPurchase(VendingButton button)
		{
			return new VendingPurchaseFormat
			{
				PrefabName = ((Object)button.purchase.itemObject).name,
				Chance = button.purchase.chance,
				Price = button.purchase.price
			};
		}
	}
}