using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("GTFOAutoTranslator")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GTFOAutoTranslator")]
[assembly: AssemblyTitle("GTFOAutoTranslator")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
}
namespace GTFOAutoTranslator
{
internal sealed class LibreTranslateClient
{
private sealed class TranslationRequest
{
[JsonPropertyName("q")]
public string Q { get; set; } = "";
[JsonPropertyName("source")]
public string Source { get; set; } = "auto";
[JsonPropertyName("target")]
public string Target { get; set; } = "en";
[JsonPropertyName("format")]
public string Format { get; set; } = "text";
[JsonPropertyName("api_key")]
public string? ApiKey { get; set; }
}
private sealed class TranslationResponse
{
[JsonPropertyName("translatedText")]
public string? TranslatedText { get; set; }
}
private static readonly HttpClient Http = new HttpClient
{
Timeout = TimeSpan.FromSeconds(12.0)
};
private readonly string _endpoint;
private readonly string _apiKey;
private readonly string _target;
public LibreTranslateClient(string endpoint, string apiKey, string target)
{
_endpoint = endpoint.Trim();
_apiKey = apiKey.Trim();
_target = (string.IsNullOrWhiteSpace(target) ? "en" : target.Trim());
}
public async Task<string> TranslateAsync(string text)
{
TranslationRequest value = new TranslationRequest
{
Q = text,
Source = "auto",
Target = _target,
Format = "text",
ApiKey = (string.IsNullOrWhiteSpace(_apiKey) ? null : _apiKey)
};
using HttpResponseMessage response = await Http.PostAsJsonAsync(_endpoint, value);
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync<TranslationResponse>((JsonSerializerOptions?)null, default(CancellationToken)))?.TranslatedText ?? text;
}
}
[BepInPlugin("kai.gtfo.autotranslator", "GTFO Auto Translator", "0.2.0")]
public sealed class Plugin : BasePlugin
{
public const string PluginGuid = "kai.gtfo.autotranslator";
public const string PluginName = "GTFO Auto Translator";
public const string PluginVersion = "0.2.0";
internal static ManualLogSource Logger;
internal static TranslatorConfig Settings;
public override void Load()
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Expected O, but got Unknown
Logger = ((BasePlugin)this).Log;
Settings = new TranslatorConfig(((BasePlugin)this).Config);
((BasePlugin)this).AddComponent<TranslationBehaviour>();
ManualLogSource log = ((BasePlugin)this).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(9, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("GTFO Auto Translator");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.2.0");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded.");
}
log.LogInfo(val);
}
}
internal sealed class TranslatorConfig
{
public ConfigEntry<bool> Enabled { get; }
public ConfigEntry<string> ToggleKey { get; }
public ConfigEntry<bool> ShowStatusIndicator { get; }
public ConfigEntry<float> StatusIndicatorSeconds { get; }
public ConfigEntry<string> Endpoint { get; }
public ConfigEntry<string> ApiKey { get; }
public ConfigEntry<string> TargetLanguage { get; }
public ConfigEntry<float> ScanInterval { get; }
public ConfigEntry<int> MinimumLength { get; }
public ConfigEntry<bool> TranslateAsciiOnly { get; }
public ConfigEntry<bool> PreserveRichText { get; }
public ConfigEntry<int> MaxRequestsPerMinute { get; }
public ConfigEntry<bool> LogTranslations { get; }
public ConfigEntry<bool> PersistentCache { get; }
public ConfigEntry<float> CacheSaveInterval { get; }
public ConfigEntry<bool> OnlyTranslateMatchingHierarchy { get; }
public ConfigEntry<bool> RequireHierarchyMatchForAscii { get; }
public ConfigEntry<string> HierarchyIncludePatterns { get; }
public ConfigEntry<string> HierarchyExcludePatterns { get; }
public TranslatorConfig(ConfigFile config)
{
Enabled = config.Bind<bool>("General", "Enabled", true, "Enable or disable automatic translation.");
ToggleKey = config.Bind<string>("General", "ToggleKey", "F8", "Unity KeyCode used to toggle translation while in game. Example: F8, F9, Insert.");
ShowStatusIndicator = config.Bind<bool>("General", "ShowStatusIndicator", true, "Show a small on-screen translator status indicator.");
StatusIndicatorSeconds = config.Bind<float>("General", "StatusIndicatorSeconds", 3f, "How long toggle/translation status messages stay fully visible.");
TargetLanguage = config.Bind<string>("General", "TargetLanguage", "en", "LibreTranslate target language code. Use 'en' for English.");
ScanInterval = config.Bind<float>("General", "ScanIntervalSeconds", 0.4f, "How often active TextMeshPro UI is checked.");
MinimumLength = config.Bind<int>("General", "MinimumTextLength", 2, "Ignore strings shorter than this.");
TranslateAsciiOnly = config.Bind<bool>("Detection", "TranslateAsciiOnlyText", false, "Also submit ASCII-only text. Useful for French/Spanish/German, but use hierarchy filters to avoid normal English UI.");
PreserveRichText = config.Bind<bool>("Detection", "PreserveRichTextTags", true, "Temporarily protects common TextMeshPro rich-text tags while translating.");
OnlyTranslateMatchingHierarchy = config.Bind<bool>("Filtering", "OnlyTranslateMatchingHierarchy", false, "When true, all text must match at least one HierarchyIncludePatterns entry before it can be translated.");
RequireHierarchyMatchForAscii = config.Bind<bool>("Filtering", "RequireHierarchyMatchForAsciiOnly", true, "When ASCII-only translation is enabled, require a hierarchy include match. Recommended to prevent normal GTFO English UI from being submitted.");
HierarchyIncludePatterns = config.Bind<string>("Filtering", "HierarchyIncludePatterns", "", "Semicolon-separated case-insensitive fragments matched against the TMP object's Unity hierarchy. Example: MyMod;CustomMenu;ChineseUI");
HierarchyExcludePatterns = config.Bind<string>("Filtering", "HierarchyExcludePatterns", "", "Semicolon-separated hierarchy fragments that should never be translated.");
PersistentCache = config.Bind<bool>("Cache", "PersistentCache", true, "Save successful translations to disk and reuse them on future launches.");
CacheSaveInterval = config.Bind<float>("Cache", "CacheSaveIntervalSeconds", 5f, "How often new cached translations are flushed to disk.");
Endpoint = config.Bind<string>("LibreTranslate", "Endpoint", "http://localhost:5000/translate", "LibreTranslate /translate endpoint. Local self-hosting is recommended.");
ApiKey = config.Bind<string>("LibreTranslate", "ApiKey", "", "API key if your LibreTranslate server requires one.");
MaxRequestsPerMinute = config.Bind<int>("LibreTranslate", "MaxRequestsPerMinute", 60, "Simple request-rate limit to avoid hammering the translation server.");
LogTranslations = config.Bind<bool>("Debug", "LogTranslations", false, "Log original and translated strings in BepInEx console/log.");
}
}
internal readonly record struct ProtectedText(string Text, Dictionary<string, string> Tokens);
internal static class RichTextProtector
{
private static readonly Regex TagRegex = new Regex("<[^>]+>", RegexOptions.Compiled);
public static ProtectedText Protect(string input)
{
Dictionary<string, string> tokens = new Dictionary<string, string>();
int index = 0;
return new ProtectedText(TagRegex.Replace(input, delegate(Match match)
{
string text = $"__GTFO_TAG_{index++}__";
tokens[text] = match.Value;
return text;
}), tokens);
}
public static string Restore(string input, Dictionary<string, string> tokens)
{
foreach (KeyValuePair<string, string> token in tokens)
{
input = input.Replace(token.Key, token.Value, StringComparison.Ordinal);
}
return input;
}
public static string StripTags(string input)
{
return TagRegex.Replace(input, string.Empty);
}
}
public sealed class TranslationBehaviour : MonoBehaviour
{
private sealed record TranslationResult(TMP_Text Target, string Original, string Translated, bool Success);
private static readonly ConcurrentQueue<TranslationResult> Completed = new ConcurrentQueue<TranslationResult>();
private static readonly HashSet<string> Pending = new HashSet<string>(StringComparer.Ordinal);
private static readonly Dictionary<string, string> MemoryCache = new Dictionary<string, string>(StringComparer.Ordinal);
private static readonly Dictionary<int, string> LastApplied = new Dictionary<int, string>();
private static readonly object Sync = new object();
private readonly Queue<DateTime> _requestTimes = new Queue<DateTime>();
private float _nextScan;
private float _nextCacheSave;
private float _statusUntil;
private string _statusMessage = "Translator ready";
private LibreTranslateClient? _client;
private TranslationCacheStore? _diskCache;
private KeyCode _toggleKey = (KeyCode)289;
public TranslationBehaviour(IntPtr ptr)
: base(ptr)
{
}//IL_001c: Unknown result type (might be due to invalid IL or missing references)
private void Start()
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Expected O, but got Unknown
_client = new LibreTranslateClient(Plugin.Settings.Endpoint.Value, Plugin.Settings.ApiKey.Value, Plugin.Settings.TargetLanguage.Value);
if (!Enum.TryParse<KeyCode>(Plugin.Settings.ToggleKey.Value, ignoreCase: true, out _toggleKey))
{
_toggleKey = (KeyCode)289;
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Unknown ToggleKey '");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Plugin.Settings.ToggleKey.Value);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'. Falling back to F8.");
}
logger.LogWarning(val);
}
if (Plugin.Settings.PersistentCache.Value)
{
_diskCache = new TranslationCacheStore(Plugin.Settings.TargetLanguage.Value);
_diskCache.Load();
lock (Sync)
{
foreach (KeyValuePair<string, string> entry in _diskCache.Entries)
{
MemoryCache[entry.Key] = entry.Value;
}
}
SetStatus($"Loaded {_diskCache.Count} cached translations");
}
else
{
SetStatus("Translator ready");
}
}
private void Update()
{
ApplyCompleted();
HandleToggleKey();
SaveCacheWhenDue();
if (Plugin.Settings.Enabled.Value && !(Time.unscaledTime < _nextScan))
{
_nextScan = Time.unscaledTime + Math.Max(0.1f, Plugin.Settings.ScanInterval.Value);
ScanVisibleText();
}
}
private void OnDestroy()
{
_diskCache?.SaveIfDirty();
}
private void OnApplicationQuit()
{
_diskCache?.SaveIfDirty();
}
private void OnGUI()
{
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_011a: Unknown result type (might be due to invalid IL or missing references)
if (Plugin.Settings.ShowStatusIndicator.Value)
{
bool value = Plugin.Settings.Enabled.Value;
int num = 0;
int value2 = 0;
lock (Sync)
{
num = Pending.Count;
value2 = MemoryCache.Count;
}
if (!(!(Time.unscaledTime <= _statusUntil) && value) || num != 0)
{
string text = (value ? $"GTFO Translator: ON | Pending {num} | Cache {value2}\n{_statusMessage}" : $"GTFO Translator: OFF | Press {_toggleKey} to enable");
GUI.Box(new Rect(16f, 16f, 420f, 52f), text);
}
}
}
private void HandleToggleKey()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Expected O, but got Unknown
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Expected O, but got Unknown
bool flag = default(bool);
try
{
if (Input.GetKeyDown(_toggleKey))
{
Plugin.Settings.Enabled.Value = !Plugin.Settings.Enabled.Value;
SetStatus(Plugin.Settings.Enabled.Value ? "Translation enabled" : "Translation disabled");
ManualLogSource logger = Plugin.Logger;
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(21, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Runtime translation ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Plugin.Settings.Enabled.Value ? "enabled" : "disabled");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
}
logger.LogInfo(val);
}
}
catch (Exception ex)
{
ManualLogSource logger2 = Plugin.Logger;
BepInExDebugLogInterpolatedStringHandler val2 = new BepInExDebugLogInterpolatedStringHandler(25, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Toggle key check failed: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
}
logger2.LogDebug(val2);
}
}
private void SaveCacheWhenDue()
{
if (_diskCache != null && !(Time.unscaledTime < _nextCacheSave))
{
_nextCacheSave = Time.unscaledTime + Math.Max(1f, Plugin.Settings.CacheSaveInterval.Value);
_diskCache.SaveIfDirty();
}
}
private void ApplyCompleted()
{
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Expected O, but got Unknown
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Expected O, but got Unknown
TranslationResult result;
while (Completed.TryDequeue(out result))
{
lock (Sync)
{
Pending.Remove(result.Original);
if (result.Success && !string.IsNullOrWhiteSpace(result.Translated))
{
MemoryCache[result.Original] = result.Translated;
_diskCache?.Put(result.Original, result.Translated);
}
}
if (!result.Success || (Object)(object)result.Target == (Object)null)
{
continue;
}
bool flag;
try
{
if (!(result.Target.text == result.Original))
{
continue;
}
result.Target.text = result.Translated;
LastApplied[((Object)result.Target).GetInstanceID()] = result.Translated;
SetStatus("Translated UI text");
if (Plugin.Settings.LogTranslations.Value)
{
ManualLogSource logger = Plugin.Logger;
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(20, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Translated: '");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(result.Original);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' -> '");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(result.Translated);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'");
}
logger.LogInfo(val);
}
}
catch (Exception ex)
{
ManualLogSource logger2 = Plugin.Logger;
BepInExDebugLogInterpolatedStringHandler val2 = new BepInExDebugLogInterpolatedStringHandler(29, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Could not apply translation: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
}
logger2.LogDebug(val2);
}
}
}
private void ScanVisibleText()
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
TMP_Text[] array;
try
{
array = Il2CppArrayBase<TMP_Text>.op_Implicit(Object.FindObjectsOfType<TMP_Text>());
}
catch (Exception ex)
{
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(16, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("UI scan failed: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
logger.LogWarning(val);
return;
}
TMP_Text[] array2 = array;
foreach (TMP_Text val2 in array2)
{
if ((Object)(object)val2 == (Object)null || !((Behaviour)val2).isActiveAndEnabled)
{
continue;
}
string text = val2.text;
if (!ShouldTranslate(val2, text))
{
continue;
}
int instanceID = ((Object)val2).GetInstanceID();
if (LastApplied.TryGetValue(instanceID, out string value) && value == text)
{
continue;
}
string value2;
lock (Sync)
{
MemoryCache.TryGetValue(text, out value2);
}
if (!string.IsNullOrEmpty(value2))
{
val2.text = value2;
LastApplied[instanceID] = value2;
continue;
}
lock (Sync)
{
if (Pending.Contains(text) || !CanMakeRequest())
{
continue;
}
Pending.Add(text);
MarkRequest();
goto IL_0148;
}
IL_0148:
TranslateAsync(val2, text);
}
}
private async Task TranslateAsync(TMP_Text target, string original)
{
try
{
if (_client == null)
{
Completed.Enqueue(new TranslationResult(target, original, original, Success: false));
return;
}
ProtectedText protectedText = (Plugin.Settings.PreserveRichText.Value ? RichTextProtector.Protect(original) : new ProtectedText(original, new Dictionary<string, string>()));
string text = RichTextProtector.Restore(await _client.TranslateAsync(protectedText.Text), protectedText.Tokens);
bool flag = !string.IsNullOrWhiteSpace(text) && !string.Equals(text, original, StringComparison.Ordinal);
if (!flag)
{
text = original;
}
Completed.Enqueue(new TranslationResult(target, original, text, flag));
}
catch (Exception ex)
{
ManualLogSource logger = Plugin.Logger;
bool flag2 = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(20, 1, ref flag2);
if (flag2)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Translation failed: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
logger.LogWarning(val);
Completed.Enqueue(new TranslationResult(target, original, original, Success: false));
}
}
private static bool ShouldTranslate(TMP_Text target, string? text)
{
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
string text2 = RichTextProtector.StripTags(text).Trim();
if (text2.Length < Plugin.Settings.MinimumLength.Value)
{
return false;
}
if (Regex.IsMatch(text2, "^[\\d\\s\\p{P}\\p{S}_]+$"))
{
return false;
}
string value = BuildHierarchy(target);
bool flag = MatchesAny(value, Plugin.Settings.HierarchyIncludePatterns.Value);
if (MatchesAny(value, Plugin.Settings.HierarchyExcludePatterns.Value))
{
return false;
}
if (Plugin.Settings.OnlyTranslateMatchingHierarchy.Value && !flag)
{
return false;
}
if (text2.Any((char c) => c > '\u007f' && char.IsLetter(c)))
{
return true;
}
if (!Plugin.Settings.TranslateAsciiOnly.Value)
{
return false;
}
if (Plugin.Settings.RequireHierarchyMatchForAscii.Value && !flag)
{
return false;
}
return true;
}
private static string BuildHierarchy(TMP_Text target)
{
try
{
List<string> list = new List<string>(8);
Transform val = target.transform;
int num = 0;
while ((Object)(object)val != (Object)null && num++ < 16)
{
list.Add(((Object)val).name ?? string.Empty);
val = val.parent;
}
list.Reverse();
return string.Join("/", list);
}
catch
{
return ((Object)target).name ?? string.Empty;
}
}
private static bool MatchesAny(string value, string patternList)
{
if (string.IsNullOrWhiteSpace(patternList))
{
return false;
}
string[] array = patternList.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (string value2 in array)
{
if (value.Contains(value2, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private bool CanMakeRequest()
{
DateTime dateTime = DateTime.UtcNow.AddMinutes(-1.0);
while (_requestTimes.Count > 0 && _requestTimes.Peek() < dateTime)
{
_requestTimes.Dequeue();
}
return _requestTimes.Count < Math.Max(1, Plugin.Settings.MaxRequestsPerMinute.Value);
}
private void MarkRequest()
{
_requestTimes.Enqueue(DateTime.UtcNow);
}
private void SetStatus(string message)
{
_statusMessage = message;
_statusUntil = Time.unscaledTime + Math.Max(0.5f, Plugin.Settings.StatusIndicatorSeconds.Value);
}
}
internal sealed class TranslationCacheStore
{
private readonly string _path;
private readonly Dictionary<string, string> _entries = new Dictionary<string, string>(StringComparer.Ordinal);
private bool _dirty;
public int Count => _entries.Count;
public IReadOnlyDictionary<string, string> Entries => _entries;
public TranslationCacheStore(string targetLanguage)
{
string text = string.Concat((targetLanguage ?? "en").Where((char c) => char.IsLetterOrDigit(c) || c == '-' || c == '_'));
if (string.IsNullOrWhiteSpace(text))
{
text = "en";
}
_path = Path.Combine(Paths.ConfigPath, "GTFOAutoTranslator.cache." + text + ".json");
}
public void Load()
{
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Expected O, but got Unknown
try
{
if (!File.Exists(_path))
{
return;
}
Dictionary<string, string> dictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(_path));
if (dictionary == null)
{
return;
}
_entries.Clear();
foreach (KeyValuePair<string, string> item in dictionary)
{
if (!string.IsNullOrWhiteSpace(item.Key) && !string.IsNullOrWhiteSpace(item.Value))
{
_entries[item.Key] = item.Value;
}
}
}
catch (Exception ex)
{
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not load translation cache: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
logger.LogWarning(val);
}
}
public bool TryGet(string original, out string translated)
{
return _entries.TryGetValue(original, out translated);
}
public void Put(string original, string translated)
{
if (!string.IsNullOrWhiteSpace(original) && !string.IsNullOrWhiteSpace(translated) && (!_entries.TryGetValue(original, out string value) || !(value == translated)))
{
_entries[original] = translated;
_dirty = true;
}
}
public void SaveIfDirty()
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Expected O, but got Unknown
if (!_dirty)
{
return;
}
try
{
Directory.CreateDirectory(Path.GetDirectoryName(_path));
string text = _path + ".tmp";
string contents = JsonSerializer.Serialize(_entries, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(text, contents);
File.Move(text, _path, overwrite: true);
_dirty = false;
}
catch (Exception ex)
{
ManualLogSource logger = Plugin.Logger;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(34, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not save translation cache: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
logger.LogWarning(val);
}
}
}
}