Decompiled source of OdinHorse HU v1.0.1

BepInEx/plugins/SeHun-OdinHorseAdapter/SeHun.OdinHorseAdapter.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using BepInEx;
using BepInEx.Bootstrap;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SeHun.Core;
using SeHunTranslationLoader;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SeHun.OdinHorseAdapter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("SeHun.OdinHorseAdapter")]
[assembly: AssemblyTitle("SeHun.OdinHorseAdapter")]
[assembly: AssemblyVersion("0.1.0.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 SeHun.OdinHorseAdapter
{
	[BepInPlugin("sehun.odinhorse.adapter", "SeHun OdinHorse Adapter TEST", "0.1.0")]
	[BepInDependency("sehun.translationloader", "0.6.0")]
	[BepInDependency("Raelaziel.OdinHorse", "1.9.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "sehun.odinhorse.adapter";

		private Adapter adapter;

		private void Awake()
		{
			try
			{
				Assembly assembly = ((object)Chainloader.PluginInfos["Raelaziel.OdinHorse"].Instance).GetType().Assembly;
				adapter = new Adapter(assembly);
				SeHunPlugin.Adapters.Register((ITranslationAdapter)(object)adapter);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"OdinHorseAdapter 0.1.0 REGISTERED; waiting for Loader dispatch (valheim.odinhorse).");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("OdinHorseAdapter DISABLED: " + ex));
			}
		}

		private void OnDestroy()
		{
			adapter?.Stop();
		}
	}
	public sealed class Adapter : ITranslationAdapter
	{
		private readonly Assembly odin;

		private Harmony harmony;

		private static Bridge bridge;

		private static Action<string> warning;

		private static bool faulted;

		public string Id => "valheim.odinhorse";

		public Adapter(Assembly odin)
		{
			this.odin = odin;
		}

		public void Load(Package package, Action<string> info, Action<string> warn)
		{
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Expected O, but got Unknown
			if (harmony != null)
			{
				throw new InvalidOperationException("Only one OdinHorse package is supported.");
			}
			if (package.SchemaVersion != 1 || package.Game != "valheim" || package.Id != "OdinHorse" || package.Adapter != Id || package.Language != "Hungarian")
			{
				throw new InvalidDataException("Expected OdinHorse / valheim.odinhorse / Hungarian schema 1 package.");
			}
			RequireHash(odin.Location, "4618EA520BC1B51407E09F46D34D8F6E03F8D1B67291BB3BE90AE27A9179664A");
			RequireHash(typeof(SeHunPlugin).Assembly.Location, "586771DECBAD1CA1A4E1282483A05CDEDBA1DF7A0E4FD48FFAF4B944654AA9C9");
			RequireHash(typeof(ITranslationAdapter).Assembly.Location, "C483573FA7751A2FE6FAEB795F21ACF5B7A4E39B9C53A344DD38968157E545DA");
			Dictionary<string, string> dictionary = Packages.ReadTranslations(package.FilePath);
			Type type = odin.GetType("LocalizationManager.Localizer", throwOnError: true);
			MethodInfo method = type.GetMethod("UpdatePlaceholderText", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(Localization),
				typeof(string)
			}, null);
			if (method == null || method.ReturnType != typeof(void))
			{
				throw new MissingMethodException("OdinHorse UpdatePlaceholderText(Localization,string) not found.");
			}
			ConditionalWeakTable<Localization, string> conditionalWeakTable = type.GetField("localizationLanguage", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as ConditionalWeakTable<Localization, string>;
			List<WeakReference<Localization>> list = type.GetField("localizationObjects", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as List<WeakReference<Localization>>;
			if (conditionalWeakTable == null || list == null)
			{
				throw new InvalidOperationException("OdinHorse localization tracking unavailable.");
			}
			Bridge bridge = new Bridge(dictionary, conditionalWeakTable);
			Harmony val = new Harmony("sehun.odinhorse.adapter");
			try
			{
				warning = warn;
				faulted = false;
				Adapter.bridge = bridge;
				val.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(Adapter), "AfterOriginalWord", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				int num = 0;
				WeakReference<Localization>[] array = list.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i].TryGetTarget(out var target))
					{
						num += bridge.RestoreAll(target);
					}
				}
				harmony = val;
				info("OdinHorseAdapter 0.1.0 ACTIVE via Loader; keys=" + dictionary.Count + "; catch-up writes=" + num + "; only tracked Hungarian instances; English cache untouched.");
			}
			catch
			{
				val.UnpatchSelf();
				Adapter.bridge = null;
				throw;
			}
		}

		private static void AfterOriginalWord(Localization __0, string __1)
		{
			if (bridge == null || faulted)
			{
				return;
			}
			try
			{
				bridge.Restore(__0, __1);
			}
			catch (Exception ex)
			{
				faulted = true;
				warning?.Invoke("OdinHorseAdapter DISABLED for this session after error: " + ex);
			}
		}

		public void Stop()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			harmony = null;
			bridge = null;
		}

		public static void RequireHash(string path, string expected)
		{
			using FileStream inputStream = File.OpenRead(path);
			using SHA256 sHA = SHA256.Create();
			if (BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "") != expected)
			{
				throw new InvalidOperationException("Unsupported DLL hash: " + Path.GetFileName(path));
			}
		}
	}
	public sealed class Bridge
	{
		private readonly Dictionary<string, string> translations;

		private readonly ConditionalWeakTable<Localization, string> languages;

		private readonly FieldInfo words = typeof(Localization).GetField("m_translations", BindingFlags.Instance | BindingFlags.NonPublic);

		private readonly MethodInfo addWord = typeof(Localization).GetMethod("AddWord", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2]
		{
			typeof(string),
			typeof(string)
		}, null);

		public Bridge(Dictionary<string, string> translations, ConditionalWeakTable<Localization, string> languages)
		{
			if (words == null || words.FieldType != typeof(Dictionary<string, string>) || addWord == null)
			{
				throw new MissingMemberException("Valheim Localization contract changed.");
			}
			if (translations == null || translations.Count == 0 || languages == null)
			{
				throw new InvalidDataException("Missing translations/language tracking.");
			}
			foreach (KeyValuePair<string, string> translation in translations)
			{
				if (string.IsNullOrWhiteSpace(translation.Key) || translation.Key.StartsWith("$") || translation.Value == null)
				{
					throw new InvalidDataException("Invalid translation key/value.");
				}
			}
			this.translations = new Dictionary<string, string>(translations, StringComparer.Ordinal);
			this.languages = languages;
		}

		public bool Restore(Localization target, string key)
		{
			if (target == null || key == null || !languages.TryGetValue(target, out var value) || value != "Hungarian" || !translations.TryGetValue(key, out var value2))
			{
				return false;
			}
			if (!(words.GetValue(target) is Dictionary<string, string> dictionary) || !dictionary.TryGetValue(key, out var value3) || value3 == value2)
			{
				return false;
			}
			addWord.Invoke(target, new object[2] { key, value2 });
			return true;
		}

		public int RestoreAll(Localization target)
		{
			int num = 0;
			foreach (string key in translations.Keys)
			{
				if (Restore(target, key))
				{
					num++;
				}
			}
			return num;
		}
	}
}