using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Networking;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Translator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Translator")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("9024b3b8-fda6-4b1c-a8ed-75acebba59e2")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ChatTranslator;
[BepInPlugin("jp.jyoki_tsubame.gladiomori.translator", "Translator", "1.0.0")]
public class ChatTranslatorPlugin : BaseUnityPlugin
{
[CompilerGenerated]
private sealed class <>c__DisplayClass24_0
{
public string translated;
internal void <TranslateAndRunOriginal>b__0(string t)
{
translated = t;
}
}
[CompilerGenerated]
private sealed class <TranslateAndRunOriginal>d__24 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public ChatTranslatorPlugin <>4__this;
public string message;
private <>c__DisplayClass24_0 <>8__1;
public MultiplayerChat inst;
public string playerName;
public string nameColor;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <TranslateAndRunOriginal>d__24(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>8__1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
ChatTranslatorPlugin chatTranslatorPlugin = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>8__1 = new <>c__DisplayClass24_0();
<>8__1.translated = null;
<>2__current = chatTranslatorPlugin.TranslateCoroutine(message, delegate(string t)
{
<>8__1.translated = t;
});
<>1__state = 1;
return true;
case 1:
{
<>1__state = -1;
string finalMessage = message;
if (!string.IsNullOrWhiteSpace(<>8__1.translated) && <>8__1.translated != message)
{
finalMessage = message + " (" + <>8__1.translated + ")";
}
chatTranslatorPlugin.TryRunOriginalManually(inst, finalMessage, playerName, nameColor);
return false;
}
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <TranslateCoroutine>d__20 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public ChatTranslatorPlugin <>4__this;
public string original;
public Action<string> onDone;
private UnityWebRequest <req>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <TranslateCoroutine>d__20(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<req>5__2 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Expected O, but got Unknown
bool result;
try
{
int num = <>1__state;
ChatTranslatorPlugin chatTranslatorPlugin = <>4__this;
switch (num)
{
default:
result = false;
break;
case 0:
{
<>1__state = -1;
if (!chatTranslatorPlugin.Enabled.Value || string.IsNullOrWhiteSpace(chatTranslatorPlugin.AuthKey.Value) || string.IsNullOrWhiteSpace(original) || original.Length < chatTranslatorPlugin.MinChars.Value)
{
onDone?.Invoke(null);
result = false;
break;
}
string text = (chatTranslatorPlugin.UseFreeEndpoint.Value ? "https://api-free.deepl.com/v2/translate" : "https://api.deepl.com/v2/translate");
WWWForm val = new WWWForm();
val.AddField("auth_key", chatTranslatorPlugin.AuthKey.Value);
val.AddField("text", original);
val.AddField("target_lang", chatTranslatorPlugin.TargetLang.Value.Trim().ToUpperInvariant());
if (!string.IsNullOrWhiteSpace(chatTranslatorPlugin.SourceLang.Value))
{
val.AddField("source_lang", chatTranslatorPlugin.SourceLang.Value.Trim().ToUpperInvariant());
}
val.AddField("preserve_formatting", "1");
<req>5__2 = UnityWebRequest.Post(text, val);
<>1__state = -3;
<req>5__2.timeout = Mathf.Max(1, chatTranslatorPlugin.TimeoutSec.Value);
<>2__current = <req>5__2.SendWebRequest();
<>1__state = 1;
result = true;
break;
}
case 1:
{
<>1__state = -3;
if (<req>5__2.isNetworkError || <req>5__2.isHttpError)
{
((BaseUnityPlugin)chatTranslatorPlugin).Logger.LogWarning((object)("[ChatTranslator] DeepL request failed: " + <req>5__2.error));
onDone?.Invoke(null);
result = false;
<>m__Finally1();
break;
}
string obj = null;
try
{
Match match = Regex.Match(<req>5__2.downloadHandler.text, "\"text\"\\s*:\\s*\"((?:\\\\.|[^\"])*)\"");
if (match.Success)
{
obj = match.Groups[1].Value.Replace("\\\"", "\"").Replace("\\\\", "\\").Replace("\\n", "\n")
.Replace("\\r", "\r")
.Replace("\\t", "\t");
}
}
catch
{
}
onDone?.Invoke(obj);
<>m__Finally1();
<req>5__2 = null;
result = false;
break;
}
}
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
return result;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<req>5__2 != null)
{
((IDisposable)<req>5__2).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
public const string PluginGuid = "jp.jyoki_tsubame.gladiomori.translator";
public const string PluginName = "Translator";
public const string PluginVersion = "1.0.0";
internal static ChatTranslatorPlugin Instance;
internal ConfigEntry<bool> Enabled;
internal ConfigEntry<string> AuthKey;
internal ConfigEntry<bool> UseFreeEndpoint;
internal ConfigEntry<string> TargetLang;
internal ConfigEntry<string> SourceLang;
internal ConfigEntry<int> TimeoutSec;
internal ConfigEntry<int> MinChars;
private MethodInfo _miGetFromPool;
private MethodInfo _miUpdateHideTime;
private FieldInfo _fiChatHolder;
public static ManualLogSource LogInstance { get; private set; }
private void Awake()
{
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
Instance = this;
LogInstance = ((BaseUnityPlugin)this).Logger;
Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enables or disables the translation feature.");
MinChars = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MinChars", 2, "Sets the minimum number of characters to translate; shorter text is passed through unchanged.");
TimeoutSec = ((BaseUnityPlugin)this).Config.Bind<int>("Network", "TimeoutSeconds", 6, "Sets the timeout for DeepL requests (seconds).");
AuthKey = ((BaseUnityPlugin)this).Config.Bind<string>("DeepL", "AuthKey", "", "Specifies the auth key for the DeepL API (required).");
UseFreeEndpoint = ((BaseUnityPlugin)this).Config.Bind<bool>("DeepL", "UseFreeEndpoint", true, "Uses the Free endpoint (api-free.deepl.com).");
TargetLang = ((BaseUnityPlugin)this).Config.Bind<string>("DeepL", "TargetLanguage", "JA", "Sets the target language (e.g., JA, EN-US, ZH, KO).");
SourceLang = ((BaseUnityPlugin)this).Config.Bind<string>("DeepL", "SourceLanguage", "", "Sets the source language; auto-detected if empty.");
new Harmony("jp.jyoki_tsubame.gladiomori.translator").PatchAll();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Translator loaded. (async translation mode)");
}
private void EnsureReflection()
{
Type typeFromHandle = typeof(MultiplayerChat);
if (_miGetFromPool == null)
{
_miGetFromPool = AccessTools.Method(typeFromHandle, "GetChatMessageItemFromPool", (Type[])null, (Type[])null);
}
if (_miUpdateHideTime == null)
{
_miUpdateHideTime = AccessTools.Method(typeFromHandle, "UpdateHideTime", (Type[])null, (Type[])null);
}
if (_fiChatHolder == null)
{
_fiChatHolder = AccessTools.Field(typeFromHandle, "chatHolder");
}
}
[IteratorStateMachine(typeof(<TranslateCoroutine>d__20))]
internal IEnumerator TranslateCoroutine(string original, Action<string> onDone)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <TranslateCoroutine>d__20(0)
{
<>4__this = this,
original = original,
onDone = onDone
};
}
private static GameObject ExtractGameObject(object obj)
{
GameObject val = (GameObject)((obj is GameObject) ? obj : null);
if (val != null)
{
return val;
}
Component val2 = (Component)((obj is Component) ? obj : null);
if (val2 != null)
{
return val2.gameObject;
}
PropertyInfo propertyInfo = AccessTools.Property(obj.GetType(), "gameObject");
if (propertyInfo != null)
{
object? value = propertyInfo.GetValue(obj, null);
return (GameObject)((value is GameObject) ? value : null);
}
return null;
}
private static object GetMultiplayerChatMessageFromItem(object itemObj)
{
if (itemObj == null)
{
return null;
}
FieldInfo fieldInfo = AccessTools.Field(itemObj.GetType(), "multiplayerChatMessage");
if (fieldInfo != null)
{
object value = fieldInfo.GetValue(itemObj);
if (value != null)
{
return value;
}
}
PropertyInfo propertyInfo = AccessTools.Property(itemObj.GetType(), "multiplayerChatMessage");
if (propertyInfo != null)
{
object value2 = propertyInfo.GetValue(itemObj, null);
if (value2 != null)
{
return value2;
}
}
GameObject val = ExtractGameObject(itemObj);
Type type = AccessTools.TypeByName("MultiplayerChatMessage");
if ((Object)(object)val != (Object)null && type != null)
{
Component component = val.GetComponent(type);
if ((Object)(object)component != (Object)null)
{
return component;
}
}
return null;
}
private bool TryRunOriginalManually(MultiplayerChat inst, string finalMessage, string playerName, string nameColor)
{
try
{
EnsureReflection();
if (_miGetFromPool == null)
{
return false;
}
object obj = _miGetFromPool.Invoke(inst, null);
if (obj == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[ChatTranslator] GetChatMessageItemFromPool returned null");
return false;
}
object multiplayerChatMessageFromItem = GetMultiplayerChatMessageFromItem(obj);
if (multiplayerChatMessageFromItem == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[ChatTranslator] multiplayerChatMessage not found on item");
return false;
}
MethodInfo methodInfo = AccessTools.Method(multiplayerChatMessageFromItem.GetType(), "SetMessage", new Type[3]
{
typeof(string),
typeof(string),
typeof(string)
}, (Type[])null);
if (methodInfo == null)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"[ChatTranslator] SetMessage method not found on multiplayerChatMessage");
return false;
}
methodInfo.Invoke(multiplayerChatMessageFromItem, new object[3] { finalMessage, playerName, nameColor });
GameObject val = ExtractGameObject(obj);
Transform val2 = (Transform)((_fiChatHolder != null) ? /*isinst with value type is only supported in some contexts*/: null);
if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null)
{
val.transform.SetParent(val2);
}
if (_miUpdateHideTime != null)
{
_miUpdateHideTime.Invoke(inst, null);
}
return true;
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("[ChatTranslator] TryRunOriginalManually error: " + ex.Message));
return false;
}
}
[IteratorStateMachine(typeof(<TranslateAndRunOriginal>d__24))]
internal IEnumerator TranslateAndRunOriginal(MultiplayerChat inst, string message, string playerName, string nameColor)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <TranslateAndRunOriginal>d__24(0)
{
<>4__this = this,
inst = inst,
message = message,
playerName = playerName,
nameColor = nameColor
};
}
private static ChatMessageItem AsChatMessageItem(object obj)
{
if (obj == null)
{
return null;
}
ChatMessageItem val = (ChatMessageItem)((obj is ChatMessageItem) ? obj : null);
if (val != null)
{
return val;
}
GameObject val2 = (GameObject)((obj is GameObject) ? obj : null);
if ((Object)(object)val2 != (Object)null)
{
val = val2.GetComponent<ChatMessageItem>();
if (val != null)
{
return val;
}
return val2.GetComponentInChildren<ChatMessageItem>(true);
}
Component val3 = (Component)((obj is Component) ? obj : null);
if ((Object)(object)val3 != (Object)null)
{
val = val3.GetComponent<ChatMessageItem>();
if (val != null)
{
return val;
}
return val3.GetComponentInChildren<ChatMessageItem>(true);
}
return null;
}
}
[HarmonyPatch(typeof(MultiplayerChat), "ShowNewMessage", new Type[]
{
typeof(string),
typeof(string),
typeof(string)
})]
public static class MultiplayerChat_ShowNewMessage_Prefix
{
private static bool Prefix(MultiplayerChat __instance, string message, string playerName, string nameColor)
{
try
{
if ((Object)(object)ChatTranslatorPlugin.Instance == (Object)null)
{
return true;
}
((MonoBehaviour)ChatTranslatorPlugin.Instance).StartCoroutine(ChatTranslatorPlugin.Instance.TranslateAndRunOriginal(__instance, message, playerName, nameColor));
return false;
}
catch (Exception ex)
{
ChatTranslatorPlugin.LogInstance.LogWarning((object)("[ChatTranslator] Prefix error: " + ex.Message));
return true;
}
}
}
[Serializable]
public class DeepLResponse
{
public DeepLTranslation[] translations;
}
[Serializable]
public class DeepLTranslation
{
public string detected_source_language;
public string text;
}