Skip to content

Commit

Permalink
Partial modifiaction of CSV resources.
Browse files Browse the repository at this point in the history
  • Loading branch information
Albeoris committed Jan 13, 2022
1 parent 1fff68f commit 76caa7f
Show file tree
Hide file tree
Showing 10 changed files with 551 additions and 102 deletions.
5 changes: 4 additions & 1 deletion Memoria.FF1/Memoria.FF1.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<GameRegistryPath>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 1173770</GameRegistryPath>
<GamePath Condition="'$(GamePath)' == ''">$([MSBuild]::GetRegistryValueFromView('$(GameRegistryPath)', 'InstallLocation', null, RegistryView.Registry32))</GamePath>
<GamePath Condition="'$(GamePath)' == ''">$([MSBuild]::GetRegistryValueFromView('$(GameRegistryPath)', 'InstallLocation', null, RegistryView.Registry64))</GamePath>
<GamePath Condition="'$(GamePath)' == ''">bin\FF3</GamePath>
<GamePath Condition="'$(GamePath)' == ''">bin\FF1</GamePath>
<OutputPath>$(GamePath)\BepInEx\plugins\</OutputPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
Expand Down Expand Up @@ -102,6 +102,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Shared\BeepInEx\ExtensionMethods.cs" />
<Compile Include="Shared\Configuration\AcceptableDirectoryPath.cs" />
<Compile Include="Shared\Configuration\ApplicationPathConverter.cs" />
<Compile Include="Shared\Configuration\AssetsConfiguration.cs" />
Expand All @@ -110,6 +111,7 @@
<Compile Include="Shared\Configuration\ModConfiguration.cs" />
<Compile Include="Shared\Configuration\SpeedConfiguration.cs" />
<Compile Include="Shared\Core\AssetExtensionResolver.cs" />
<Compile Include="Shared\Core\CsvMerger.cs" />
<Compile Include="Shared\Core\GameEncountersControl.cs" />
<Compile Include="Shared\Core\GameSpeedControl.cs" />
<Compile Include="Shared\Core\InputManager.cs" />
Expand All @@ -121,6 +123,7 @@
<Compile Include="Shared\IL2CPP\ResourceManager_IsLoadAssetCompleted.cs" />
<Compile Include="Shared\IL2CPP\ModComponent.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Shared\Mods\ModIndex.cs" />
<Compile Include="Shared\SingletonInitializer.cs" />
<Compile Include="Shared\TypeRegister.cs" />
</ItemGroup>
Expand Down
44 changes: 44 additions & 0 deletions Memoria.FF1/Shared/BeepInEx/ExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;

namespace Memoria.FFPR.BeepInEx;

public static class ExtensionMethods
{
public static void Deconstruct<TKey, TValue>(this Il2CppSystem.Collections.Generic.KeyValuePair<TKey, TValue> il2cpp, out TKey key, out TValue value)
{
key = il2cpp.Key;
value = il2cpp.Value;
}

public static Dictionary<TKey1, Dictionary<TKey2, TValue>> ToManaged<TKey1, TKey2, TValue>(
this Il2CppSystem.Collections.Generic.Dictionary<TKey1, Il2CppSystem.Collections.Generic.Dictionary<TKey2, TValue>> il2cpp)
{
return il2cpp.ToManaged(k => k, v => v.ToManaged());
}

public static Dictionary<TKey, TValue> ToManaged<TKey, TValue>(this Il2CppSystem.Collections.Generic.Dictionary<TKey, TValue> il2cpp)
{
return il2cpp.ToManaged(k => k, v => v);
}

public static Dictionary<TTargetKey, TTargetValue> ToManaged<TSourceKey, TSourceValue, TTargetKey, TTargetValue>(
this Il2CppSystem.Collections.Generic.Dictionary<TSourceKey, TSourceValue> il2cpp,
Func<TSourceKey, TTargetKey> keySelector,
Func<TSourceValue, TTargetValue> valueSelector)
{
if (il2cpp is null) throw new ArgumentNullException(nameof(il2cpp));
if (keySelector is null) throw new ArgumentNullException(nameof(keySelector));
if (valueSelector is null) throw new ArgumentNullException(nameof(valueSelector));

if (il2cpp.comparer.Pointer != Il2CppSystem.Collections.Generic.EqualityComparer<TSourceKey>.Default.Pointer)
throw new ArgumentException($"The IL2CPP Dictionary uses a non-standard Comparer ([{il2cpp.comparer}]) that cannot be converted to a Managed type.", nameof(il2cpp));

var result = new Dictionary<TTargetKey, TTargetValue>(il2cpp.Count);

foreach ((TSourceKey k, TSourceValue v) in il2cpp)
result.Add(keySelector(k), valueSelector(v));

return result;
}
}
14 changes: 14 additions & 0 deletions Memoria.FF1/Shared/Configuration/AssetsConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ public sealed class AssetsConfiguration
private readonly ConfigEntry<Boolean> _importTextures;
// private readonly ConfigEntry<Boolean> _importBinary; // Cannot import :/

