Skip to content

Commit

Permalink
Mods: Partial .txt support (Messages)
Browse files Browse the repository at this point in the history
  • Loading branch information
Albeoris committed Mar 12, 2022
1 parent 35d1944 commit 5562050
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Memoria.FF1/Memoria.FF1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Compile Include="Shared\Configuration\Scopes\SavesConfiguration.cs" />
<Compile Include="Shared\Configuration\Scopes\SpeedConfiguration.cs" />
<Compile Include="Shared\Core\AssetExtensionResolver.cs" />
<Compile Include="Shared\Core\CSV\MessageMerger.cs" />
<Compile Include="Shared\Core\GameBattleAtbControl.cs" />
<Compile Include="Shared\Core\GameEncountersControl.cs" />
<Compile Include="Shared\Core\GameEntitiesControl.cs" />
Expand All @@ -51,6 +52,7 @@
<Compile Include="Shared\Core\InputManager.cs" />
<Compile Include="Shared\Core\ModConstants.cs" />
<Compile Include="Shared\Core\CSV\CsvMerger.cs" />
<Compile Include="Shared\Core\OrderedDictionary.cs" />
<Compile Include="Shared\Core\OrderedSet.cs" />
<Compile Include="Shared\Core\PathUtility.cs" />
<Compile Include="Shared\Core\SafeComponent.cs" />
Expand Down
119 changes: 119 additions & 0 deletions Memoria.FF1/Shared/Core/CSV/MessageMerger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.IO;
using Memoria.FFPR.Configuration;
using Memoria.FFPR.IL2CPP;

namespace Memoria.FFPR.Core;

public sealed class MessageMerger
{
private const Char Separator = '\t';
private readonly OrderedDictionary<String, String> _rows = new();

public MessageMerger(String txtContent)
{
if (txtContent is null) throw new ArgumentNullException(nameof(txtContent));
if (txtContent == String.Empty) throw new ArgumentException(nameof(txtContent));

using (var sr = new StringReader(txtContent))
{
while (TryReadContent(sr, out String key, out String value))
_rows.AddOrUpdate(key, value);
}
}

public void MergeFiles(IReadOnlyList<String> filePaths)
{
if (filePaths is null) throw new ArgumentNullException(nameof(filePaths));

foreach (String fullPath in filePaths)
{
try
{
String shortPath = ApplicationPathConverter.ReturnPlaceholders(fullPath);
ModComponent.Log.LogInfo($"[Mod] Merging data from {shortPath}");

using (StreamReader sr = File.OpenText(fullPath))
MergeFile(sr);
}
catch (Exception ex)
{
throw new ArgumentException($"Failed to merge data from {fullPath}", nameof(fullPath), ex);
}
}
}

public String BuildContent()
{
using (StringWriter sw = new StringWriter())
{
foreach ((String key, String value) in _rows)
sw.WriteLine($"{key}{Separator}{value}");

sw.Flush();
return sw.ToString();
}
}

private void MergeFile(StreamReader sr)
{
while (TryReadContent(sr, out String key, out String value))
{
if (_rows.TryReplace(key, value, out var previousValue))
{
if (previousValue != value)
ModComponent.Log.LogInfo($"[Mod] Changed message [{key}]: [{previousValue}] -> [{value}]");
continue;
}

if (_rows.TryAdd(key, value))
{
ModComponent.Log.LogInfo($"[Mod] Added new message [{key}]: [{value}]");
continue;
}

throw new InvalidOperationException("Collection is out of sync.");
}
}

private Boolean TryReadContent(TextReader reader, out String key, out String value)
{
while (true)
{
String line = reader.ReadLine();
if (line is null)
break;

if (String.IsNullOrWhiteSpace(line))
continue;

String[] parts = line.Split(Separator);
switch (parts.Length)
{
case 1:
{
key = parts[0];
value = String.Empty;
break;
}
case 2:
{
key = parts[0];
value = parts[1];
break;
}
default:
{
throw new FormatException($"Unexpected line in .txt file: {line}");
}
}

return true;
}

key = null;
value = null;
return false;
}
}
82 changes: 82 additions & 0 deletions Memoria.FF1/Shared/Core/OrderedDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace Memoria.FFPR.Core;

public sealed class OrderedDictionary<TKey, TValue> : IEnumerable<(TKey key, TValue value)>
{
private readonly Dictionary<TKey, (Int32 index, TValue value)> _set;
private readonly List<(TKey key, TValue value)> _list;

public OrderedDictionary()
: this(EqualityComparer<TKey>.Default)
{
}

public OrderedDictionary(IEqualityComparer<TKey> comparer)
{
_set = new Dictionary<TKey, (Int32 index, TValue value)>(comparer);
_list = new List<(TKey key, TValue value)>();
}

public void AddOrUpdate(TKey key, TValue value)
{
if (TryAdd(key, value)) return;
if (TryReplace(key, value, out _)) return;
throw new InvalidOperationException("Collection is out of sync.");
}

public Boolean TryAdd(TKey key, TValue value)
{
if (_set.ContainsKey(key))
return false;

_set.Add(key, (_list.Count, value));
_list.Add((key, value));
return true;
}

public Boolean TryReplace(TKey key, TValue value, out TValue previousValue)
{
if (!_set.TryGetValue(key, out var pair))
{
previousValue = default;
return false;
}

previousValue = pair.value;
_set[key] = (pair.index, value);
_list[pair.index] = (key, value);
return true;
}

public Boolean TryRemove(TKey key, out TValue value)
{
if (!_set.TryGetValue(key, out var pair))
{
value = default;
return false;
}

_set.Remove(key);
_list.RemoveAt(pair.index);
value = pair.value;
return true;
}

public Boolean ContainsKey(TKey key)
{
return _set.ContainsKey(key);
}

public IEnumerator<(TKey key, TValue value)> GetEnumerator()
{
return _list.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ private static Boolean TryModAsset(String type, AssetsConfiguration config, Obje
case "UnityEngine.TextAsset":
{
String fullPath = modPath.Last();
if (Path.GetExtension(fullPath) == ".csv")
String fileExtension = Path.GetExtension(fullPath);
if (fileExtension == ".csv")
{
TextAsset textAsset = assetObject.Cast<TextAsset>();

Expand All @@ -383,6 +384,15 @@ private static Boolean TryModAsset(String type, AssetsConfiguration config, Obje

newAsset = new TextAsset(merger.BuildContent());
}
else if (fileExtension == ".txt" && fullPath.Contains("Assets/GameAssets/Serial/Data/Message/"))
{
TextAsset textAsset = assetObject.Cast<TextAsset>();

MessageMerger merger = new(textAsset.text);
merger.MergeFiles(modPath);

newAsset = new TextAsset(merger.BuildContent());
}
else
{
newAsset = ImportTextAsset(fullPath);
Expand Down

0 comments on commit 5562050

Please sign in to comment.