Skip to content

Commit

Permalink
override file handling
Browse files Browse the repository at this point in the history
  • Loading branch information
katycat5e committed Dec 13, 2023
1 parent 9646202 commit 49d78b3
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 25 deletions.
5 changes: 5 additions & 0 deletions Data/TranslationData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public void Validate()
new TranslationItem() { Value = englishName }
}
};

public void Concat(TranslationData other)
{
Items.AddRange(other.Items);
}
}

[Serializable]
Expand Down
66 changes: 66 additions & 0 deletions Runtime/LangHelperMain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,56 @@ private static void DrawGUI(UnityModManager.ModEntry entry)
{
Settings.Draw(entry);

GUILayout.Space(5);
GUILayout.Label("NOTE: Changes made to translation overrides may require a game restart");
GUILayout.BeginHorizontal();

if (GUILayout.Button("Reload Overrides", GUILayout.Width(200)))
{
OverrideManager.ReloadOverrides(true);
}

if (GUILayout.Button("Open Folder", GUILayout.Width(200)))
{
if (!Directory.Exists(OverrideManager.OverrideDir))
{
Directory.CreateDirectory(OverrideManager.OverrideDir);
}
System.Diagnostics.Process.Start("explorer.exe", OverrideManager.OverrideDir);
}

GUILayout.EndHorizontal();

foreach (var source in TranslationInjector.Instances)
{
GUILayout.BeginHorizontal();

GUILayout.Label(source.Id);

if (GUILayout.Button("New Override File", GUILayout.Width(200)))
{
OverrideManager.CreateOverrideFile(source.Id);
GuiMessage = $"Created {source.Id} override file";
}

GUI.enabled = OverrideManager.DoesOverrideExist(source.Id);
if (GUILayout.Button("Reload", GUILayout.Width(100)))
{
OverrideManager.ReloadOverrideForSource(source.Id);
GuiMessage = $"Reloaded overrides for {source.Id}";
}

if (GUILayout.Button("Delete", GUILayout.Width(100)))
{
OverrideManager.DeleteOverrideFile(source.Id);
GuiMessage = $"Deleted {source.Id} override file";
}
GUI.enabled = true;

GUILayout.EndHorizontal();
}

GUILayout.Space(5);
if (!string.IsNullOrEmpty(GuiMessage))
{
GUILayout.Label(GuiMessage);
Expand Down Expand Up @@ -102,4 +152,20 @@ public override void Save(UnityModManager.ModEntry modEntry)
Save(this, modEntry);
}
}

