Skip to content

Commit

Permalink
Merge pull request #1241 from VladiStep/buildSubFuncCacheImprovements
Browse files Browse the repository at this point in the history
  • Loading branch information
Grossley authored Mar 26, 2023
2 parents 5b6f883 + 76aa3e8 commit b0000b4
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 31 deletions.
89 changes: 65 additions & 24 deletions UndertaleModLib/Decompiler/Decompiler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
Expand Down Expand Up @@ -3769,11 +3770,17 @@ private static string MakeLocalVars(DecompileContext context, string decompiledC
return result;
}

public static string Decompile(UndertaleCode code, GlobalDecompileContext globalContext)
public static string Decompile(UndertaleCode code, GlobalDecompileContext globalContext, Action<string> msgDelegate = null)
{
globalContext.DecompilerWarnings.Clear();
DecompileContext context = new DecompileContext(globalContext, code);

if (msgDelegate is not null)
msgDelegate("Building the cache of all sub-functions...");
BuildSubFunctionCache(globalContext.Data);
if (msgDelegate is not null)
msgDelegate("Decompiling, please wait... This can take a while on complex scripts.");

try
{
if (globalContext.Data != null && globalContext.Data.ToolInfo.ProfileMode)
Expand Down Expand Up @@ -3826,40 +3833,74 @@ public static void BuildSubFunctionCache(UndertaleData data)
// Use the cache so this only gets calculated once
if (data == null || !data.IsVersionAtLeast(2, 3) || data.KnownSubFunctions != null)
return;
foreach (var func in data.Functions)
{
if (data.Code.ByName(func.Name.Content) != null)
func.Autogenerated = true;
}
data.KnownSubFunctions = new Dictionary<string, UndertaleFunction>();
GlobalDecompileContext globalDecompileContext = new GlobalDecompileContext(data, false);
Parallel.ForEach(data.GlobalInitScripts, globalScript =>

// There's no "ConcurrentHashSet<>"; values aren't used.
ConcurrentDictionary<string, string> processingCodeList = new();
byte elapsedSec = 1;
Task mainTask = Task.Run(() =>
{
UndertaleCode scriptCode = globalScript.Code;
try
HashSet<string> codeNames = new(data.Code.Select(c => c.Name?.Content));
foreach (var func in data.Functions)
{
if (codeNames.Contains(func.Name.Content))
func.Autogenerated = true;
}
data.KnownSubFunctions = new Dictionary<string, UndertaleFunction>();
GlobalDecompileContext globalDecompileContext = new GlobalDecompileContext(data, false);

Parallel.ForEach(data.GlobalInitScripts, globalScript =>
{
DecompileContext childContext = new DecompileContext(globalDecompileContext, scriptCode, false);
childContext.DisableAnonymousFunctionNameResolution = true;
Dictionary<uint, Block> blocks2 = PrepareDecompileFlow(scriptCode, new List<uint>() { 0 });
DecompileFromBlock(childContext, blocks2, blocks2[0]);
List<Statement> statements = HLDecompile(childContext, blocks2, blocks2[0], blocks2[scriptCode.Length / 4]);
foreach (Statement stmt2 in statements)
UndertaleCode scriptCode = globalScript.Code;
processingCodeList[scriptCode.Name.Content] = null;
try
{
if (stmt2 is AssignmentStatement assign &&
assign.Value is FunctionDefinition funcDef)
DecompileContext childContext = new DecompileContext(globalDecompileContext, scriptCode, false);
childContext.DisableAnonymousFunctionNameResolution = true;
Dictionary<uint, Block> blocks2 = PrepareDecompileFlow(scriptCode, new List<uint>() { 0 });
DecompileFromBlock(childContext, blocks2, blocks2[0]);
List<Statement> statements = HLDecompile(childContext, blocks2, blocks2[0], blocks2[scriptCode.Length / 4]);
foreach (Statement stmt2 in statements)
{
lock (data.KnownSubFunctions)
if (stmt2 is AssignmentStatement assign &&
assign.Value is FunctionDefinition funcDef)
{
data.KnownSubFunctions.Add(assign.Destination.Var.Name.Content, funcDef.Function);
lock (data.KnownSubFunctions)
{
data.KnownSubFunctions.Add(assign.Destination.Var.Name.Content, funcDef.Function);
}
}
}
}
}
catch (Exception e)
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}

processingCodeList.Remove(scriptCode.Name.Content, out _);
});

elapsedSec = 3 * 60;
});

Task timeoutTask = Task.Run(async () =>
{
while (true)
{
Debug.WriteLine(e.ToString());
await Task.Delay(1000);

if (++elapsedSec > 3 * 60)
return;
}
});

// If the timeout task ended earlier than the main task
if (Task.WaitAny(mainTask, timeoutTask) == 1)
{
throw new TimeoutException("The building cache process hung.\n" +
"The function code entries that didn't manage to decompile:\n" +
String.Join('\n', processingCodeList.Keys) + "\n\n" +
"You should save the game data (if it's necessary) and re-open the app.\n");
}
}

