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