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