private static void DoTypePropagation(DecompileContext context, Dictionary<uint, Block> blocks)
Expand Down
4 changes: 2 additions & 2 deletions UndertaleModTool/Editors/UndertaleCodeEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ private async Task DecompileCode(UndertaleCode code, bool first, LoaderDialog ex
if (existingDialog != null)
{
dialog = existingDialog;
dialog.Message = "Decompiling, please wait...";
dialog.Message = "Decompiling, please wait... This can take a while on complex scripts.";
}
else
{
Expand Down Expand Up @@ -638,7 +638,7 @@ private async Task DecompileCode(UndertaleCode code, bool first, LoaderDialog ex
string path = Path.Combine(TempPath, code.Name.Content + ".gml");
if (!SettingsWindow.ProfileModeEnabled || !File.Exists(path))
{
decompiled = Decompiler.Decompile(code, context);
decompiled = Decompiler.Decompile(code, context, (msg) => { dialog.Message = msg; });
}
else
decompiled = File.ReadAllText(path);
Expand Down
5 changes: 4 additions & 1 deletion UndertaleModTool/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,10 @@ public async Task<bool> GenerateGMLCache(ThreadLocal<GlobalDecompileContext> dec
decompileContext = new(() => new GlobalDecompileContext(Data, false));

if (Data.KnownSubFunctions is null) //if we run script before opening any code
Decompiler.BuildSubFunctionCache(Data);
{
SetProgressBar(null, "Building the cache of all sub-functions...", 0, 0);
await Task.Run(() => Decompiler.BuildSubFunctionCache(Data));
}

if (Data.GMLCache.IsEmpty)
{
Expand Down
6 changes: 5 additions & 1 deletion UndertaleModTool/Scripts/Builtin Scripts/Search.csx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ async Task DumpCode()
else
{
if (Data.KnownSubFunctions is null) //if we run script before opening any code
Decompiler.BuildSubFunctionCache(Data);
{
SetProgressBar(null, "Building the cache of all sub-functions...", 0, 0);
await Task.Run(() => Decompiler.BuildSubFunctionCache(Data));
SetProgressBar(null, "Code Entries", 0, Data.Code.Count);
}

await Task.Run(() => Parallel.ForEach(Data.Code, DumpCode));
}
Expand Down
12 changes: 10 additions & 2 deletions UndertaleModTool/Scripts/Resource Unpackers/ExportAllCode.csx
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,23 @@ async Task DumpCode()
if (Data.GMLCacheFailed.Count > 0)
{
if (Data.KnownSubFunctions is null) //if we run script before opening any code
Decompiler.BuildSubFunctionCache(Data);
{
SetProgressBar(null, "Building the cache of all sub-functions...", 0, 0);
await Task.Run(() => Decompiler.BuildSubFunctionCache(Data));
SetProgressBar(null, "Code Entries", 0, Data.GMLCache.Count + Data.GMLCacheFailed.Count);
}

await Task.Run(() => Parallel.ForEach(Data.GMLCacheFailed, (codeName) => DumpCode(Data.Code.ByName(codeName))));
}
}
else
{
if (Data.KnownSubFunctions is null) //if we run script before opening any code
Decompiler.BuildSubFunctionCache(Data);
{
SetProgressBar(null, "Building the cache of all sub-functions...", 0, 0);
await Task.Run(() => Decompiler.BuildSubFunctionCache(Data));
SetProgressBar(null, "Code Entries", 0, toDump.Count);
}

await Task.Run(() => Parallel.ForEach(toDump, DumpCode));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ void DumpCode()
File.WriteAllText(indexPath, indexText.ToString());

if (Data.KnownSubFunctions is null) // if we run script before opening any code
Decompiler.BuildSubFunctionCache(Data);
{
SetProgressBar(null, "Building the cache of all sub-functions...", 0, 0);
await Task.Run(() => Decompiler.BuildSubFunctionCache(Data));
SetProgressBar(null, "Code Entries", 0, toDump.Count);
}

Parallel.For(0, toDump.Count - 1, (i, _) =>
{
Expand Down

0 comments on commit b0000b4

Please sign in to comment.