private readonly ConfigEntry<Boolean> ModsEnabled;
private readonly ConfigEntry<String> _modsDirectory;

public AssetsConfiguration(ConfigFile file)
{
ExportEnabled = file.Bind(Section, nameof(ExportEnabled), false,
Expand Down Expand Up @@ -60,6 +63,13 @@ public AssetsConfiguration(ConfigFile file)

// _importBinary = file.Bind(Section, nameof(ImportBinary), true,
// "Import binary resources: .bytes, etc.");

ModsEnabled = file.Bind(Section, nameof(ModsEnabled), true,
$"Overwrite the supported resources from the {nameof(ModsDirectory)}.");

_modsDirectory = file.Bind(Section, nameof(ModsDirectory), "%StreamingAssets%/Mods",
$"Directory from which the supported resources will be updated.",
new AcceptableDirectoryPath(nameof(ModsDirectory)));
}

public String ExportDirectory => ExportEnabled.Value
Expand All @@ -81,6 +91,10 @@ public AssetsConfiguration(ConfigFile file)
public Boolean ImportText => _importText.Value;
public Boolean ImportTextures => _importTextures.Value;
public Boolean ImportBinary => false; // _importBinary.Value;

public String ModsDirectory => ModsEnabled.Value
? AcceptableDirectoryPath.Preprocess(_modsDirectory.Value)
: String.Empty;

public void DisableExport() => ExportEnabled.Value = false;
}
Expand Down
223 changes: 223 additions & 0 deletions Memoria.FF1/Shared/Core/CsvMerger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using Memoria.FFPR.Configuration;
using Memoria.FFPR.IL2CPP;

namespace Memoria.FFPR.Core;

public sealed class CsvMerger
{
private const Char Separator = ',';

private readonly String[] _columnNames;
private readonly Dictionary<String, Int32> _columnNameIndices;
private readonly List<String[]> _rows;
private readonly Dictionary<Int32, Int32> _rowIndices;
private readonly HashSet<Int32> _removedRows = new HashSet<Int32>();

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

using (var sr = new StringReader(csvContent))
{
if (!TryReadContent(sr, out String[] parts))
parts = Array.Empty<String>();
else if (parts[0] != "id")
throw new NotSupportedException($"Not supported CSV-format. Unexpected first column: [{parts[0]}]. Expected: [id]");

HashSet<String> processedColumns = new();
_columnNames = parts;
_columnNameIndices = new(_columnNames.Length);
for (Int32 i = 0; i < _columnNames.Length; i++)
{
String columnName = _columnNames[i];
if (!processedColumns.Add(columnName))
throw new FormatException($"The header contains several columns with the same name: [{columnName}]");

_columnNameIndices.Add(columnName, i);
}

_rows = new List<String[]>();
_rowIndices = new Dictionary<Int32, Int32>();
while (TryReadContent(sr, out parts))
{
Int32 id = Int32.Parse(parts[0], CultureInfo.InvariantCulture);
AddNewRow(id, parts);
}
}
}

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

private void AddNewRow(Int32 id, String[] row)
{
_rowIndices.Add(id, _rows.Count);
_rows.Add(row);
}