[HarmonyPatch]
internal static class UMM_AfterLoadPatch
{
[HarmonyTargetMethod]
public static MethodInfo GetTarget()
{
return typeof(UnityModManager).GetNestedType("GameScripts", BindingFlags.NonPublic).GetMethod("OnAfterLoadMods");
}

[HarmonyPostfix]
public static void AfterLoaded()
{
OverrideManager.ReloadOverrides(false);
}
}
}
117 changes: 117 additions & 0 deletions Runtime/OverrideManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using I2.Loc;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace DVLangHelper.Runtime
{
internal static class OverrideManager
{
public const string OVERRIDE_DIR_NAME = "override";

public static string OverrideDir => Path.Combine(LangHelperMain.Instance.Path, OVERRIDE_DIR_NAME);
public static string OverridePath(string sourceId) => Path.Combine(LangHelperMain.Instance.Path, OVERRIDE_DIR_NAME, $"{sourceId}.csv");

public static bool DoesOverrideExist(string sourceId) => File.Exists(OverridePath(sourceId));

public static void CreateOverrideFile(string sourceId)
{
static string CsvValue(string value)
{
return value.Contains(' ') ? $"\"{value}\"" : value;
}

var source = TranslationInjector.Instances.First(x => x.Id == sourceId);

if (!Directory.Exists(OverrideDir))
{
Directory.CreateDirectory(OverrideDir);
}

var sb = new StringBuilder();

// header
sb.Append("Key,Description");

int langCount = 0;
foreach (var lang in source.Languages)
{
sb.Append(',');
sb.Append(CsvValue(lang.Name));
langCount += 1;
}
sb.AppendLine();

// terms
string termCommas = new string(Enumerable.Repeat(',', langCount).ToArray());
foreach (var term in source.Terms)
{
sb.Append($"{term.Term},{CsvValue(term.Description)}");
sb.AppendLine(termCommas);
}

File.WriteAllText(OverridePath(sourceId), sb.ToString(), Encoding.UTF8);
}

public static void DeleteOverrideFile(string sourceId)
{
string overridePath = OverridePath(sourceId);
if (File.Exists(overridePath))
{
try
{
File.Delete(overridePath);
}
catch (Exception ex)
{
LangHelperMain.Error("Failed to delete override file", ex);
}
}
}

public static void ReloadOverrideForSource(string sourceId)
{
string overrideFilePath = OverridePath(sourceId);
if (File.Exists(overrideFilePath) &&
(TranslationInjector.Instances.FirstOrDefault(i => i.Id == sourceId) is TranslationInjector source))
{
source.AddTranslationsFromCsv(overrideFilePath, true);
LangHelperMain.Log($"Loaded override file for {sourceId}");
}
}

public static void ReloadOverrides(bool reloadAll)
{
CoroutineManager.Start(ReloadOverridesWorker(reloadAll));
}

private static IEnumerator ReloadOverridesWorker(bool reloadAll)
{
foreach (string overrideFilePath in Directory.EnumerateFiles(OverrideDir, "*.csv"))
{
string sourceId = Path.GetFileNameWithoutExtension(overrideFilePath);

if (TranslationInjector.Instances.FirstOrDefault(i => i.Id == sourceId) is TranslationInjector source)
{
if (reloadAll)
{
source.Reload();
}

while (source.PendingWebRequests)
{
yield return new WaitForEndOfFrame();
}

source.AddTranslationsFromCsv(overrideFilePath, true);
LangHelperMain.Log($"Loaded override file for {sourceId}");
}
}
}
}
}
96 changes: 71 additions & 25 deletions Runtime/TranslationInjector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,27 @@ public class TranslationInjector

public static int ReloadTranslationFiles()
{
int totalReloaded = 0;
foreach (var instance in _instances)
{
foreach (var file in instance._csvFiles)
{
switch (file.Type)
{
case CsvFileInfo.SourceType.Local:
instance.AddTranslationsFromCsv(file.Path);
totalReloaded++;
break;
case CsvFileInfo.SourceType.URL:
instance.AddTranslationsFromWebCsv(file.Path);
totalReloaded++;
break;
}
}
instance.Reload();
}
return totalReloaded;
return _instances.Count;
}

public readonly string Id;
private readonly GameObject _sourceHolder;
private readonly LanguageSource _source;
private readonly LanguageSourceData _langData;

public IEnumerable<LanguageData> Languages => _langData.mLanguages;
public IEnumerable<TermData> Terms => _langData.mTerms;

private readonly Dictionary<string, TranslationData> _originalData = new Dictionary<string, TranslationData>();
private readonly List<CsvFileInfo> _csvFiles = new List<CsvFileInfo>(1);

private readonly List<string> _pendingWebRequests = new List<string>();
public bool PendingWebRequests => _pendingWebRequests.Count > 0;

public TranslationInjector(string sourceId)
{
Id = sourceId;
Expand All @@ -59,28 +52,58 @@ public TranslationInjector(string sourceId)
_source = _sourceHolder.AddComponent<LanguageSource>();
_langData = _source.SourceData;

ResetData();

_instances.Add(this);

if (InjectionStarted)
{
PerformInjection();
}
}

public void ResetData()
{
_langData.ClearAllData();

foreach (DVLanguage language in Enum.GetValues(typeof(DVLanguage)))
{
_langData.AddLanguage(language.Name(), language.Code());
_langData.EnableLanguage(language.Name(), true);
}
}

