Skip to content

Commit

Permalink
Add basic support for the "Find all references" feature to fonts.
Browse files Browse the repository at this point in the history
  • Loading branch information
VladiStep committed Jan 1, 2024
1 parent 979db25 commit edfa370
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 3 deletions.
2 changes: 2 additions & 0 deletions UndertaleModTool/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,8 @@ private void DisposeGameData()
UpdateLayout();
Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);

UndertaleResourceReferenceMethodsMap.ClearFontFunctionList();

Data.Dispose();
Data = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class TypesForVersion

public static class UndertaleResourceReferenceMap
{
// Don't forget to update `referenceableTypes` as well
private static readonly Dictionary<Type, TypesForVersion[]> typeMap = new()
{
{
Expand Down Expand Up @@ -112,6 +113,28 @@ public static class UndertaleResourceReferenceMap
},
}
},
{
typeof(UndertaleFont),
new[]
{
new TypesForVersion
{
Version = (1, 0, 0),
Types = new[]
{
(typeof(UndertaleCode), "Code")
}
},
new TypesForVersion
{
Version = (2, 2, 1),
Types = new[]
{
(typeof(UndertaleTextureGroupInfo), "Texture groups")
}
},
}
},
{
typeof(UndertaleTexturePageItem),
new[]
Expand Down Expand Up @@ -388,11 +411,13 @@ public static class UndertaleResourceReferenceMap
}
};

