Skip to content

Commit

Permalink
#37 Decompile optional parameter expressions.
Browse files Browse the repository at this point in the history
  • Loading branch information
EliotVU committed Jun 24, 2022
1 parent 0ca537f commit 6d889c8
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 75 deletions.
55 changes: 40 additions & 15 deletions src/ByteCodeDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Runtime.CompilerServices;
using UELib.Annotations;
using UELib.Flags;
using UELib.Tokens;

namespace UELib.Core
Expand Down Expand Up @@ -1208,17 +1209,6 @@ public void InitDecompile()

FieldToken.LastField = null;

// TODO: Corrigate detection and version.
DefaultParameterToken._NextParamIndex = 0;
if (Package.Version > 300)
{
var func = _Container as UFunction;
if (func?.Params != null)
DefaultParameterToken._NextParamIndex = func.Params.FindIndex(
p => p.HasPropertyFlag(Flags.PropertyFlagsLO.OptionalParm)
);
}

// Reset these, in case of a loop in the Decompile function that did not finish due exception errors!
_IsWithinClassContext = false;
_CanAddSemicolon = false;
Expand Down Expand Up @@ -1300,7 +1290,42 @@ public string Decompile()
var spewOutput = false;
var tokenEndIndex = 0;
Token lastStatementToken = null;
#if !DEBUG_HIDDENTOKENS
if (_Container is UFunction func
&& func.HasOptionalParamData()
&& DeserializedTokens.Count > 0)
{
CurrentTokenIndex = 0;
foreach (var parm in func.Params)
{
if (!parm.HasPropertyFlag(PropertyFlagsLO.OptionalParm))
continue;

switch (CurrentToken)
{
// Skip NothingToken and DefaultParameterToken (up to EndParmValueToken)
case NothingToken _:
++CurrentTokenIndex;
continue;

case DefaultParameterToken _:
{
Token token;
do
{
token = NextToken;
} while (!(token is EndParmValueToken));

break;
}

default:
Debug.Fail($"Unexpected token for optional parameter {parm.GetOuterGroup()}");
break;
}
}
}
#endif
while (CurrentTokenIndex + 1 < DeserializedTokens.Count)
{
//Decompile chain==========
Expand Down Expand Up @@ -1680,19 +1705,19 @@ private string DecompileNests(bool outputAllRemainingNests = false)
return output;
}

#endregion
#endregion

#region Disassemble
#region Disassemble

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
public string Disassemble()
{
return string.Empty;
}

#endregion
#endregion

#endif
}
}
}
}
8 changes: 8 additions & 0 deletions src/Core/Classes/UFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ public bool IsPre()
return IsOperator() && HasFunctionFlag(Flags.FunctionFlags.PreOperator);
}

public bool HasOptionalParamData()
{
// FIXME: Deprecate version check, and re-map the function flags using the EngineBranch class approach.
return Package.Version > 300
&& ByteCodeManager != null
&& HasFunctionFlag(Flags.FunctionFlags.OptionalParameters);
}

#endregion
}
}
70 changes: 45 additions & 25 deletions src/Core/Classes/UFunctionDecompiler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if DECOMPILE
using System;
using System.Linq;
using UELib.Flags;

namespace UELib.Core
{
Expand All @@ -9,7 +10,7 @@ public partial class UFunction
/// <summary>
/// Decompiles this object into a text format of:
///
/// [FLAGS] function NAME([VARIABLES]) [const]
/// [FLAGS] function NAME([VARIABLES])[;] [const]
/// {
/// [LOCALS]
///
Expand All @@ -19,16 +20,12 @@ public partial class UFunction
/// <returns></returns>
public override string Decompile()
{
string code;
try
{
code = FormatCode();
}
catch (Exception e)
{
code = e.Message;
}
return FormatHeader() + (string.IsNullOrEmpty(code) ? DecompileMeta() + ";" : code + DecompileMeta());
string code = FormatCode();
var body = $"{FormatHeader()}{code}{DecompileMeta()}";
// Write a declaration only if code is empty.
return string.IsNullOrEmpty(code)
? $"{body};"
: body;
}

private string FormatFlags()
Expand Down Expand Up @@ -197,17 +194,13 @@ protected override string FormatHeader()
output = $"// Export U{Outer.Name}::exec{Name}(FFrame&, void* const)\r\n{UDecompilingState.Tabs}";
}

var comment = FormatTooltipMetaData();
if(comment != string.Empty)
{
output = comment + output;
}
string comment = FormatTooltipMetaData();
string returnCode = ReturnProperty != null
? ReturnProperty.GetFriendlyType() + " "
: string.Empty;