_instances.Add(this);
public void Reload()
{
ResetData();

if (InjectionStarted)
foreach (var file in _csvFiles)
{
PerformInjection();
switch (file.Type)
{
case CsvFileInfo.SourceType.Local:
AddTranslationsFromCsv(file.Path);
break;
case CsvFileInfo.SourceType.URL:
AddTranslationsFromWebCsv(file.Path);
break;
}
}

foreach (var kvp in _originalData)
{
AddTranslations(kvp.Key, kvp.Value, true);
}
}

public void AddTranslationsFromCsv(string csvPath)
public void AddTranslationsFromCsv(string csvPath, bool isOverride = false)
{
try
{
string csvText = LocalizationReader.ReadCSVfile(csvPath, Encoding.UTF8);
_langData.Import_CSV(string.Empty, csvText, eSpreadsheetUpdateMode.Merge);

if (!_csvFiles.Any(f => f.Path == csvPath))
if (!isOverride && !_csvFiles.Any(f => f.Path == csvPath))
{
_csvFiles.Add(new CsvFileInfo(CsvFileInfo.SourceType.Local, csvPath));
}
Expand All @@ -93,6 +116,7 @@ public void AddTranslationsFromCsv(string csvPath)

public void AddTranslationsFromWebCsv(string url)
{
_pendingWebRequests.Add(url);
_source.StartCoroutine(AddWebCsvCoro(url));
}

Expand All @@ -108,11 +132,13 @@ private IEnumerator AddWebCsvCoro(string url)
if (LangHelperMain.Settings.UseCache && File.Exists(cachePath))
{
LangHelperMain.Warning($"Failed to fetch web csv translations @ {url}, using cached");
_pendingWebRequests.Remove(url);
AddTranslationsFromCsv(cachePath);
yield break;
}

LangHelperMain.Error($"Failed to fetch web csv translations @ {url}");
_pendingWebRequests.Remove(url);
yield break;
}

Expand All @@ -124,6 +150,7 @@ private IEnumerator AddWebCsvCoro(string url)
_csvFiles.Add(new CsvFileInfo(CsvFileInfo.SourceType.URL, url));
}

_pendingWebRequests.Remove(url);
LangHelperMain.Log($"Successfully fetched web csv translations from {url}");

if (LangHelperMain.Settings.UseCache)
Expand All @@ -140,13 +167,15 @@ private IEnumerator AddWebCsvCoro(string url)
}
}

public void AddTranslations(string key, TranslationData data)
public void AddTranslations(string key, TranslationData data, bool isOverride = false)
{
AddTranslations(key, data.Items);
AddTranslations(key, data.Items, isOverride);
}

public void AddTranslations(string key, IEnumerable<TranslationItem> items)
public void AddTranslations(string key, IEnumerable<TranslationItem> items, bool isOverride = false)
{
if (!isOverride) RegisterOriginalData(key, items.ToArray());

var term = _langData.AddTerm(key);

foreach (var item in items)
Expand All @@ -156,14 +185,31 @@ public void AddTranslations(string key, IEnumerable<TranslationItem> items)
}
}

public void AddTranslation(string key, DVLanguage language, string value)
public void AddTranslation(string key, DVLanguage language, string value, bool isOverride = false)
{
if (!isOverride) RegisterOriginalData(key, new TranslationItem(language, value));

var term = _langData.AddTerm(key);

int idx = _langData.GetLanguageIndexFromCode(language.Code());
term.SetTranslation(idx, value);
}

private void RegisterOriginalData(string key, params TranslationItem[] items)
{
if (!_originalData.TryGetValue(key, out var data))
{
data = new TranslationData()
{
Items = items.ToList(),
};
_originalData.Add(key, data);
return;
}

data.Items.AddRange(items);
}

public static void StartInjection()
{
foreach (var injector in Instances)
Expand Down

0 comments on commit 49d78b3

Please sign in to comment.