Decompiled source of InFalsusAdvVN v1.0.5

BepInEx/plugins/InFalsusAdvVN/InFalsusAdvVN.dll

Decompiled 15 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using FastText;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Str;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;
using ifapp.Game.Data;
using ifapp.Game.Data.Story;
using ifapp.Game.NovelEngine;
using ifapp.Game.Scenes;
using ifapp.Game.Scenes.Story;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("InFalsusAdvVN")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.5.0")]
[assembly: AssemblyInformationalVersion("1.0.5+deda473ddf76b00640e1ac24515187d787710bed")]
[assembly: AssemblyProduct("InFalsusAdvVN")]
[assembly: AssemblyTitle("InFalsusAdvVN")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.5.0")]
[module: UnverifiableCode]
[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 InFalsusAdvVN
{
	internal sealed class GameBindings
	{
		public IntPtr StringsClass;

		public IntPtr TargetLocalizationField;

		public IntPtr DynamicStringClass;

		public int DynamicStringSize;

		public IntPtr DynamicStringCtor;

		public IntPtr TranslationDetailsClass;

		public int TranslationsOffset;

		public IntPtr StringMappingClass;

		public int MappingSize;

		public int MappingIdOffset;

		public int MappingEnglishOffset;

		public int MappingJapaneseOffset;

		public int MappingKoreanOffset;

		public int MappingChineseTCOffset;

		public int MappingChineseSCOffset;

		public IntPtr StoryStateClass;

		public int StateTextBoxOffset;

		public IntPtr TextBoxStateClass;

		public int CurrentTextOffset;

		public int DisplayCountOffset;

		public int CharacterNameOffset;

		public IntPtr StorySceneClass;

		public int SceneStateOffset;

		public int SceneTextBoxesOffset;

		public int SceneTextIdOffset;

		public int EnglishTextBoxesOffset;

		public int JapaneseTextBoxesOffset;

		public int ChineseTextBoxesOffset;

		public int SceneNameContainerOffset;

		public int SceneChatLogOffset;

		public IntPtr ChatLogEntryClass;

		public int EntryNameOffset;

		public int EntryTextsOffset;

		public int EntryIdOffset;

		public IntPtr NameContainerClass;

		public IntPtr NameContainerSetMethod;

		public IntPtr FastTextClass;

		public IntPtr SetTextMethod;

		public IntPtr SetTextNonLocalizedMethod;

		public IntPtr SetMaxVisibleCharactersMethod;

		public IntPtr GetGameObjectMethod;

		public IntPtr SetActiveMethod;

		public bool Ready { get; private set; }

		public static GameBindings Resolve(Action<string> log, bool dump = false)
		{
			GameBindings gameBindings = new GameBindings();
			List<string> list = new List<string>();
			gameBindings.StringsClass = Il2.FindClass("Str", "Strings");
			if (gameBindings.StringsClass == IntPtr.Zero)
			{
				list.Add("Str.Strings not found");
			}
			else
			{
				gameBindings.TargetLocalizationField = Il2.Field(gameBindings.StringsClass, "targetLocalization");
			}
			gameBindings.DynamicStringClass = Il2.FindClass("Str", "DynamicString");
			if (gameBindings.DynamicStringClass == IntPtr.Zero)
			{
				list.Add("Str.DynamicString not found");
			}
			else
			{
				gameBindings.DynamicStringSize = Il2.ValueSize(gameBindings.DynamicStringClass);
				gameBindings.DynamicStringCtor = FindCtor(gameBindings.DynamicStringClass, 1);
				if (gameBindings.DynamicStringCtor == IntPtr.Zero)
				{
					list.Add("Str.DynamicString..ctor(string) not found");
				}
			}
			gameBindings.TranslationDetailsClass = Il2.FindClass("ifapp.Game.Data.Story", "StoryTranslationDetails");
			if (gameBindings.TranslationDetailsClass == IntPtr.Zero)
			{
				list.Add("StoryTranslationDetails not found");
			}
			else
			{
				IntPtr intPtr = Il2.Field(gameBindings.TranslationDetailsClass, "Translations");
				if (intPtr != IntPtr.Zero)
				{
					gameBindings.TranslationsOffset = Il2.FieldOffset(intPtr);
				}
				if (gameBindings.TranslationsOffset == 0)
				{
					list.Add("StoryTranslationDetails.Translations not found");
				}
				gameBindings.StringMappingClass = Il2.FindClassBySimpleName("StringMapping");
				if (gameBindings.StringMappingClass == IntPtr.Zero)
				{
					list.Add("StoryTranslationDetails.StringMapping not found");
				}
				else
				{
					gameBindings.MappingSize = Il2.ValueSize(gameBindings.StringMappingClass);
					gameBindings.MappingIdOffset = OffsetOf(gameBindings.StringMappingClass, "Id");
					gameBindings.MappingEnglishOffset = OffsetOf(gameBindings.StringMappingClass, "English");
					gameBindings.MappingJapaneseOffset = OffsetOf(gameBindings.StringMappingClass, "Japanese");
					gameBindings.MappingKoreanOffset = OffsetOf(gameBindings.StringMappingClass, "Korean");
					gameBindings.MappingChineseTCOffset = OffsetOf(gameBindings.StringMappingClass, "ChineseTC");
					gameBindings.MappingChineseSCOffset = OffsetOf(gameBindings.StringMappingClass, "ChineseSC");
					if (dump)
					{
						log($"  StringMapping size={gameBindings.MappingSize}, Id=+0x{gameBindings.MappingIdOffset:X}, En=+0x{gameBindings.MappingEnglishOffset:X}, Jp=+0x{gameBindings.MappingJapaneseOffset:X}, Ko=+0x{gameBindings.MappingKoreanOffset:X}, TC=+0x{gameBindings.MappingChineseTCOffset:X}, SC=+0x{gameBindings.MappingChineseSCOffset:X}");
					}
					if (gameBindings.MappingIdOffset < 0 || gameBindings.MappingEnglishOffset < 0 || gameBindings.MappingJapaneseOffset < 0 || gameBindings.MappingChineseTCOffset < 0 || gameBindings.MappingChineseSCOffset < 0)
					{
						list.Add("StringMapping language fields could not be resolved");
					}
				}
			}
			gameBindings.StoryStateClass = Il2.FindClass("ifapp.Game.NovelEngine", "StoryState");
			gameBindings.TextBoxStateClass = Il2.FindClass("ifapp.Game.NovelEngine", "StoryTextBoxState");
			if (gameBindings.StoryStateClass == IntPtr.Zero)
			{
				list.Add("StoryState not found");
			}
			else
			{
				foreach (var (value, text, num) in Il2.ListFields(gameBindings.StoryStateClass))
				{
					if (text.IndexOf("StoryTextBoxState", StringComparison.Ordinal) >= 0)
					{
						gameBindings.StateTextBoxOffset = num;
						log($"  StoryState.{value} (StoryTextBoxState) @ +0x{num:X}");
						break;
					}
				}
				if (gameBindings.StateTextBoxOffset == 0)
				{
					list.Add("StoryState.TextBoxState field not found");
				}
			}
			if (gameBindings.TextBoxStateClass == IntPtr.Zero)
			{
				list.Add("StoryTextBoxState not found");
			}
			else
			{
				foreach (var (text2, _, num2) in Il2.ListFields(gameBindings.TextBoxStateClass))
				{
					switch (text2)
					{
					case "CurrentText":
						gameBindings.CurrentTextOffset = num2;
						break;
					case "CharacterName":
						gameBindings.CharacterNameOffset = num2;
						break;
					case "NumberOfCharacterToDisplay":
						gameBindings.DisplayCountOffset = num2;
						break;
					}
				}
				if (gameBindings.CurrentTextOffset == 0)
				{
					list.Add("StoryTextBoxState.CurrentText not found");
				}
				if (gameBindings.DisplayCountOffset == 0)
				{
					list.Add("StoryTextBoxState.NumberOfCharacterToDisplay not found");
				}
			}
			gameBindings.StorySceneClass = Il2.FindClass("ifapp.Game.Scenes", "StoryScene");
			if (gameBindings.StorySceneClass == IntPtr.Zero)
			{
				list.Add("StoryScene not found");
			}
			else
			{
				List<(string, string, int)> list2 = Il2.ListFields(gameBindings.StorySceneClass);
				if (dump)
				{
					log("  StoryScene fields:");
					foreach (var (value2, value3, value4) in list2)
					{
						log($"    +0x{value4:X4}  {value3}  {value2}");
					}
				}
				foreach (var (text3, text4, num3) in list2)
				{
					if (text4.IndexOf("StoryState", StringComparison.Ordinal) >= 0 && gameBindings.SceneStateOffset == 0)
					{
						gameBindings.SceneStateOffset = num3;
					}
					if (text3 == "storyNameContainer")
					{
						gameBindings.SceneNameContainerOffset = num3;
					}
					if (text3 == "chatLogContainer")
					{
						gameBindings.SceneChatLogOffset = num3;
					}
					switch (text3)
					{
					case "englishText":
						gameBindings.EnglishTextBoxesOffset = num3;
						break;
					case "japaneseText":
						gameBindings.JapaneseTextBoxesOffset = num3;
						break;
					case "chineseText":
						gameBindings.ChineseTextBoxesOffset = num3;
						break;
					}
					if (text4.IndexOf("FastText", StringComparison.Ordinal) >= 0 && text4.EndsWith("[]", StringComparison.Ordinal) && num3 == 720)
					{
						gameBindings.SceneTextBoxesOffset = num3;
					}
					if (text4.IndexOf("String", StringComparison.Ordinal) >= 0 && num3 == 688)
					{
						gameBindings.SceneTextIdOffset = num3;
					}
				}
				log($"  StoryScene renderer sets: english=+0x{gameBindings.EnglishTextBoxesOffset:X} japanese=+0x{gameBindings.JapaneseTextBoxesOffset:X} chinese=+0x{gameBindings.ChineseTextBoxesOffset:X} active=+0x{gameBindings.SceneTextBoxesOffset:X} textId=+0x{gameBindings.SceneTextIdOffset:X}");
				if (gameBindings.SceneStateOffset == 0)
				{
					list.Add("StoryScene story-state field not found");
				}
				if (gameBindings.SceneTextBoxesOffset == 0)
				{
					list.Add("StoryScene active renderer set not found");
				}
				if (gameBindings.EnglishTextBoxesOffset == 0)
				{
					list.Add("StoryScene.englishText not found");
				}
				if (gameBindings.JapaneseTextBoxesOffset == 0)
				{
					list.Add("StoryScene.japaneseText not found");
				}
				if (gameBindings.ChineseTextBoxesOffset == 0)
				{
					list.Add("StoryScene.chineseText not found");
				}
				if (gameBindings.SceneTextIdOffset == 0)
				{
					list.Add("StoryScene last-text-id field not found");
				}
			}
			gameBindings.NameContainerClass = Il2.FindClass("ifapp.Game.Scenes", "StoryNameContainer");
			if (gameBindings.NameContainerClass != IntPtr.Zero)
			{
				foreach (var item4 in Il2.ListMethods(gameBindings.NameContainerClass))
				{
					IntPtr item = item4.Method;
					if (Il2.ParamCount(item) == 1 && Il2.ParamTypeName(item, 0).IndexOf("DynamicString", StringComparison.Ordinal) >= 0)
					{
						gameBindings.NameContainerSetMethod = item;
						break;
					}
				}
				if (dump)
				{
					log($"  StoryNameContainer setter: {((gameBindings.NameContainerSetMethod != IntPtr.Zero) ? "found" : "MISSING")}, scene field +0x{gameBindings.SceneNameContainerOffset:X}");
				}
			}
			else
			{
				log("  StoryNameContainer not found (speaker name will only refresh on the next line)");
			}
			gameBindings.ChatLogEntryClass = Il2.FindClass("ifapp.Game.Scenes.Story", "ChatLogEntryContainer");
			if (gameBindings.ChatLogEntryClass != IntPtr.Zero)
			{
				gameBindings.EntryNameOffset = OffsetOf(gameBindings.ChatLogEntryClass, "characterName");
				gameBindings.EntryTextsOffset = OffsetOf(gameBindings.ChatLogEntryClass, "chatTexts");
				gameBindings.EntryIdOffset = OffsetOf(gameBindings.ChatLogEntryClass, "debug_uniqueId");
				if (dump)
				{
					log($"  ChatLogEntryContainer: name=+0x{gameBindings.EntryNameOffset:X}, texts=+0x{gameBindings.EntryTextsOffset:X}, id=+0x{gameBindings.EntryIdOffset:X}");
				}
			}
			else
			{
				log("  ChatLogEntryContainer not found (LOG panel cannot be re-translated in place).");
			}
			gameBindings.FastTextClass = Il2.FindClass("FastText", "FastText");
			if (gameBindings.FastTextClass == IntPtr.Zero)
			{
				list.Add("FastText not found");
			}
			else
			{
				List<(string, IntPtr)> list3 = Il2.ListMethods(gameBindings.FastTextClass);
				if (dump)
				{
					log("  FastText SetText overloads:");
					foreach (var (text5, method) in list3)
					{
						if (!(text5 != "SetText"))
						{
							string text6 = "";
							for (int i = 0; i < Il2.ParamCount(method); i++)
							{
								text6 = text6 + ((i > 0) ? ", " : "") + Il2.ParamTypeName(method, i);
							}
							log("    SetText(" + text6 + ")");
						}
					}
				}
				foreach (var item5 in list3)
				{
					string item2 = item5.Item1;
					IntPtr item3 = item5.Item2;
					int num4 = Il2.ParamCount(item3);
					if (item2 == "SetText" && num4 == 2 && Il2.ParamTypeName(item3, 0).IndexOf("DynamicString", StringComparison.Ordinal) >= 0)
					{
						gameBindings.SetTextMethod = item3;
					}
					if (item2 == "SetMaxVisibleCharacters" && num4 == 2)
					{
						gameBindings.SetMaxVisibleCharactersMethod = item3;
					}
					if (item2 == "SetTextNonLocalized" && num4 == 2 && Il2.ParamTypeName(item3, 0).IndexOf("String", StringComparison.Ordinal) >= 0)
					{
						gameBindings.SetTextNonLocalizedMethod = item3;
					}
				}
				if (gameBindings.SetTextNonLocalizedMethod == IntPtr.Zero)
				{
					list.Add("FastText.SetTextNonLocalized(string, UpdateType) not found");
				}
				if (gameBindings.SetMaxVisibleCharactersMethod == IntPtr.Zero)
				{
					list.Add("FastText.SetMaxVisibleCharacters not found");
				}
			}
			IntPtr intPtr2 = Il2.FindClass("UnityEngine", "Component");
			if (intPtr2 != IntPtr.Zero)
			{
				gameBindings.GetGameObjectMethod = Il2.Method(intPtr2, "get_gameObject", 0);
			}
			IntPtr intPtr3 = Il2.FindClass("UnityEngine", "GameObject");
			if (intPtr3 != IntPtr.Zero)
			{
				gameBindings.SetActiveMethod = Il2.Method(intPtr3, "SetActive", 1);
			}
			gameBindings.Ready = list.Count == 0;
			foreach (string item6 in list)
			{
				log("  MISSING: " + item6);
			}
			log($"  Str.DynamicString size = {gameBindings.DynamicStringSize}");
			return gameBindings;
		}

		private static int OffsetOf(IntPtr klass, string fieldName)
		{
			IntPtr intPtr = Il2.Field(klass, fieldName);
			if (!(intPtr == IntPtr.Zero))
			{
				return Il2.FieldOffset(intPtr);
			}
			return -1;
		}

		private static IntPtr FindCtor(IntPtr klass, int paramCount)
		{
			IntPtr intPtr = Il2.Method(klass, ".ctor", paramCount);
			if (intPtr != IntPtr.Zero)
			{
				return intPtr;
			}
			foreach (var (text, intPtr2) in Il2.ListMethods(klass))
			{
				if (text == ".ctor" && Il2.ParamCount(intPtr2) == paramCount)
				{
					return intPtr2;
				}
			}
			return IntPtr.Zero;
		}

		public static IntPtr ReadPtr(IntPtr obj, int offset)
		{
			if (obj == IntPtr.Zero || offset <= 0)
			{
				return IntPtr.Zero;
			}
			return Marshal.ReadIntPtr(obj, offset);
		}

		public static int ReadInt(IntPtr obj, int offset)
		{
			if (!(obj == IntPtr.Zero))
			{
				return Marshal.ReadInt32(obj, offset);
			}
			return 0;
		}

		public static void WriteInt(IntPtr obj, int offset, int value)
		{
			if (obj != IntPtr.Zero)
			{
				Marshal.WriteInt32(obj, offset, value);
			}
		}

		public static void WritePtr(IntPtr obj, int offset, IntPtr value)
		{
			if (obj != IntPtr.Zero)
			{
				Marshal.WriteIntPtr(obj, offset, value);
			}
		}

		public static int ArrayLength(IntPtr array)
		{
			if (!(array == IntPtr.Zero))
			{
				return Marshal.ReadInt32(array, 24);
			}
			return 0;
		}

		public static IntPtr ArrayElement(IntPtr array, int index)
		{
			if (!(array == IntPtr.Zero))
			{
				return Marshal.ReadIntPtr(array, 32 + index * IntPtr.Size);
			}
			return IntPtr.Zero;
		}

		public static IntPtr ListItems(IntPtr list)
		{
			return ReadPtr(list, 16);
		}

		public static int ListCount(IntPtr list)
		{
			return ReadInt(list, 24);
		}

		public static void SetLocalization(IntPtr targetLocalizationField, int value)
		{
			if (targetLocalizationField == IntPtr.Zero)
			{
				return;
			}
			IntPtr intPtr = Il2.Alloc(4);
			try
			{
				Marshal.WriteInt32(intPtr, value);
				Il2.FieldStaticSetValue(targetLocalizationField, intPtr);
			}
			finally
			{
				Marshal.FreeHGlobal(intPtr);
			}
		}

		public static int CurrentLocalization(IntPtr targetLocalizationField)
		{
			IntPtr intPtr = Il2.Alloc(4);
			try
			{
				Il2.FieldStaticGetValue(targetLocalizationField, intPtr);
				return Marshal.ReadInt32(intPtr);
			}
			finally
			{
				Marshal.FreeHGlobal(intPtr);
			}
		}
	}
	internal static class Il2
	{
		private const string Dll = "GameAssembly";

		private const CallingConvention Cc = CallingConvention.Cdecl;

		private static IntPtr[] _images;

		private static readonly Dictionary<string, IntPtr> SimpleNameCache = new Dictionary<string, IntPtr>(StringComparer.Ordinal);

		public static IReadOnlyList<string> ImageNames { get; private set; } = Array.Empty<string>();

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_domain_get")]
		private static extern IntPtr DomainGet();

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_domain_get_assemblies")]
		private static extern IntPtr DomainGetAssemblies(IntPtr domain, ref uint size);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_assembly_get_image")]
		private static extern IntPtr AssemblyGetImage(IntPtr assembly);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_image_get_name")]
		private static extern IntPtr ImageGetName(IntPtr image);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_from_name")]
		private static extern IntPtr ClassFromName(IntPtr image, string namespaze, string name);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_image_get_class_count")]
		private static extern uint ImageGetClassCount(IntPtr image);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_image_get_class")]
		private static extern IntPtr ImageGetClass(IntPtr image, int index);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_get_name")]
		private static extern IntPtr ClassGetName(IntPtr klass);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_get_namespace")]
		private static extern IntPtr ClassGetNamespace(IntPtr klass);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_value_size")]
		private static extern uint ClassValueSize(IntPtr klass, out uint align);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_get_fields")]
		private static extern IntPtr ClassGetFields(IntPtr klass, out IntPtr iter);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_get_field_from_name")]
		private static extern IntPtr ClassGetFieldFromName(IntPtr klass, string name);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_field_get_name")]
		private static extern IntPtr FieldGetName(IntPtr field);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_field_get_offset")]
		private static extern int FieldGetOffset(IntPtr field);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_field_get_type")]
		private static extern IntPtr FieldGetType(IntPtr field);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_type_get_name")]
		private static extern IntPtr TypeGetName(IntPtr type);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_get_method_from_name")]
		private static extern IntPtr ClassGetMethodFromName(IntPtr klass, string name, int argCount);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_class_get_methods")]
		private static extern IntPtr ClassGetMethods(IntPtr klass, out IntPtr iter);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_method_get_name")]
		private static extern IntPtr MethodGetName(IntPtr method);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_method_get_param_count")]
		private static extern int MethodGetParamCount(IntPtr method);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_method_get_param")]
		private static extern IntPtr MethodGetParam(IntPtr method, int index);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_object_get_class")]
		internal static extern IntPtr ObjectGetClass(IntPtr obj);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_runtime_invoke")]
		private static extern IntPtr RuntimeInvoke(IntPtr method, IntPtr obj, IntPtr[] parameters, out IntPtr exception);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_object_new")]
		internal static extern IntPtr ObjectNew(IntPtr klass);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_object_unbox")]
		internal static extern IntPtr ObjectUnbox(IntPtr obj);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_string_new_utf16")]
		private static extern IntPtr StringNewUtf16(IntPtr text, int length);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_string_length")]
		internal static extern int StringLength(IntPtr value);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_string_chars")]
		internal static extern IntPtr StringChars(IntPtr value);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_gchandle_new")]
		private static extern uint GcHandleNew(IntPtr obj, bool pinned);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_gchandle_free")]
		private static extern void GcHandleFree(uint handle);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_field_get_value")]
		private static extern void FieldGetValue(IntPtr obj, IntPtr field, IntPtr value);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_field_static_get_value")]
		internal static extern void FieldStaticGetValue(IntPtr field, IntPtr value);

		[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, EntryPoint = "il2cpp_field_static_set_value")]
		internal static extern void FieldStaticSetValue(IntPtr field, IntPtr value);

		public static string ReadAnsi(IntPtr p)
		{
			if (!(p == IntPtr.Zero))
			{
				return Marshal.PtrToStringAnsi(p);
			}
			return null;
		}

		public static void CacheImages()
		{
			if (_images == null)
			{
				IntPtr domain = DomainGet();
				uint size = 0u;
				IntPtr ptr = DomainGetAssemblies(domain, ref size);
				IntPtr[] array = new IntPtr[size];
				List<string> list = new List<string>((int)size);
				for (uint num = 0u; num < size; num++)
				{
					list.Add(ReadAnsi(ImageGetName(array[num] = AssemblyGetImage(Marshal.ReadIntPtr(ptr, (int)num * IntPtr.Size)))));
				}
				_images = array;
				ImageNames = list;
			}
		}

		public static IntPtr FindClass(string namespaze, string name)
		{
			CacheImages();
			IntPtr[] images = _images;
			for (int i = 0; i < images.Length; i++)
			{
				IntPtr intPtr = ClassFromName(images[i], namespaze, name);
				if (intPtr != IntPtr.Zero)
				{
					return intPtr;
				}
			}
			return IntPtr.Zero;
		}

		public static IntPtr FindClassBySimpleName(string simpleName)
		{
			if (SimpleNameCache.TryGetValue(simpleName, out var value))
			{
				return value;
			}
			CacheImages();
			IntPtr[] images = _images;
			foreach (IntPtr image in images)
			{
				uint num = ImageGetClassCount(image);
				for (int j = 0; j < num; j++)
				{
					IntPtr intPtr = ImageGetClass(image, j);
					if (!(intPtr == IntPtr.Zero) && !(ReadAnsi(ClassGetName(intPtr)) != simpleName))
					{
						SimpleNameCache[simpleName] = intPtr;
						return intPtr;
					}
				}
			}
			SimpleNameCache[simpleName] = IntPtr.Zero;
			return IntPtr.Zero;
		}

		public static string ClassName(IntPtr klass)
		{
			if (klass == IntPtr.Zero)
			{
				return "<null>";
			}
			string text = ReadAnsi(ClassGetNamespace(klass));
			string text2 = ReadAnsi(ClassGetName(klass));
			if (!string.IsNullOrEmpty(text))
			{
				return text + "." + text2;
			}
			return text2;
		}

		public static string TypeName(IntPtr type)
		{
			return ReadAnsi(TypeGetName(type));
		}

		public static int ValueSize(IntPtr klass)
		{
			if (klass == IntPtr.Zero)
			{
				return 0;
			}
			uint align;
			return (int)ClassValueSize(klass, out align);
		}

		public static List<(string Name, IntPtr Method)> ListMethods(IntPtr klass)
		{
			List<(string, IntPtr)> list = new List<(string, IntPtr)>();
			if (klass == IntPtr.Zero)
			{
				return list;
			}
			IntPtr iter = IntPtr.Zero;
			IntPtr intPtr;
			while ((intPtr = ClassGetMethods(klass, out iter)) != IntPtr.Zero)
			{
				list.Add((ReadAnsi(MethodGetName(intPtr)), intPtr));
			}
			return list;
		}

		public static int ParamCount(IntPtr method)
		{
			return MethodGetParamCount(method);
		}

		public static string ParamTypeName(IntPtr method, int index)
		{
			return TypeName(MethodGetParam(method, index));
		}

		public static IntPtr ObjectClass(IntPtr obj)
		{
			if (!(obj == IntPtr.Zero))
			{
				return ObjectGetClass(obj);
			}
			return IntPtr.Zero;
		}

		public static int FieldOffset(IntPtr field)
		{
			return FieldGetOffset(field);
		}

		public static IntPtr Field(IntPtr klass, string name)
		{
			return ClassGetFieldFromName(klass, name);
		}

		public static List<(string Name, string Type, int Offset)> ListFields(IntPtr klass)
		{
			List<(string, string, int)> list = new List<(string, string, int)>();
			if (klass == IntPtr.Zero)
			{
				return list;
			}
			IntPtr iter = IntPtr.Zero;
			IntPtr field;
			while ((field = ClassGetFields(klass, out iter)) != IntPtr.Zero)
			{
				list.Add((ReadAnsi(FieldGetName(field)), TypeName(FieldGetType(field)), FieldGetOffset(field)));
			}
			return list;
		}

		public static IntPtr Method(IntPtr klass, string name, int argCount)
		{
			return ClassGetMethodFromName(klass, name, argCount);
		}

		public static IntPtr Invoke(IntPtr method, IntPtr instance, IntPtr[] args, out string error)
		{
			error = null;
			if (method == IntPtr.Zero)
			{
				error = "method not found";
				return IntPtr.Zero;
			}
			IntPtr exception;
			IntPtr result = RuntimeInvoke(method, instance, args, out exception);
			if (exception != IntPtr.Zero)
			{
				error = DescribeException(exception);
				return IntPtr.Zero;
			}
			return result;
		}

		public static string DescribeException(IntPtr exception)
		{
			try
			{
				IntPtr exception2;
				IntPtr intPtr = RuntimeInvoke(Method(FindClass("System", "Object"), "ToString", 0), exception, null, out exception2);
				if (intPtr != IntPtr.Zero)
				{
					return ToManaged(intPtr);
				}
			}
			catch
			{
			}
			return "il2cpp exception";
		}

		public static IntPtr NewString(string value)
		{
			if (value == null)
			{
				return IntPtr.Zero;
			}
			int length = value.Length;
			IntPtr intPtr = Marshal.AllocHGlobal((length == 0) ? 1 : (length * 2));
			try
			{
				for (int i = 0; i < length; i++)
				{
					Marshal.WriteInt16(intPtr, i * 2, (short)value[i]);
				}
				return StringNewUtf16(intPtr, length);
			}
			finally
			{
				Marshal.FreeHGlobal(intPtr);
			}
		}

		public static string ToManaged(IntPtr il2cppString)
		{
			if (il2cppString == IntPtr.Zero)
			{
				return null;
			}
			int num = StringLength(il2cppString);
			if (num <= 0)
			{
				return string.Empty;
			}
			return Marshal.PtrToStringUni(StringChars(il2cppString), num);
		}

		public static uint Pin(IntPtr obj)
		{
			return GcHandleNew(obj, pinned: false);
		}

		public static void Unpin(uint handle)
		{
			if (handle != 0)
			{
				GcHandleFree(handle);
			}
		}

		public unsafe static IntPtr Alloc(int bytes)
		{
			IntPtr intPtr = Marshal.AllocHGlobal(bytes);
			new Span<byte>((void*)intPtr, bytes).Clear();
			return intPtr;
		}
	}
	[BepInPlugin("aria.infalsus.advvn", "InFalsusAdvVN", "1.0.5")]
	public sealed class Plugin : BasePlugin
	{
		internal sealed class LanguageOption
		{
			public static readonly LanguageOption English = new LanguageOption("English", (Localization)1);

			public static readonly LanguageOption Japanese = new LanguageOption("Japanese", (Localization)2);

			public static readonly LanguageOption Korean = new LanguageOption("Korean", (Localization)3);

			public static readonly LanguageOption ChineseSC = new LanguageOption("ChineseSC", (Localization)5);

			public static readonly LanguageOption ChineseTC = new LanguageOption("ChineseTC", (Localization)4);

			public string Name { get; private set; }

			public Localization Localization { get; private set; }

			public LanguageOption(string name, Localization localization)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				Name = name;
				Localization = localization;
			}

			public static LanguageOption Parse(string value)
			{
				switch (value.Trim().ToLowerInvariant())
				{
				case "en":
				case "eng":
				case "english":
					return English;
				case "jp":
				case "ja":
				case "jpn":
				case "japanese":
					return Japanese;
				case "ko":
				case "kr":
				case "korean":
					return Korean;
				case "sc":
				case "zh-hans":
				case "zh_hans":
				case "zhsc":
				case "zh-cn":
				case "chinesesc":
				case "simplified":
				case "chinesesimplified":
					return ChineseSC;
				case "tc":
				case "zh-hant":
				case "zh_hant":
				case "zhtc":
				case "zh-tw":
				case "chinesetc":
				case "traditional":
				case "chinesetraditional":
					return ChineseTC;
				default:
					return null;
				}
			}
		}

		public const string Guid = "aria.infalsus.advvn";

		public const string Version = "1.0.5";

		private const int RendererEnforceFrames = 120;

		private const string SectionLanguage = "LanguageSwitch";

		private const string SectionScroll = "Scroll";

		private static ManualLogSource _log;

		private static ConfigEntry<string> _hotkeyEntry;

		private static ConfigEntry<string> _orderEntry;

		private static ConfigEntry<bool> _verboseEntry;

		private static ConfigEntry<bool> _refreshPhoneEntry;

		private static ConfigEntry<bool> _refreshLogEntry;

		private static ConfigEntry<bool> _rememberEntry;

		private static ConfigEntry<bool> _visibilityEntry;

		private static ConfigEntry<bool> _wheelAdvanceEntry;

		private static ConfigEntry<bool> _wheelOpenLogEntry;

		private static ConfigEntry<bool> _rightClickEntry;

		private static GameBindings _bindings;

		private static StoryTextController _controller;

		private static readonly List<LanguageOption> Languages = new List<LanguageOption>();

		private static int _index = -1;

		private static int _pendingRestore;

		private static bool _faulted;

		private static bool _hotkeyHeld;

		private static Key _hotkey = (Key)26;

		private static StoryScene _activeScene;

		private static int _enforceFrames;

		private static int _frame;

		private static bool _wheelLatched;

		private static int _wheelReportCount;

		private static int _lastGameLocalization = -1;

		private static IntPtr _lastStoryIdPointer;

		private static IntPtr _pendingScenePtr;

		private static int _reapplyFrames;

		private static bool _refreshing;

		internal static int StoryLocalization;

		public override void Load()
		{
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected I4, but got Unknown
			_log = ((BasePlugin)this).Log;
			_hotkeyEntry = ((BasePlugin)this).Config.Bind<string>("LanguageSwitch", "Hotkey", "L", "Key that cycles the story text language. It is only acted on while a story (visual novel) scene is loaded. Unity Input System key name, e.g. L, D, F6, Tab.");
			_orderEntry = ((BasePlugin)this).Config.Bind<string>("LanguageSwitch", "LanguageOrder", "English,ChineseSC,Japanese,ChineseTC", "Languages cycled by the hotkey. Accepted: English, Japanese, Korean, ChineseSC, ChineseTC (or en / jp / ko / zh-hans / zh-hant). 'In Falsus' ships story translations for English, Japanese, Simplified Chinese and Traditional Chinese; Korean falls back to English in the game data.");
			_rememberEntry = ((BasePlugin)this).Config.Bind<bool>("LanguageSwitch", "RememberSelection", true, "Remember the story language selected with the hotkey and re-apply it on the next launch.");
			_wheelAdvanceEntry = ((BasePlugin)this).Config.Bind<bool>("Scroll", "WheelDownAdvances", true, "Mouse wheel down advances the story, like a left click (drives the scene's own input trigger through StoryScene._dp).");
			_rightClickEntry = ((BasePlugin)this).Config.Bind<bool>("Scroll", "RightClickClosesLog", true, "Right click closes the story history (LOG) panel while it is open, like its quit button.");
			_wheelOpenLogEntry = ((BasePlugin)this).Config.Bind<bool>("Scroll", "WheelUpOpensLog", true, "Mouse wheel up opens the story history (LOG) panel, like clicking the LOG button.");
			_visibilityEntry = ((BasePlugin)this).Config.Bind<bool>("LanguageSwitch", "ManageRendererVisibility", false, "Also hide the renderer sets of the other languages after switching. The set for the selected language is always activated; enable this only if leftover text from another language remains visible.");
			_verboseEntry = ((BasePlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Log every resolved story string id and every applied text update.");
			_refreshPhoneEntry = ((BasePlugin)this).Config.Bind<bool>("Advanced", "RefreshPhoneChat", true, "Re-translate an already visible phone chat panel when the language changes. Turn off if it turns out to be involved in a crash.");
			_refreshLogEntry = ((BasePlugin)this).Config.Bind<bool>("Advanced", "RefreshOpenLog", true, "Re-translate an already open LOG panel when the language changes.");
			ParseHotkey(_hotkeyEntry.Value);
			ParseLanguages(_orderEntry.Value);
			if (_rememberEntry.Value)
			{
				string saved = RememberedLanguage.Load();
				if (saved != null)
				{
					int num = Languages.FindIndex((LanguageOption l) => string.Equals(l.Name, saved, StringComparison.OrdinalIgnoreCase));
					if (num >= 0)
					{
						_index = num;
						_pendingRestore = (int)Languages[num].Localization;
					}
				}
			}
			_log.LogInfo((object)("InFalsusAdvVN 1.0.5 loaded. Hotkey = " + ((object)Unsafe.As<Key, Key>(ref _hotkey)/*cast due to .constrained prefix*/).ToString() + " (story mode only), languages = " + string.Join(", ", Languages.ConvertAll((LanguageOption l) => l.Name))));
			ResolveBindings();
			StoryLocalizationPatch.Install(_controller, delegate(string msg)
			{
				_log.LogInfo((object)msg);
			});
			WheelListener.SetLog(delegate(string msg)
			{
				_log.LogInfo((object)msg);
			});
			InjectTickBehaviour();
			if (_pendingRestore != 0)
			{
				StoryLocalization = _pendingRestore;
				_log.LogInfo((object)("Restored remembered story language: " + DescribeLocalization(_pendingRestore)));
				_pendingRestore = 0;
			}
			_log.LogInfo((object)("Game localization currently = " + DescribeLocalization(ProbeLocalization()) + " (the mod never changes it; only story text is overridden)."));
		}

		private static void InjectTickBehaviour()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ClassInjector.RegisterTypeInIl2Cpp<HotkeyBehaviour>();
				GameObject val = new GameObject("InFalsusAdvVN.Hotkey");
				Object.DontDestroyOnLoad((Object)val);
				((Object)val).hideFlags = (HideFlags)61;
				val.AddComponent<HotkeyBehaviour>();
				_log.LogInfo((object)"Hotkey behaviour injected.");
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Could not inject the hotkey behaviour: " + ex));
				_faulted = true;
			}
		}

		private static void ResolveBindings()
		{
			try
			{
				_bindings = GameBindings.Resolve(delegate(string msg)
				{
					_log.LogInfo((object)msg);
				}, _verboseEntry.Value);
				_controller = new StoryTextController(_bindings, _visibilityEntry.Value, delegate(string msg)
				{
					_log.LogInfo((object)msg);
				}, delegate(string msg)
				{
					if (_verboseEntry.Value)
					{
						_log.LogInfo((object)("[debug] " + msg));
					}
				});
				if (!_bindings.Ready)
				{
					_faulted = true;
					_log.LogError((object)"InFalsusAdvVN could not bind to the game's localization API; hotkey disabled.");
				}
				else
				{
					_log.LogInfo((object)"InFalsusAdvVN bindings resolved.");
				}
			}
			catch (Exception ex)
			{
				_faulted = true;
				_log.LogError((object)("Binding resolution threw: " + ex));
			}
		}

		internal static void ReportPatchError(Exception e)
		{
			_faulted = true;
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogError((object)("Story patch failed and is now disabled: " + e));
			}
		}

		internal static void OnFrame()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (_faulted || _controller == null)
			{
				return;
			}
			try
			{
				_frame++;
				if (_frame % 30 == 0)
				{
					TrackGameLanguage();
				}
				if (_enforceFrames > 0)
				{
					_enforceFrames--;
					if ((Object)(object)_activeScene != (Object)null && StoryLocalization != 0)
					{
						_controller.PrepareRendererSet(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)_activeScene), StoryLocalization, hideOthers: false);
					}
				}
				Keyboard current = Keyboard.current;
				if (current != null)
				{
					KeyControl val = current[_hotkey];
					bool flag = val != null && ((ButtonControl)val).isPressed;
					if (flag && !_hotkeyHeld)
					{
						_hotkeyHeld = true;
						HandleHotkey();
					}
					else if (!flag)
					{
						_hotkeyHeld = false;
					}
				}
			}
			catch (Exception ex)
			{
				_faulted = true;
				_log.LogError((object)("Hotkey handling failed and is now disabled: " + ex));
			}
		}

		internal static void OnLateFrame()
		{
			if (_faulted || _controller == null)
			{
				return;
			}
			try
			{
				PollMouseWheel();
				PollRightClick();
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Late wheel poll failed: " + ex));
			}
		}

		internal static void OnStoryEvent(StoryScene scene)
		{
			if (!_faulted && _controller != null && !((Object)(object)scene == (Object)null))
			{
				IntPtr scene2 = IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)scene);
				_activeScene = scene;
				IntPtr intPtr = _controller.ReadCurrentStoryStringIdPointer(scene2);
				if (intPtr != IntPtr.Zero)
				{
					_lastStoryIdPointer = intPtr;
				}
			}
		}

		private static void HandleHotkey()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected I4, but got Unknown
			StoryScene val = FindStoryScene();
			if (!((Object)(object)val == (Object)null))
			{
				_activeScene = val;
				IntPtr intPtr = IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)val);
				if (_index < 0)
				{
					SyncIndexWithGame();
				}
				_index = (_index + 1) % Languages.Count;
				LanguageOption languageOption = Languages[_index];
				int localization = (StoryLocalization = (int)languageOption.Localization);
				_enforceFrames = 120;
				_log.LogInfo((object)("Story language -> " + languageOption.Name));
				if (_rememberEntry.Value)
				{
					RememberedLanguage.Save(languageOption.Name);
				}
				if (_verboseEntry.Value)
				{
					_log.LogInfo((object)("[debug] " + _controller.DescribeScene(intPtr)));
				}
				RefreshCurrentLine(intPtr, localization, "hotkey");
				Step("line refresh");
				if (_controller.RefreshSpeakerName(intPtr, localization))
				{
					_log.LogInfo((object)"Speaker name refreshed.");
				}
				Step("speaker name");
				if (_refreshLogEntry.Value)
				{
					_controller.RefreshOpenLog(intPtr);
				}
				Step("log refresh");
				if (_refreshPhoneEntry.Value)
				{
					_controller.RefreshOpenPhoneChat();
				}
				Step("phone chat refresh");
			}
		}

		private static void Step(string name)
		{
			if (_verboseEntry != null && _verboseEntry.Value)
			{
				_log.LogInfo((object)("[debug] step: " + name + " ok"));
			}
		}

		private static void RefreshCurrentLine(IntPtr scenePtr, int localization, string reason)
		{
			_refreshing = true;
			try
			{
				string text = _controller.ReadCurrentStoryStringId(scenePtr);
				if (string.IsNullOrEmpty(text))
				{
					if (reason == "hotkey")
					{
						_log.LogInfo((object)"No story line is registered on screen yet; the change applies from the next line.");
					}
					return;
				}
				_log.LogInfo((object)("Refreshing line '" + text + "' (" + reason + ") -> " + DescribeLocalization(localization) + "..."));
				string text2 = _controller.LookupTranslation(text, localization);
				if (string.IsNullOrEmpty(text2))
				{
					_log.LogWarning((object)("No " + DescribeLocalization(localization) + " text for this line in the game data."));
					return;
				}
				if (_verboseEntry.Value)
				{
					_log.LogInfo((object)("[debug] resolved " + text2.Length + " chars: " + text2));
				}
				bool flag = _controller.ReapplyVisible(scenePtr, text2, localization);
				_log.LogInfo((object)(flag ? "On-screen text refreshed." : "Text resolved but nothing was written to the screen (see log)."));
			}
			finally
			{
				_refreshing = false;
			}
		}

		private static void TrackGameLanguage()
		{
			int gameLocalization = _controller.GameLocalization;
			if (gameLocalization == 0)
			{
				return;
			}
			if (_lastGameLocalization < 0)
			{
				_lastGameLocalization = gameLocalization;
			}
			else if (gameLocalization != _lastGameLocalization)
			{
				_lastGameLocalization = gameLocalization;
				if (StoryLocalization != 0)
				{
					StoryLocalization = 0;
					_log.LogInfo((object)("Game language changed to " + DescribeLocalization(gameLocalization) + "; story text follows the game setting again."));
				}
			}
		}

		private static void PollMouseWheel()
		{
			if (_faulted || _controller == null)
			{
				return;
			}
			if (!WheelListener.Installed && _frame % 60 == 0 && (Object)(object)FindStoryScene() != (Object)null)
			{
				WheelListener.InstallLate();
			}
			if (!_wheelAdvanceEntry.Value && !_wheelOpenLogEntry.Value)
			{
				return;
			}
			int devices;
			string report;
			float num = ReadWheelDelta(out devices, out report);
			if (num == 0f)
			{
				_wheelLatched = false;
			}
			else
			{
				if (_wheelLatched)
				{
					return;
				}
				_wheelLatched = true;
				if (_wheelReportCount < 25)
				{
					_wheelReportCount++;
					_log.LogInfo((object)("[debug] wheel delta=" + num + " devices=" + devices + " " + report));
				}
				StoryScene val = FindStoryScene();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				IntPtr scene = IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)val);
				if (_controller.IsLogOpen(scene))
				{
					return;
				}
				if (num < 0f && _wheelAdvanceEntry.Value)
				{
					Step("wheel: advance begin");
					if (_controller.AdvanceStory(scene))
					{
						_log.LogInfo((object)"Wheel down -> next line.");
					}
					Step("wheel: advance done");
				}
				else if (num > 0f && _wheelOpenLogEntry.Value)
				{
					Step("wheel: open log begin");
					if (_controller.OpenLog(scene))
					{
						_log.LogInfo((object)"Wheel up -> LOG panel.");
					}
					Step("wheel: open log done");
				}
			}
		}

		private static void PollRightClick()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			if (_faulted || _controller == null || _rightClickEntry == null || !_rightClickEntry.Value)
			{
				return;
			}
			bool flag = false;
			try
			{
				Mouse current = Mouse.current;
				flag = current != null && current.rightButton != null && current.rightButton.wasPressedThisFrame;
			}
			catch
			{
			}
			int num = WheelListener.ConsumeRightClicks();
			if (!flag && num == 0)
			{
				return;
			}
			if (_verboseEntry != null && _verboseEntry.Value)
			{
				ManualLogSource log = _log;
				bool flag2 = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(44, 2, ref flag2);
				if (flag2)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[debug] right click: deviceEdge=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(flag);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" eventEdges=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
				}
				log.LogInfo(val);
			}
			StoryScene val2 = FindStoryScene();
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			IntPtr scene = IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)val2);
			bool flag3 = _controller.IsLogOpen(scene);
			if (_verboseEntry != null && _verboseEntry.Value)
			{
				_log.LogInfo((object)("[debug] right click: logOpen=" + flag3));
			}
			if (flag3)
			{
				bool flag4 = _controller.CloseLog(scene);
				if (_verboseEntry != null && _verboseEntry.Value)
				{
					_log.LogInfo((object)("[debug] right click: CloseLog=" + flag4));
				}
				if (flag4)
				{
					_log.LogInfo((object)"Right click -> LOG panel closed.");
				}
			}
		}

		private static float ReadWheelDelta(out int devices, out string report)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			devices = 0;
			report = "";
			float num = WheelListener.Consume();
			if (num != 0f)
			{
				report = "[event=" + num + "]";
				return num;
			}
			try
			{
				float y = Input.mouseScrollDelta.y;
				if (y != 0f)
				{
					report = "[legacy=" + y + "]";
					return y;
				}
			}
			catch
			{
			}
			float num2 = 0f;
			try
			{
				ReadOnlyArray<InputDevice> all = InputDevice.all;
				devices = all.Count;
				for (int i = 0; i < all.Count; i++)
				{
					InputDevice val = all[i];
					if (val == null)
					{
						continue;
					}
					Mouse val2 = ((Il2CppObjectBase)val).TryCast<Mouse>();
					if (val2 != null && val2.scroll != null)
					{
						float y2 = ((InputControl<Vector2>)(object)val2.scroll).ReadValue().y;
						if (report.Length < 200)
						{
							report = report + "[" + i + ":" + ((InputControl)val2).name + "=" + y2 + "]";
						}
						if (y2 != 0f && num2 == 0f)
						{
							num2 = y2;
						}
					}
				}
			}
			catch
			{
			}
			return num2;
		}

		private static StoryScene FindStoryScene()
		{
			try
			{
				Object val = Object.FindObjectOfType(Il2CppType.Of<StoryScene>());
				if (val == (Object)null)
				{
					return null;
				}
				return ((Il2CppObjectBase)val).TryCast<StoryScene>();
			}
			catch
			{
				return null;
			}
		}

		private static void SyncIndexWithGame()
		{
			int current = _controller.GameLocalization;
			int num = Languages.FindIndex((LanguageOption l) => (int)l.Localization == current);
			_index = ((num >= 0) ? num : (-1));
		}

		private static int ProbeLocalization()
		{
			try
			{
				IntPtr intPtr = Il2.FindClass("Str", "Strings");
				if (intPtr == IntPtr.Zero)
				{
					return 0;
				}
				IntPtr intPtr2 = Il2.Field(intPtr, "targetLocalization");
				return (!(intPtr2 == IntPtr.Zero)) ? GameBindings.CurrentLocalization(intPtr2) : 0;
			}
			catch
			{
				return 0;
			}
		}

		private static string DescribeLocalization(int value)
		{
			return value switch
			{
				0 => "Unset", 
				1 => "English", 
				2 => "Japanese", 
				3 => "Korean", 
				4 => "Traditional Chinese", 
				5 => "Simplified Chinese", 
				_ => "Unknown(" + value + ")", 
			};
		}

		private static void ParseHotkey(string value)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (Enum.TryParse<Key>(value, ignoreCase: true, out Key result))
			{
				_hotkey = result;
				return;
			}
			_hotkey = (Key)26;
			_log.LogWarning((object)("Unknown hotkey '" + value + "'; falling back to L."));
		}

		private static void ParseLanguages(string value)
		{
			Languages.Clear();
			string[] array = value.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				LanguageOption option = LanguageOption.Parse(text);
				if (option == null)
				{
					_log.LogWarning((object)("Ignoring unknown language '" + text + "'."));
				}
				else if (!Languages.Exists((LanguageOption l) => l.Localization == option.Localization))
				{
					Languages.Add(option);
				}
			}
			if (Languages.Count == 0)
			{
				Languages.Add(LanguageOption.English);
				Languages.Add(LanguageOption.ChineseSC);
				_log.LogWarning((object)"LanguageOrder was empty; defaulting to English, ChineseSC.");
			}
		}
	}
	public sealed class HotkeyBehaviour : MonoBehaviour
	{
		public HotkeyBehaviour(IntPtr pointer)
			: base(pointer)
		{
		}

		private void Update()
		{
			Plugin.OnFrame();
		}

		private void LateUpdate()
		{
			Plugin.OnLateFrame();
		}
	}
	internal static class RememberedLanguage
	{
		private static string Path
		{
			get
			{
				string directoryName = System.IO.Path.GetDirectoryName(typeof(Plugin).Assembly.Location);
				return System.IO.Path.Combine(string.IsNullOrEmpty(directoryName) ? "." : directoryName, "InFalsusAdvVN.last");
			}
		}

		public static void Save(string name)
		{
			try
			{
				File.WriteAllText(Path, name);
			}
			catch
			{
			}
		}

		public static string Load()
		{
			try
			{
				return File.Exists(Path) ? File.ReadAllText(Path).Trim() : null;
			}
			catch
			{
				return null;
			}
		}
	}
	internal static class StoryLocalizationPatch
	{
		private static Action<string> _log;

		private static int _depth;

		private static StoryTextController _controller;

		private static int _saved;

		public static bool Installed { get; private set; }

		private static IntPtr Field
		{
			get
			{
				if (_controller != null)
				{
					return _controller.LocalizationField;
				}
				return IntPtr.Zero;
			}
		}

		public static void Install(StoryTextController controller, Action<string> log)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_006d: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_00f2: Expected O, but got Unknown
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			//IL_0179: Expected O, but got Unknown
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Expected O, but got Unknown
			//IL_0200: Expected O, but got Unknown
			_log = log;
			_controller = controller;
			Harmony val = new Harmony("aria.infalsus.advvn.storyloc");
			int num = 0;
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(StoryCommandProcessor), "ProcessSayTextCommand", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(StoryLocalizationPatch), "BeginPrefix", (Type[])null), new HarmonyMethod(typeof(StoryLocalizationPatch), "EndPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				else
				{
					log("StoryLocalizationPatch: StoryCommandProcessor.ProcessSayTextCommand not found.");
				}
			}
			catch (Exception ex)
			{
				log("StoryLocalizationPatch (resolve) failed: " + ex.Message);
			}
			try
			{
				MethodInfo methodInfo2 = AccessTools.Method(typeof(StoryScene), "_jp", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(StoryLocalizationPatch), "RenderPrefix", (Type[])null), new HarmonyMethod(typeof(StoryLocalizationPatch), "EndPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				else
				{
					log("StoryLocalizationPatch: StoryScene._jp not found.");
				}
			}
			catch (Exception ex2)
			{
				log("StoryLocalizationPatch (render) failed: " + ex2.Message);
			}
			try
			{
				MethodInfo methodInfo3 = AccessTools.Method(typeof(ChatLogContainer), "_aS", (Type[])null, (Type[])null);
				if (methodInfo3 != null)
				{
					val.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(StoryLocalizationPatch), "BeginPrefix", (Type[])null), new HarmonyMethod(typeof(StoryLocalizationPatch), "EndPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				else
				{
					log("StoryLocalizationPatch: ChatLogContainer._aS not found (LOG panel stays in the game language).");
				}
			}
			catch (Exception ex3)
			{
				log("StoryLocalizationPatch (log) failed: " + ex3.Message);
			}
			try
			{
				MethodInfo methodInfo4 = AccessTools.Method(typeof(StoryCommandProcessor), "ProcessPhoneChatCommand", (Type[])null, (Type[])null);
				if (methodInfo4 != null)
				{
					val.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(StoryLocalizationPatch), "BeginPrefix", (Type[])null), new HarmonyMethod(typeof(StoryLocalizationPatch), "EndPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				else
				{
					log("StoryLocalizationPatch: StoryCommandProcessor.ProcessPhoneChatCommand not found (phone chat stays in the game language).");
				}
			}
			catch (Exception ex4)
			{
				log("StoryLocalizationPatch (phone) failed: " + ex4.Message);
			}
			Installed = num >= 2;
			log("StoryLocalizationPatch installed on " + num + " story-localization hook(s).");
		}

		private static void Enter()
		{
			if (Plugin.StoryLocalization != 0 && _controller != null && _depth++ == 0)
			{
				_saved = GameBindings.CurrentLocalization(Field);
				GameBindings.SetLocalization(Field, Plugin.StoryLocalization);
			}
		}

		private static void Exit()
		{
			if (_controller != null && _depth != 0 && --_depth == 0)
			{
				GameBindings.SetLocalization(Field, (_saved == 0) ? 1 : _saved);
			}
		}

		private static void BeginPrefix()
		{
			Enter();
		}

		private static void EndPostfix()
		{
			Exit();
		}

		private static void RenderPrefix(StoryScene __instance)
		{
			Enter();
			try
			{
				if (Plugin.StoryLocalization != 0 && !((Object)(object)__instance == (Object)null) && _controller != null)
				{
					IntPtr scene = IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)__instance);
					_controller.PrepareRendererSet(scene, Plugin.StoryLocalization, hideOthers: false);
				}
			}
			catch (Exception ex)
			{
				_log?.Invoke("StoryLocalizationPatch render hook: " + ex.Message);
			}
		}
	}
	internal sealed class StoryTextController
	{
		private const int UpdateTypeDefault = 0;

		private const int UpdateTypeDontUpdateMesh = 1;

		private const int EnglishLocalization = 1;

		private readonly GameBindings _bindings;

		private readonly bool _manageVisibility;

		private readonly Action<string> _log;

		private readonly Action<string> _debug;

		private IntPtr _provider;

		private uint _providerHandle;

		private bool _providerReported;

		private StoryTranslationDetails _details;

		private List<StringMapping> _translations;

		private Dictionary<string, int> _idToIndex;

		private static IntPtr _stringClass;

		private string _lastWrittenLine;

		private DynamicStringMapping _nameMapping;

		private string _rawSpeakerName;

		private string _lastSpeakerText;

		private const string MissingMapping = "Missing String Mapping";

		private Dictionary<string, string> _textToId;

		public IntPtr LocalizationField => _bindings.TargetLocalizationField;

		public int GameLocalization => GameBindings.CurrentLocalization(_bindings.TargetLocalizationField);

		public StoryTextController(GameBindings bindings, bool manageVisibility, Action<string> log, Action<string> debug)
		{
			_bindings = bindings;
			_manageVisibility = manageVisibility;
			_log = log;
			_debug = debug;
		}

		private bool EnsureIndex()
		{
			if (_idToIndex != null)
			{
				return true;
			}
			try
			{
				Il2CppReferenceArray<Object> val = Resources.FindObjectsOfTypeAll(Il2CppType.Of<StoryTranslationDetails>());
				if (val == null)
				{
					return false;
				}
				for (int i = 0; i < ((Il2CppArrayBase<Object>)(object)val).Length; i++)
				{
					Object val2 = ((Il2CppArrayBase<Object>)(object)val)[i];
					if (val2 == (Object)null)
					{
						continue;
					}
					StoryTranslationDetails val3 = ((Il2CppObjectBase)val2).TryCast<StoryTranslationDetails>();
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					List<StringMapping> translations = val3.Translations;
					if (translations == null)
					{
						continue;
					}
					Dictionary<string, int> dictionary = new Dictionary<string, int>(translations.Count, StringComparer.Ordinal);
					for (int j = 0; j < translations.Count; j++)
					{
						string id = translations[j].Id;
						if (!string.IsNullOrEmpty(id))
						{
							dictionary[id] = j;
						}
					}
					_details = val3;
					_translations = translations;
					_idToIndex = dictionary;
					_log("Translation table indexed: " + dictionary.Count + " story strings.");
					return true;
				}
			}
			catch (Exception ex)
			{
				_log("Translation table indexing failed: " + ex.Message);
			}
			return false;
		}

		public string LookupTranslation(string storyStringId, int localization)
		{
			if (string.IsNullOrEmpty(storyStringId) || !EnsureIndex())
			{
				return null;
			}
			if (!_idToIndex.TryGetValue(storyStringId, out var value))
			{
				_debug("'" + storyStringId + "' is not in the translation table");
				return null;
			}
			try
			{
				StringMapping val = _translations[value];
				string english = val.English;
				return localization switch
				{
					2 => FirstNonEmpty(val.Japanese, english), 
					4 => FirstNonEmpty(val.ChineseTC, english), 
					5 => FirstNonEmpty(val.ChineseSC, english), 
					_ => english, 
				};
			}
			catch (Exception ex)
			{
				_debug("reading translation row failed: " + ex.Message);
				return null;
			}
		}

		private static bool IsManagedString(IntPtr candidate)
		{
			if (candidate == IntPtr.Zero)
			{
				return false;
			}
			if (_stringClass == IntPtr.Zero)
			{
				_stringClass = Il2.FindClass("System", "String");
			}
			if (_stringClass != IntPtr.Zero)
			{
				return Il2.ObjectClass(candidate) == _stringClass;
			}
			return false;
		}

		private static bool StringEquals(IntPtr il2cppString, string expected)
		{
			if (il2cppString == IntPtr.Zero || !IsManagedString(il2cppString))
			{
				return false;
			}
			int num = Il2.StringLength(il2cppString);
			if (num < 0 || num > 8192)
			{
				return false;
			}
			if (num != (expected?.Length ?? 0))
			{
				return false;
			}
			IntPtr ptr = Il2.StringChars(il2cppString);
			for (int i = 0; i < num; i++)
			{
				if (Marshal.ReadInt16(ptr, i * 2) != expected[i])
				{
					return false;
				}
			}
			return true;
		}

		private static string FirstNonEmpty(string value, string fallback)
		{
			if (!string.IsNullOrEmpty(value))
			{
				return value;
			}
			return fallback;
		}

		private int RendererSetOffsetFor(int localization)
		{
			return localization switch
			{
				2 => _bindings.JapaneseTextBoxesOffset, 
				4 => _bindings.ChineseTextBoxesOffset, 
				5 => _bindings.ChineseTextBoxesOffset, 
				_ => _bindings.EnglishTextBoxesOffset, 
			};
		}

		public bool PrepareRendererSet(IntPtr scene, int localization, bool hideOthers)
		{
			if (scene == IntPtr.Zero || localization == 0)
			{
				return false;
			}
			IntPtr intPtr = GameBindings.ReadPtr(scene, RendererSetOffsetFor(localization));
			if (intPtr == IntPtr.Zero)
			{
				return false;
			}
			bool result = false;
			IntPtr intPtr2 = GameBindings.ReadPtr(scene, _bindings.SceneTextBoxesOffset);
			if (intPtr2 != IntPtr.Zero && intPtr2 != intPtr)
			{
				DeactivateSet(intPtr2);
				GameBindings.WritePtr(scene, _bindings.SceneTextBoxesOffset, intPtr);
				result = true;
			}
			ActivateSet(intPtr);
			if (hideOthers)
			{
				SyncVisibility(scene, intPtr);
			}
			return result;
		}

		private static FastText Wrap(IntPtr pointer)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			if (!(pointer == IntPtr.Zero))
			{
				return new FastText(pointer);
			}
			return null;
		}

		private void ActivateSet(IntPtr array)
		{
			int num = GameBindings.ArrayLength(array);
			for (int i = 0; i < num; i++)
			{
				FastText val = Wrap(GameBindings.ArrayElement(array, i));
				if (!((Object)(object)val == (Object)null))
				{
					GameObject gameObject = ((Component)val).gameObject;
					if ((Object)(object)gameObject != (Object)null && !gameObject.activeSelf)
					{
						gameObject.SetActive(true);
					}
				}
			}
		}

		private void DeactivateSet(IntPtr array)
		{
			int num = GameBindings.ArrayLength(array);
			for (int i = 0; i < num; i++)
			{
				FastText val = Wrap(GameBindings.ArrayElement(array, i));
				if (!((Object)(object)val == (Object)null))
				{
					GameObject gameObject = ((Component)val).gameObject;
					if ((Object)(object)gameObject != (Object)null && gameObject.activeSelf)
					{
						gameObject.SetActive(false);
					}
				}
			}
		}

		private static bool LooksLikeText(IntPtr il2cppString)
		{
			try
			{
				int num = Il2.StringLength(il2cppString);
				return num > 0 && num < 4096;
			}
			catch
			{
				return false;
			}
		}

		private bool SetRendererText(IntPtr array, string text, int localization)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			int num = GameBindings.ArrayLength(array);
			if (num <= 0)
			{
				_debug("renderer set is empty");
				return false;
			}
			int value = GameBindings.CurrentLocalization(_bindings.TargetLocalizationField);
			int num2 = 0;
			try
			{
				GameBindings.SetLocalization(_bindings.TargetLocalizationField, localization);
				DynamicString val = new DynamicString(text);
				for (int i = 0; i < num; i++)
				{
					FastText val2 = Wrap(GameBindings.ArrayElement(array, i));
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					try
					{
						_debug($"FastText[{i}]: SetText(DynamicString, {text.Length} chars, loc={localization})");
						val2.SetText(val, (UpdateType)0);
						int maxVisibleCharacters = val2.MaxVisibleCharacters;
						if (maxVisibleCharacters < text.Length)
						{
							_debug($"FastText[{i}]: SetMaxVisibleCharacters({maxVisibleCharacters} -> {text.Length})");
							val2.SetMaxVisibleCharacters(text.Length, (UpdateType)0);
						}
						num2++;
					}
					catch (Exception ex)
					{
						_debug($"FastText[{i}] update threw: {ex.Message}");
					}
				}
			}
			catch (Exception ex2)
			{
				_debug("SetText(DynamicString) setup threw: " + ex2.Message);
			}
			finally
			{
				GameBindings.SetLocalization(_bindings.TargetLocalizationField, value);
			}
			_debug($"renderer text updated: {num2}/{num}");
			return num2 > 0;
		}

		public bool ReapplyVisible(IntPtr scene, string text, int localization)
		{
			if (scene == IntPtr.Zero || string.IsNullOrEmpty(text))
			{
				return false;
			}
			IntPtr intPtr = GameBindings.ReadPtr(scene, RendererSetOffsetFor(localization));
			if (intPtr == IntPtr.Zero)
			{
				return false;
			}
			return SetRendererText(intPtr, text, localization);
		}

		private void SyncVisibility(IntPtr scene, IntPtr activeArray)
		{
			if (_bindings.SetActiveMethod == IntPtr.Zero || _bindings.GetGameObjectMethod == IntPtr.Zero)
			{
				return;
			}
			HashSet<IntPtr> hashSet = new HashSet<IntPtr>();
			int num = GameBindings.ArrayLength(activeArray);
			for (int i = 0; i < num; i++)
			{
				IntPtr intPtr = GameBindings.ArrayElement(activeArray, i);
				if (intPtr != IntPtr.Zero)
				{
					hashSet.Add(intPtr);
				}
			}
			int num2 = 0;
			int num3 = 0;
			int[] array = new int[3] { _bindings.EnglishTextBoxesOffset, _bindings.JapaneseTextBoxesOffset, _bindings.ChineseTextBoxesOffset };
			foreach (int offset in array)
			{
				IntPtr array2 = GameBindings.ReadPtr(scene, offset);
				int num4 = GameBindings.ArrayLength(array2);
				for (int k = 0; k < num4; k++)
				{
					IntPtr intPtr2 = GameBindings.ArrayElement(array2, k);
					if (intPtr2 == IntPtr.Zero)
					{
						continue;
					}
					bool flag = hashSet.Contains(intPtr2);
					string error;
					IntPtr intPtr3 = Il2.Invoke(_bindings.GetGameObjectMethod, intPtr2, null, out error);
					if (!(intPtr3 == IntPtr.Zero))
					{
						IntPtr intPtr4 = Il2.Alloc(4);
						try
						{
							Marshal.WriteInt32(intPtr4, flag ? 1 : 0);
							Il2.Invoke(_bindings.SetActiveMethod, intPtr3, new IntPtr[1] { intPtr4 }, out error);
						}
						finally
						{
							Marshal.FreeHGlobal(intPtr4);
						}
						if (flag)
						{
							num2++;
						}
						else
						{
							num3++;
						}
					}
				}
			}
			_debug($"renderer visibility: {num2} active, {num3} hidden");
		}

		private DynamicStringMapping FindNameMapping()
		{
			if ((Object)(object)_nameMapping != (Object)null)
			{
				return _nameMapping;
			}
			try
			{
				Il2CppReferenceArray<Object> val = Resources.FindObjectsOfTypeAll(Il2CppType.Of<DynamicStringMapping>());
				if (val == null)
				{
					return null;
				}
				for (int i = 0; i < ((Il2CppArrayBase<Object>)(object)val).Length; i++)
				{
					Object val2 = ((Il2CppArrayBase<Object>)(object)val)[i];
					if (!(val2 == (Object)null))
					{
						DynamicStringMapping val3 = ((Il2CppObjectBase)val2).TryCast<DynamicStringMapping>();
						if (!((Object)(object)val3 == (Object)null))
						{
							_nameMapping = val3;
							_log("DynamicStringMapping found (" + ((Il2CppArrayBase<Object>)(object)val).Length + " candidate object(s)).");
							return _nameMapping;
						}
					}
				}
			}
			catch (Exception ex)
			{
				_debug("DynamicStringMapping lookup threw: " + ex.Message);
			}
			return null;
		}

		public bool RefreshSpeakerName(IntPtr scene, int localization)
		{
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Expected O, but got Unknown
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (scene == IntPtr.Zero || localization == 0)
				{
					return false;
				}
				if (_bindings.SceneNameContainerOffset <= 0 || _bindings.CharacterNameOffset <= 0)
				{
					return false;
				}
				IntPtr intPtr = GameBindings.ReadPtr(scene, _bindings.SceneStateOffset);
				IntPtr intPtr2 = ((intPtr == IntPtr.Zero) ? IntPtr.Zero : GameBindings.ReadPtr(intPtr, _bindings.StateTextBoxOffset));
				IntPtr intPtr3 = GameBindings.ReadPtr(scene, _bindings.SceneNameContainerOffset);
				if (intPtr2 == IntPtr.Zero || intPtr3 == IntPtr.Zero)
				{
					return false;
				}
				IntPtr intPtr4 = GameBindings.ReadPtr(intPtr2, _bindings.CharacterNameOffset);
				string text = ((intPtr4 == IntPtr.Zero) ? null : Il2.ToManaged(intPtr4));
				if (string.IsNullOrEmpty(text))
				{
					return false;
				}
				string text2 = text;
				if (_rawSpeakerName != null && _lastSpeakerText != null && text == _lastSpeakerText)
				{
					text2 = _rawSpeakerName;
				}
				if ((Object)(object)FindNameMapping() == (Object)null)
				{
					_debug("DynamicStringMapping is not loaded; speaker name left as is");
					return false;
				}
				int gameLocalization = GameLocalization;
				GameBindings.SetLocalization(_bindings.TargetLocalizationField, localization);
				try
				{
					string text3 = ResolveName(text2);
					if (IsUnusable(text3) && text2 != text)
					{
						text3 = ResolveName(text);
					}
					if (IsUnusable(text3))
					{
						_debug("speaker name key '" + text2 + "' did not resolve; name plate left unchanged");
						return false;
					}
					DynamicString val = _nameMapping.Get((DynamicStringTypeFlags)128, (Localization)localization, new RawStoryName(text2));
					new StoryNameContainer(intPtr3)._yO(val);
					new StoryTextBoxState(intPtr2).CharacterName = text3;
					_rawSpeakerName = text2;
					_lastSpeakerText = text3;
					_log($"Speaker name '{text2}' -> '{text3}'.");
					return true;
				}
				finally
				{
					GameBindings.SetLocalization(_bindings.TargetLocalizationField, gameLocalization);
				}
			}
			catch (Exception ex)
			{
				_debug("speaker name refresh threw: " + ex.Message);
				return false;
			}
		}

		private string ResolveName(string key)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			DynamicString val = _nameMapping.Get((DynamicStringTypeFlags)128, (Localization)GameLocalization, new RawStoryName(key));
			if (val != null)
			{
				return val.CreateString();
			}
			return null;
		}

		private static bool IsUnusable(string text)
		{
			if (!string.IsNullOrEmpty(text))
			{
				return text == "Missing String Mapping";
			}
			return true;
		}

		public bool AdvanceStory(IntPtr scene)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (scene == IntPtr.Zero)
				{
					return false;
				}
				new StoryScene(scene)._dp();
				return true;
			}
			catch (Exception ex)
			{
				_debug("advance threw: " + ex.Message);
				return false;
			}
		}

		public bool OpenLog(IntPtr scene)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (scene == IntPtr.Zero || _bindings.SceneChatLogOffset <= 0)
				{
					return false;
				}
				IntPtr intPtr = GameBindings.ReadPtr(scene, _bindings.SceneChatLogOffset);
				if (intPtr == IntPtr.Zero)
				{
					return false;
				}
				GameObject gameObject = ((Component)new ChatLogContainer(intPtr)).gameObject;
				if ((Object)(object)gameObject == (Object)null)
				{
					return false;
				}
				if (gameObject.activeSelf)
				{
					return false;
				}
				gameObject.SetActive(true);
				new StoryScene(scene)._Ep();
				_log("LOG panel opened from the mouse wheel.");
				return true;
			}
			catch (Exception ex)
			{
				_debug("opening the LOG panel threw: " + ex.Message);
				return false;
			}
		}

		public bool CloseLog(IntPtr scene)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			try
			{
				if (scene == IntPtr.Zero || _bindings.SceneChatLogOffset <= 0)
				{
					return false;
				}
				IntPtr intPtr = GameBindings.ReadPtr(scene, _bindings.SceneChatLogOffset);
				if (intPtr == IntPtr.Zero)
				{
					return false;
				}
				ChatLogContainer val = new ChatLogContainer(intPtr);
				GameObject gameObject = ((Component)val).gameObject;
				if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy)
				{
					return false;
				}
				val._bS();
				_log("LOG panel closing (fade out via ChatLogContainer._bS).");
				return true;
			}
			catch (Exception ex)
			{
				_debug("closing the LOG panel threw: " + ex.Message);
				return false;
			}
		}

		public bool IsLogOpen(IntPtr scene)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			try
			{
				if (scene == IntPtr.Zero || _bindings.SceneChatLogOffset <= 0)
				{
					return false;
				}
				IntPtr intPtr = GameBindings.ReadPtr(scene, _bindings.SceneChatLogOffset);
				if (intPtr == IntPtr.Zero)
				{
					return false;
				}
				ChatLogContainer val = new ChatLogContainer(intPtr);
				GameObject gameObject = ((Component)val).gameObject;
				return (Object)(object)gameObject != (Object)null && gameObject.activeInHierarchy && ((Behaviour)val).enabled;
			}
			catch
			{
				return false;
			}
		}

		private void EnsureReverseIndex()
		{
			if (_textToId != null || !EnsureIndex())
			{
				return;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(_translations.Count * 2, StringComparer.Ordinal);
			for (int i = 0; i < _translations.Count; i++)
			{
				string id = _translations[i].Id;
				if (!string.IsNullOrEmpty(id))
				{
					StringMapping val = _translations[i];
					AddToReverse(dictionary, val.English, id);
					AddToReverse(dictionary, val.Japanese, id);
					AddToReverse(dictionary, val.ChineseTC, id);
					AddToReverse(dictionary, val.ChineseSC, id);
				}
			}
			_textToId = dictionary;
			_log("Reverse text index built: " + dictionary.Count + " strings.");
		}

		private static void AddToReverse(Dictionary<string, string> map, string text, string id)
		{
			if (!string.IsNullOrEmpty(text) && !map.ContainsKey(text))
			{
				map[text] = id;
			}
		}

		public bool RefreshOpenLog(IntPtr scene)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			try
			{
				if (scene == IntPtr.Zero || _bindings.SceneChatLogOffset <= 0 || _bindings.ChatLogEntryClass == IntPtr.Zero)
				{
					return false;
				}
				int storyLocalization = Plugin.StoryLocalization;
				if (storyLocalization == 0)
				{
					return false;
				}
				IntPtr intPtr = GameBindings.ReadPtr(scene, _bindings.SceneChatLogOffset);
				if (intPtr == IntPtr.Zero)
				{
					return false;
				}
				_debug("log: container found");
				ChatLogContainer val = new ChatLogContainer(intPtr);
				GameObject gameObject = ((Component)val).gameObject;
				if ((Object)(object)gameObject == (Object)null || !gameObject.activeSelf)
				{
					return false;
				}
				_debug("log: panel is open");
				EnsureReverseIndex();
				if (_textToId == null)
				{
					return false;
				}
				_debug("log: collecting entries");
				Il2CppReferenceArray<Component> componentsInChildren = ((Component)val).GetComponentsInChildren(Il2CppType.Of<ChatLogEntryContainer>(), false);
				if (componentsInChildren == null || ((Il2CppArrayBase<Component>)(object)componentsInChildren).Length == 0)
				{
					return false;
				}
				_debug("log: rewriting " + ((Il2CppArrayBase<Component>)(object)componentsInChildren).Length + " entries");
				int num = 0;
				for (int i = 0; i < ((Il2CppArrayBase<Component>)(object)componentsInChildren).Length; i++)
				{
					Component val2 = ((Il2CppArrayBase<Component>)(object)componentsInChildren)[i];
					if (!((Object)(object)val2 == (Object)null))
					{
						IntPtr intPtr2 = IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)val2);
						if (!(intPtr2 == IntPtr.Zero) && RetranslateEntry(intPtr2, storyLocalization))
						{
							num++;
						}
					}
				}
				_debug($"LOG entries refreshed: {num}/{((Il2CppArrayBase<Component>)(object)componentsInChildren).Length}");
				return num > 0;
			}
			catch (Exception ex)
			{
				_debug("LOG panel refresh threw: " + ex.Message);
				return false;
			}
		}

		private bool RetranslateEntry(IntPtr entryPtr, int localization)
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			bool result = false;
			int entryTextsOffset = _bindings.EntryTextsOffset;
			if (entryTextsOffset > 0)
			{
				IntPtr array = GameBindings.ReadPtr(entryPtr, entryTextsOffset);
				int num = GameBindings.ArrayLength(array);
				for (int i = 0; i < num; i++)
				{
					IntPtr intPtr = GameBindings.ArrayElement(array, i);
					if (intPtr == IntPtr.Zero)
					{
						continue;
					}
					FastText val = new FastText(intPtr);
					string text = (((Object)(object)val == (Object)null) ? null : val.GetText());
					if (!string.IsNullOrEmpty(text) && !(text == "Missing String Mapping") && _textToId.TryGetValue(text, out var value))
					{
						string text2 = LookupTranslation(value, localization);
						if (!string.IsNullOrEmpty(text2) && !(text2 == text))
						{
							SetFastText(val, text2, localization);
							result = true;
						}
					}
				}
			}
			if (_bindings.EntryNameOffset > 0 && (Object)(object)FindNameMapping() != (Object)null)
			{
				IntPtr intPtr2 = GameBindings.ReadPtr(entryPtr, _bindings.EntryNameOffset);
				if (intPtr2 != IntPtr.Zero)
				{
					FastText val2 = new FastText(intPtr2);
					string text3 = (((Object)(object)val2 == (Object)null) ? null : val2.GetText());
					if (!string.IsNullOrEmpty(text3) && text3 != "Missing String Mapping")
					{
						DynamicString val3 = _nameMapping.Get((DynamicStringTypeFlags)128, (Localization)localization, new RawStoryName(text3));
						string text4 = ((val3 == null) ? null : val3.CreateString());
						if (!IsUnusable(text4) && text4 != text3)
						{
							SetFastText(val2, text4, localization);
							result = true;
						}
					}
				}
			}
			return result;
		}

		public bool RefreshOpenPhoneChat()
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Expected O, but got Unknown
			try
			{
				int storyLocalization = Plugin.StoryLocalization;
				if (storyLocalization == 0)
				{
					return false;
				}
				_debug("phone: searching container");
				PhoneChatContainer val = Object.FindObjectOfType<PhoneChatContainer>();
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				GameObject gameObject = ((Component)val).gameObject;
				if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy)
				{
					return false;
				}
				_debug("phone: container is visible");
				EnsureReverseIndex();
				if (_textToId == null)
				{
					return false;
				}
				_debug("phone: collecting texts");
				Il2CppReferenceArray<Component> componentsInChildren = ((Component)val).GetComponentsInChildren(Il2CppType.Of<FastText>(), false);
				if (componentsInChildren == null || ((Il2CppArrayBase<Component>)(object)componentsInChildren).Length == 0)
				{
					return false;
				}
				_debug("phone: rewriting " + ((Il2CppArrayBase<Component>)(object)componentsInChildren).Length + " texts");
				int num = 0;
				for (int i = 0; i < ((Il2CppArrayBase<Component>)(object)componentsInChildren).Length; i++)
				{
					Component val2 = ((Il2CppArrayBase<Component>)(object)componentsInChildren)[i];
					if ((Object)(object)val2 == (Object)null)
					{
						continue;
					}
					FastText val3 = new FastText(IL2CPP.Il2CppObjectBaseToPtr((Il2CppObjectBase)(object)val2));
					string text = (((Object)(object)val3 == (Object)null) ? null : val3.GetText());
					if (IsUnusable(text))
					{
						continue;
					}
					if (_textToId.TryGetValue(text, out var value))
					{
						string text2 = LookupTranslation(value, storyLocalization);
						if (!IsUnusable(text2) && text2 != text)
						{
							SetFastText(val3, text2, storyLocalization);
							num++;
						}
					}
					else if (!((Object)(object)FindNameMapping() == (Object)null))
					{
						DynamicString val4 = _nameMapping.Get((DynamicStringTypeFlags)128, (Localization)storyLocalization, new RawStoryName(text));
						string text3 = ((val4 == null) ? null : val4.CreateString());
						if (!IsUnusable(text3) && text3 != text)
						{
							SetFastText(val3, text3, storyLocalization);
							num++;
						}
					}
				}
				_debug($"phone chat texts refreshed: {num}/{((Il2CppArrayBase<Component>)(object)componentsInChildren).Length}");
				return num > 0;
			}
			catch (Exception ex)
			{
				_debug("phone chat refresh threw: " + ex.Message);
				return false;
			}
		}

		private void SetFastText(FastText renderer, string text, int localization)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			int gameLocalization = GameLocalization;
			GameBindings.SetLocalization(_bindings.TargetLocalizationField, localization);
			try
			{
				renderer.SetText(new DynamicString(text), (UpdateType)0);
				if (renderer.MaxVisibleCharacters < text.Length)
				{
					renderer.SetMaxVisibleCharacters(text.Length, (UpdateType)0);
				}
			}
			finally
			{
				GameBindings.SetLocalization(_bindings.TargetLocalizationField, gameLocalization);
			}
		}

		public IntPtr ReadCurrentStoryStringIdPointer(IntPtr scene)
		{
			if (_bindings.SceneTextIdOffset <= 0)
			{
				return IntPtr.Zero;
			}
			return GameBindings.ReadPtr(scene, _bindings.SceneTextIdOffset);
		}

		public string ReadCurrentStoryStringId(IntPtr scene)
		{
			IntPtr intPtr = ReadCurrentStoryStringIdPointer(scene);
			if (!(intPtr == IntPtr.Zero))
			{
				return Il2.ToManaged(intPtr);
			}
			return null;
		}

		public string DescribeScene(IntPtr scene)
		{
			IntPtr intPtr = GameBindings.ReadPtr(scene, _bindings.EnglishTextBoxesOffset);
			IntPtr intPtr2 = GameBindings.ReadPtr(scene, _bindings.JapaneseTextBoxesOffset);
			IntPtr intPtr3 = GameBindings.ReadPtr(scene, _bindings.ChineseTextBoxesOffset);
			IntPtr intPtr4 = GameBindings.ReadPtr(scene, _bindings.SceneTextBoxesOffset);
			IntPtr intPtr5 = GameBindings.ReadPtr(scene, _bindings.SceneStateOffset);
			IntPtr intPtr6 = ((intPtr5 == IntPtr.Zero) ? IntPtr.Zero : GameBindings.ReadPtr(intPtr5, _bindings.StateTextBoxOffset));
			string text = ((intPtr4 == intPtr) ? "english" : ((intPtr4 == intPtr2) ? "japanese" : ((intPtr4 == intPtr3) ? "chinese" : ((intPtr4 == IntPtr.Zero) ? "none" : "other"))));
			return "scene: gameLocalization=" + GameLocalization + ", override=" + Plugin.StoryLocalization + ", textId='" + (ReadCurrentStoryStringId(scene) ?? "<none>") + "', sets english/japanese/chinese=" + GameBindings.ArrayLength(intPtr) + "/" + GameBindings.ArrayLength(intPtr2) + "/" + GameBindings.ArrayLength(intPtr3) + ", activeSet=" + text + ", textBoxState=" + ((intPtr6 != IntPtr.Zero) ? "ok" : "null");
		}
	}
	internal static class WheelListener
	{
		private const int MouseStateSize = 32;

		private const int ScrollOffsetInState = 16;

		private static float _pending;

		private static int _rightClicks;

		private static int _lastButtons;

		private static Action<string> _log;

		private static int _reports;

		private static int _probes;

		private static int _mouseEvents;

		public static bool Installed { get; private set; }

		public static void SetLog(Action<string> log)
		{
			_log = log;
		}

		public static void InstallLate()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (Installed)
			{
				return;
			}
			try
			{
				Action<InputEventPtr, InputDevice> val = DelegateSupport.ConvertDelegate<Action<InputEventPtr, InputDevice>>((Delegate)new Action<InputEventPtr, InputDevice>(OnEvent));
				InputSystem.onEvent += val;
				Installed = true;
				_log?.Invoke("WheelListener attached to InputSystem.onEvent.");
			}
			catch (Exception ex)
			{
				_log?.Invoke("WheelListener failed: " + ex.Message);
			}
		}

		public static float Consume()
		{
			float pending = _pending;
			_pending = 0f;
			return pending;
		}

		public static int ConsumeRightClicks()
		{
			int rightClicks = _rightClicks;
			_rightClicks = 0;
			return rightClicks;
		}

		private unsafe static void OnEvent(InputEventPtr eventPtr, InputDevice device)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (device == null)
				{
					return;
				}
				Mouse val = ((Il2CppObjectBase)device).TryCast<Mouse>();
				if (val == null)
				{
					return;
				}
				_mouseEvents++;
				try
				{
					int num = (int)(((InputEventPtr)(ref eventPtr)).sizeInBytes - 32);
					if (num >= 16 && num <= 64)
					{
						short num2 = Marshal.ReadInt16((IntPtr)((InputEventPtr)(ref eventPtr)).data, num + 24);
						if ((num2 & 2) != 0 && (_lastButtons & 2) == 0)
						{
							_rightClicks++;
						}
						_lastButtons = num2;
					}
				}
				catch
				{
				}
				float num3 = ((val.scroll != null) ? ((InputControl<Vector2>)(object)val.scroll).ReadValue().y : 0f);
				if (num3 == 0f)
				{
					num3 = ReadScrollFromEvent(eventPtr);
				}
				if (num3 != 0f)
				{
					_pending += num3;
					if (_reports < 12)
					{
						_reports++;
						_log?.Invoke("[debug] wheel event=" + num3 + " device=" + ((InputControl)val).name + " mouseEvents=" + _mouseEvents);
					}
				}
			}
			catch
			{
			}
		}

		private unsafe static float ReadScrollFromEvent(InputEventPtr eventPtr)
		{
			try
			{
				int sizeInBytes = (int)((InputEventPtr)(ref eventPtr)).sizeInBytes;
				int num = sizeInBytes - 32;
				if (num < 16 || num > 64)
				{
					return 0f;
				}
				IntPtr intPtr = (IntPtr)((InputEventPtr)(ref eventPtr)).data;
				if (intPtr == IntPtr.Zero)
				{
					return 0f;
				}
				IntPtr intPtr2 = IntPtr.Add(intPtr, num + 16);
				float num2 = Marshal.PtrToStructure<float>(intPtr2);
				float result = Marshal.PtrToStructure<float>(IntPtr.Add(intPtr2, 4));
				if (_probes < 10)
				{
					_probes++;
					_log?.Invoke("[debug] mouse event size=" + sizeInBytes + " base=" + num + " pos=(" + Marshal.PtrToStructure<float>(IntPtr.Add(intPtr, num)) + "," + Marshal.PtrToStructure<float>(IntPtr.Add(intPtr, num + 4)) + ") scroll=(" + num2 + "," + result + ")");
				}
				return result;
			}
			catch
			{
				return 0f;
			}
		}
	}
}