output += FormatFlags()
+ (ReturnProperty != null
? ReturnProperty.GetFriendlyType() + " "
: string.Empty)
+ FriendlyName + FormatParms();
output += comment +
FormatFlags() + returnCode + FriendlyName + FormatParms();
if (HasFunctionFlag(Flags.FunctionFlags.Const))
{
output += " const";
Expand All @@ -218,14 +211,41 @@ protected override string FormatHeader()

private string FormatParms()
{
if (Params == null || !Params.Any())
return "()";

bool hasOptionalData = HasOptionalParamData();
if (hasOptionalData)
{
// Ensure a sound ByteCodeManager
ByteCodeManager.Deserialize();
ByteCodeManager.JumpTo(0);
ByteCodeManager.CurrentTokenIndex = -1;
}

var output = string.Empty;
if (Params != null && Params.Any())
var parameters = Params.Where(parm => parm != ReturnProperty).ToList();
foreach (var parm in parameters)
{
var parameters = Params.Where((p) => p != ReturnProperty);
foreach (var parm in parameters)
string parmCode = parm.Decompile();
if (hasOptionalData && parm.HasPropertyFlag(PropertyFlagsLO.OptionalParm))
{
output += parm.Decompile() + (parm != parameters.Last() ? ", " : string.Empty);
// Look for an assignment.
var defaultToken = ByteCodeManager.NextToken;
if (defaultToken is UByteCodeDecompiler.DefaultParameterToken)
{
string defaultExpr = defaultToken.Decompile();
parmCode += $" = {defaultExpr}";
}
}

if (parm != parameters.Last())
{
output += $"{parmCode}, ";
continue;
}

output += parmCode;
}

return $"({output})";
Expand Down
22 changes: 1 addition & 21 deletions src/Core/Tokens/FieldTokens.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,23 +113,6 @@ public override string Decompile()

public class DefaultParameterToken : Token
{
internal static int _NextParamIndex;

private UField _NextParam
{
get
{
try
{
return ((UFunction)Decompiler._Container).Params[_NextParamIndex++];
}
catch
{
return null;
}
}
}

public override void Deserialize(IUnrealStream stream)
{
stream.ReadUInt16(); // Size
Expand All @@ -148,10 +131,7 @@ public override string Decompile()
{
string expression = DecompileNext();
DecompileNext(); // EndParmValue
Decompiler._CanAddSemicolon = true;
var param = _NextParam;
string paramName = param != null ? param.Name : $"@UnknownOptionalParam_{_NextParamIndex - 1}";
return $"{paramName} = {expression}";
return expression;
}
}

Expand Down
7 changes: 0 additions & 7 deletions src/Core/Tokens/OtherTokens.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ public override void Deserialize(IUnrealStream stream)
if (stream.Package.Build == UnrealPackage.GameBuild.BuildName.MOHA)
Decompiler.AlignSize(sizeof(int));
}

public override string Decompile()
{
// Empty default option parameter!
++DefaultParameterToken._NextParamIndex;
return string.Empty;
}
}

public class NoDelegateToken : NoneToken
Expand Down
2 changes: 2 additions & 0 deletions src/Eliot.UELib.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=UELib_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Endian/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=parm/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Parms/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=transpiled/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
37 changes: 30 additions & 7 deletions src/UnrealFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ public enum ObjectFlagsHO : ulong
}

/// <summary>
/// Flags describing an function instance.
/// Flags describing a function instance.
/// </summary>
[Flags]
public enum FunctionFlags : ulong // actually uint but were using ulong for UE2 and UE3 Compatably
public enum FunctionFlags : ulong
{
Final = 0x00000001U,
Defined = 0x00000002U,
Expand All @@ -194,24 +194,47 @@ public enum FunctionFlags : ulong // actually uint but were using ulong for UE2
Event = 0x00000800U,
Operator = 0x00001000U,
Static = 0x00002000U,
NoExport = 0x00004000U, // Can also be an identifier for functions with Optional parameters.

/// <summary>
/// NoExport
/// UE3 (~V300): Indicates whether we have optional parameters, including optional expression data.
/// </summary>
OptionalParameters = 0x00004000U,
NoExport = 0x00004000U,

Const = 0x00008000U,
Invariant = 0x00010000U,

// UE2 additions
// =============

Public = 0x00020000U,
Private = 0x00040000U,
Protected = 0x00080000U,
Delegate = 0x00100000U,
NetServer = 0x00200000U,
#if VENGEANCE
// Generated/Constructor?
VG_Unk1 = 0x00200000U,
VG_Overloaded = 0x00800000U,
#endif
#endif
/// <summary>
/// UE2: Multicast (Replicated to all relevant clients)
/// UE3: Function is replicated to relevant client.
/// </summary>
NetServer = 0x00200000U,

Interface = 0x00400000U,
NetClient = 0x01000000U,
DLLImport = 0x02000000U, // Also available in UE2(unknown meaning there)

/// <summary>
/// UE2: Unknown
/// UE3 (V655)
/// </summary>
DLLImport = 0x02000000U,

// K2 Additions, late UDK, early implementation of Blueprints that were soon deprecated.
K2Call = 0x04000000U,
K2Override = 0x08000000U, // K2Call?
K2Override = 0x08000000U,
K2Pure = 0x10000000U,
}

Expand Down

0 comments on commit 6d889c8

Please sign in to comment.