private void MergeFile(StreamReader sr)
{
if (!TryReadContent(sr, out String[] parts))
return;

if (parts[0] != "id")
throw new NotSupportedException($"Not supported CSV-format. Unexpected first column: [{parts[0]}]. Expected: [id]");

StringBuilder sb = new();

String[] columnNames = parts;
Int32[] columnIndices = new Int32[columnNames.Length];
HashSet<String> processedColumns = new HashSet<String>();
for (Int32 i = 0; i < columnNames.Length; i++)
{
String columnName = columnNames[i];
if (!processedColumns.Add(columnName))
throw new FormatException($"The header contains several columns with the same name: [{columnName}]");

if (!_columnNameIndices.TryGetValue(columnName, out Int32 columnIndex))
throw new FormatException($"Cannot find index of [{columnName}] column in the full CSV-file.");

columnIndices[i] = columnIndex;
}

while (TryReadContent(sr, out parts))
{
Boolean toRemove = false;
Int32 id = Int32.Parse(parts[0], CultureInfo.InvariantCulture);
if (id < 0)
{
toRemove = true;
id *= -1;
}

if (!_rowIndices.TryGetValue(id, out var rowIndex))
{
if (toRemove)
{
ModComponent.Log.LogWarning($"[Mod] Cannot find row with id [{id}] to remove it.");
continue;
}

if (parts.Length != _columnNames.Length)
throw new FormatException($"Cannot add row with id [{id}]. Expected {_columnNames.Length} columns, but there is {parts.Length}.");

String[] row = new String[_columnNames.Length];
for (Int32 i = 0; i < row.Length; i++)
{
Int32 columnIndex = columnIndices[i];
row[columnIndex] = parts[i];
}

AddNewRow(id, row);
ModComponent.Log.LogInfo($"[Mod] Added new row: {String.Join(",", row)}.");
continue;
}

if (toRemove)
{
if (_removedRows.Add(rowIndex))
{
String[] row = _rows[rowIndex];
ModComponent.Log.LogInfo($"[Mod] Removed existing row [{id}]. {String.Join(",", row)}.");
}

continue;
}

for (Int32 i = 1; i < parts.Length; i++)
{
Int32 columnIndex = columnIndices[i];
String columnName = columnNames[i];
String[] row = _rows[rowIndex];
String oldValue = row[columnIndex];
String newValue = parts[i];
if (oldValue != newValue)
{
row[columnIndex] = newValue;
sb.Append($" {columnName} ({oldValue} -> {newValue})");
}
}

if (sb.Length > 0)
{
ModComponent.Log.LogInfo($"[Mod] Changed row [{id}]. {sb.ToString()}");
sb.Clear();
}
}
}

public String BuildContent()
{
using (StringWriter sw = new StringWriter())
{
foreach (String columnName in _columnNames)
{
sw.Write(columnName);
sw.Write(Separator);
}

sw.WriteLine();

for (int i = 0; i < _rows.Count; i++)
{
if (_removedRows.Contains(i))
continue;

String[] row = _rows[i];
foreach (String data in row)
{
sw.Write(data);
sw.Write(Separator);
}

sw.WriteLine();
}

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

private Boolean TryReadContent(TextReader reader, out String[] parts)
{
while (true)
{
String line = reader.ReadLine();
if (line is null)
{
parts = null;
return false;
}

if (!String.IsNullOrWhiteSpace(line))
{
parts = line.Split(Separator);
return true;
}
}
}
}
23 changes: 10 additions & 13 deletions Memoria.FF1/Shared/IL2CPP/EncounterLot_CheckEncount.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using Last.Management;
using Last.Map;
using Memoria.FFPR.Configuration;
Expand All @@ -12,15 +12,15 @@
using Exception = System.Exception;
using File = System.IO.File;
using IntPtr = System.IntPtr;
using Object = Il2CppSystem.Object;
using Object = System.Object;
using Path = System.IO.Path;

namespace Memoria.FFPR.IL2CPP
{
[HarmonyPatch(typeof(ContentCatalogData), nameof(ContentCatalogData.CreateLocator))]
public sealed class ContentCatalogData_CreateLocator : Il2CppSystem.Object
{
private static readonly Dictionary<Object, Object> KnownCatalogs = new();
private static readonly Dictionary<String, Dictionary<String,String>> KnownCatalogs = new();

public ContentCatalogData_CreateLocator(IntPtr ptr) : base(ptr)
{
Expand All @@ -30,17 +30,14 @@ public static String GetFileExtension(String assetAddress)
{
return GetFileExtension("AddressablesMainContentCatalog", assetAddress);
}

public static String GetFileExtension(String catalogName, String assetAddress)
{
if (!KnownCatalogs.ContainsKey(catalogName))
return String.Empty;

Dictionary<Object, Object> catalog = KnownCatalogs[catalogName].Cast<Dictionary<Object, Object>>();
if (!catalog.ContainsKey(assetAddress))
return String.Empty;

return catalog[assetAddress].ToString();
return
KnownCatalogs.TryGetValue(catalogName, out var dictionary)
&& dictionary.TryGetValue(assetAddress, out var extension)
? extension
: String.Empty;
}

public static void Prefix(ContentCatalogData __instance)
Expand All @@ -49,7 +46,7 @@ public static void Prefix(ContentCatalogData __instance)
if (KnownCatalogs.ContainsKey(locatorId))
return;

Dictionary<Object, Object> extensions = new();
Dictionary<String, String> extensions = new();
KnownCatalogs.Add(locatorId, extensions);

try
Expand Down
Loading

0 comments on commit 76caa7f

Please sign in to comment.