// From `typeMap`
private static readonly Dictionary<Type, string> referenceableTypes = new()
{
{ typeof(UndertaleSprite), "Sprites" },
{ typeof(UndertaleBackground), "Backgrounds" },
{ typeof(UndertaleEmbeddedTexture), "Embedded textures" },
{ typeof(UndertaleFont), "Fonts" },
{ typeof(UndertaleTexturePageItem), "Texture page items" },
{ typeof(UndertaleString), "Strings" },
{ typeof(UndertaleGameObject), "Game objects" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using System.Windows;
using UndertaleModLib;
using UndertaleModLib.Decompiler;
using UndertaleModLib.Models;
using static UndertaleModLib.Models.UndertaleSequence;

Expand Down Expand Up @@ -43,6 +44,9 @@ public static class UndertaleResourceReferenceMethodsMap
private static Dictionary<UndertaleCode, HashSet<UndertaleFunction>> funcReferences;
private static Dictionary<UndertaleCode, HashSet<UndertaleVariable>> variReferences;

private static Dictionary<string, AssetIDType[]> fontFunctions;
private static Dictionary<UndertaleCode, HashSet<UndertaleFont>> fontReferences;

private static readonly Dictionary<Type, PredicateForVersion[]> typeMap = new()
{
{
Expand Down Expand Up @@ -331,6 +335,101 @@ IEnumerable<object[]> GetTileLayers()
}
}
},
{
typeof(UndertaleFont),
new[]
{
new PredicateForVersion()
{
Version = (1, 0, 0),
Predicate = (objSrc, types, checkOne) =>
{
if (!types.Contains(typeof(UndertaleCode)))
return null;
if (objSrc is not UndertaleFont obj)
return null;
IEnumerable<UndertaleCode> fontRefs;
if (fontReferences is not null)
{
fontRefs = fontReferences.Where(x => x.Value.Contains(obj))
.Select(x => x.Key);
}
else
{
IEnumerable<UndertaleCode> GetCodeEntries()
{
foreach (var code in data.Code)
{
UndertaleCode fontReference = null;
for (int i = 0; i < code.Instructions.Count; i++)
{
var instr = code.Instructions[i];
string funcName = instr.Function?.Target?.Name?.Content;
if (funcName is null)
continue;
if (!fontFunctions.TryGetValue(funcName, out var argTypes))
continue;
int fontArgIndex;
if (argTypes.Length < 2)
fontArgIndex = 0;
else
fontArgIndex = Array.IndexOf(argTypes, AssetIDType.Font); // This shouldn't return -1
int fontInstrIndex = i - ((fontArgIndex + 1) * 2);
if (fontInstrIndex < 0)
continue;
var fontInstr = code.Instructions[fontInstrIndex];
if (fontInstr.Kind != UndertaleInstruction.Opcode.PushI
|| fontInstr.Type1 != UndertaleInstruction.DataType.Int16
|| fontInstr.Value is not short fontIndex
|| fontIndex < 0 || fontIndex > data.Fonts.Count - 1)
continue;
if (data.Fonts[fontIndex] == obj)
{
fontReference = code;
break;
}
}
if (fontReference is not null)
yield return fontReference;
}
}
fontRefs = GetCodeEntries();
}
if (fontRefs.Any())
return new() { { "Code", checkOne ? fontRefs.ToEmptyArray() : fontRefs.ToArray() } };
else
return null;
}
},
new PredicateForVersion()
{
Version = (2, 2, 1),
Predicate = (objSrc, types, checkOne) =>
{
if (!types.Contains(typeof(UndertaleTextureGroupInfo)))
return null;
if (objSrc is not UndertaleFont obj)
return null;
var textGroups = data.TextureGroupInfo.Where(x => x.Fonts.Any(s => s.Resource == obj));
if (textGroups.Any())
return new() { { "Texture groups", checkOne ? textGroups.ToEmptyArray() : textGroups.ToArray() } };
else
return null;
}
}
}
},
{
typeof(UndertaleTexturePageItem),
new[]
Expand Down Expand Up @@ -1400,7 +1499,6 @@ IEnumerable<object[]> GetPartSysInstances()
};



public static Dictionary<string, List<object>> GetReferencesOfObject(object obj, UndertaleData data, HashSetTypesOverride types, bool checkOne = false)
{
if (obj is null)
Expand All @@ -1409,6 +1507,12 @@ public static Dictionary<string, List<object>> GetReferencesOfObject(object obj,
if (!typeMap.TryGetValue(obj.GetType(), out PredicateForVersion[] predicatesForVer))
return null;

if (!checkOne && fontFunctions is null)
{
var kvpList = AssetTypeResolver.builtin_funcs.Where(x => x.Value.Contains(AssetIDType.Font));
fontFunctions = new(kvpList);
}

UndertaleResourceReferenceMethodsMap.data = data;

var ver = (data.GeneralInfo.Major, data.GeneralInfo.Minor, data.GeneralInfo.Release);
Expand Down Expand Up @@ -1442,7 +1546,11 @@ public static async Task<Dictionary<string, List<object>>> GetUnreferencedObject
{
UndertaleResourceReferenceMethodsMap.data = data;

var ver = (data.GeneralInfo.Major, data.GeneralInfo.Minor, data.GeneralInfo.Release);
if (fontFunctions is null)
{
var kvpList = AssetTypeResolver.builtin_funcs.Where(x => x.Value.Contains(AssetIDType.Font));
fontFunctions = new(kvpList);
}

Dictionary<string, List<object>> outDict = new();

Expand All @@ -1462,13 +1570,17 @@ public static async Task<Dictionary<string, List<object>>> GetUnreferencedObject
stringReferences = new();
funcReferences = new();
variReferences = new();
fontReferences = new();
foreach (var code in data.Code)
{
var strings = new HashSet<UndertaleString>();
var functions = new HashSet<UndertaleFunction>();
var variables = new HashSet<UndertaleVariable>();
foreach (var inst in code.Instructions)
var fonts = new HashSet<UndertaleFont>();
for (int i = 0; i < code.Instructions.Count; i++)
{
var inst = code.Instructions[i];

if (inst.Value is UndertaleResourceById<UndertaleString, UndertaleChunkSTRG> strPtr)
strings.Add(strPtr.Resource);

Expand All @@ -1478,7 +1590,34 @@ public static async Task<Dictionary<string, List<object>>> GetUnreferencedObject
variables.Add(varRef.Target);

if (inst.Function?.Target is not null)
{
functions.Add(inst.Function.Target);

string funcName = inst.Function.Target.Name?.Content;
if (funcName is not null
&& fontFunctions.TryGetValue(funcName, out var argTypes))
{
int fontArgIndex;
if (argTypes.Length < 2)
fontArgIndex = 0;
else
fontArgIndex = Array.IndexOf(argTypes, AssetIDType.Font); // This shouldn't return -1
int fontInstrIndex = i - ((fontArgIndex + 1) * 2);
if (fontInstrIndex >= 0)
{
var fontInstr = code.Instructions[fontInstrIndex];
if (fontInstr.Kind == UndertaleInstruction.Opcode.PushI
&& fontInstr.Type1 == UndertaleInstruction.DataType.Int16
&& fontInstr.Value is short fontIndex
&& fontIndex >= 0 && fontIndex <= data.Fonts.Count - 1)
{
fonts.Add(data.Fonts[fontIndex]);
break;
}
}
}
}

if (inst.Value is UndertaleInstruction.Reference<UndertaleFunction> funcRef && funcRef.Target is not null)
functions.Add(funcRef.Target);
}
Expand All @@ -1489,6 +1628,8 @@ public static async Task<Dictionary<string, List<object>>> GetUnreferencedObject
funcReferences[code] = functions;
if (variables.Count != 0)
variReferences[code] = variables;
if (fonts.Count != 0)
fontReferences[code] = fonts;
}

mainWindow.IsEnabled = false;
Expand Down Expand Up @@ -1586,6 +1727,8 @@ await Task.Run(() =>
return outDict;
}

public static void ClearFontFunctionList() => fontFunctions = null;

private static T[] ToEmptyArray<T>(this IEnumerable<T> _) => Array.Empty<T>();
}
}

0 comments on commit edfa370

Please sign in to comment.