diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 00000000..9c2a57e1
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,33 @@
+name: Benchmark
+on:
+ push:
+ branches:
+ - master
+
+permissions:
+ contents: write
+ deployments: write
+
+jobs:
+ benchmark:
+ name: Run Benchmark
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.0.x
+
+ - name: Run benchmark
+ run: cd Benchmark && dotnet run -c Release --exporters json --filter '*'
+
+ - name: Store benchmark results
+ uses: rhysd/github-action-benchmark@v1
+ with:
+ name: Benchmark.Net Benchmark
+ tool: 'benchmarkdotnet'
+ output-file-path: Benchmark/BenchmarkDotNet.Artifacts/results/Eliot.UELib.Benchmark.Benchmarks-report-full-compressed.json
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ auto-push: true
+ alert-threshold: '200%'
+ comment-on-alert: true
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ed8d415b..e8ea20c2 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -1,4 +1,4 @@
-name: Build .NET Framework and Publish to NuGet
+name: Build .NET and Publish
on:
workflow_dispatch:
@@ -8,25 +8,39 @@ on:
jobs:
build:
- runs-on: windows-2019
+ runs-on: windows-2022
+ strategy:
+ matrix:
+ dotnet: [ '8.0.x' ]
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
-
- - name: Setup MSBuild
- uses: microsoft/setup-msbuild@v2
-
- - name: Setup NuGet
- uses: NuGet/setup-nuget@v2
-
- - name: Restore dependencies
- run: nuget restore src/Eliot.UELib.csproj
-
+
+ - name: Setup
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: ${{ matrix.dotnet }}
+
+ - name: Install
+ run: dotnet restore src/Eliot.UELib.csproj
+
- name: Build
- run: msbuild src/Eliot.UELib.csproj -t:rebuild -property:Configuration=Release
-
+ run: dotnet build src/Eliot.UELib.csproj
+
+ - name: Restore
+ run: dotnet restore src/Eliot.UELib.csproj
+
+ #- name: Test
+ # run: dotnet test Test/Eliot.UELib.Test.csproj
+
+ - name: Pack
+ run: dotnet pack --configuration Release src/Eliot.UELib.csproj
+
+ #- name: Push
+ # run: dotnet nuget push src/Eliot.UELib.csproj -k ${{ secrets.NUGET_API_KEY }}} -s https://api.nuget.org/v3/index.json
+
- name: Publish Eliot.UELib to NuGet
id: nuget
uses: alirezanet/publish-nuget@v3.1.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e9d6259..148240e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
#
+## [1.8.0](https://github.com/EliotVU/Unreal-Library/releases/tag/1.8.0)
+
+* 89401543 Support for Stranglehold
+* 6c89b66a Improved support for Batman series
+* 4222595f Improved support for Borderlands: GOTYE
+* 6c89b66a Fixed bad decompilation of intrinsic array function calls under certain circumstances.
+* 8ba153b3 Fixed missing ')' in a replication statement
+* 3eab0cba #92 Fixed issue with overriding a class type when initializing a package.
+
## [1.7.0](https://github.com/EliotVU/Unreal-Library/releases/tag/1.7.0)
* 0ff0ed96 Added .NET Standard 2.1 and .NET 8.0 as framework targets.
diff --git a/CLI/Eliot.UELib.CLI.csproj b/CLI/Eliot.UELib.CLI.csproj
index 1ef437bb..d4c86efc 100644
--- a/CLI/Eliot.UELib.CLI.csproj
+++ b/CLI/Eliot.UELib.CLI.csproj
@@ -1,6 +1,7 @@
net8.0
+ 12.0
Exe
UELib.CLI
publish\
@@ -23,17 +24,4 @@
-
-
- False
- Microsoft .NET Framework 4.8 %28x86 and x64%29
- true
-
-
- False
- .NET Framework 3.5 SP1
- false
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index eebce869..f35357a5 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,7 @@ This is a table of games that are confirmed to be compatible with the current st
| | | | |
| Tom Clancy's EndWar | Unknown | 329/000 | |
| Roboblitz | 2306 | 369/006 | |
+| Stranglehold | 2605 | 375/025 | |
| Mass Effect (Xbox 360) | 2674 | 391/092 | Xenon |
| Medal of Honor: Airborne | 2859 | 421/011 | |
| Frontlines: Fuel of War | 2917 | 433/052 | |
@@ -221,10 +222,10 @@ This is a table of games that are confirmed to be compatible with the current st
| Gal\*Gun: Double Peace | 10897 | 871/000 | |
| Battleborn | 8623/1055 | 874/078 | |
| A Hat in Time | 12097 | 877-893/005 | |
+| Blue Estate The Game | 10246 | 893/000 | |
| Shadow Complex Remastered | 10897 | 893/001 | |
| Soldier Front 2 | 6712 | 904/009 | |
| Rise of the Triad | 10508 | Unknown | |
-| Outlast | 12046 | Unknown | |
| Sherlock Holmes: Crimes and Punishments | 10897 | Unknown | |
| Alien Rage | 7255 | Unknown | |
diff --git a/Test/Eliot.UELib.Test.csproj b/Test/Eliot.UELib.Test.csproj
index b2da294f..54952c49 100644
--- a/Test/Eliot.UELib.Test.csproj
+++ b/Test/Eliot.UELib.Test.csproj
@@ -1,6 +1,7 @@
net8.0
+ 12.0
false
Debug;Release
diff --git a/Test/UnrealPackageTests.cs b/Test/UnrealPackageTests.cs
index e21d0cef..8abfdc5c 100644
--- a/Test/UnrealPackageTests.cs
+++ b/Test/UnrealPackageTests.cs
@@ -10,6 +10,31 @@ namespace Eliot.UELib.Test
[TestClass]
public class UnrealPackageTests
{
+ public class MyUModel : UModel;
+
+ [TestMethod]
+ public void TestClassTypeOverride()
+ {
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
+ using var linker = new UnrealPackage(stream);
+
+ Assert.IsTrue(linker.GetClassType("Model") == typeof(UnknownObject));
+ linker.AddClassType("Model", typeof(MyUModel));
+ Assert.IsTrue(linker.GetClassType("Model") == typeof(MyUModel));
+ linker.InitializePackage(UnrealPackage.InitFlags.RegisterClasses);
+ Assert.IsTrue(linker.GetClassType("Model") == typeof(UModel));
+
+ using var stream2 = UnrealPackageUtilities.CreateTempPackageStream();
+ using var linker2 = new UnrealPackage(stream2);
+
+ // Swapped order...
+ Assert.IsTrue(linker2.GetClassType("Model") == typeof(UnknownObject));
+ linker2.InitializePackage(UnrealPackage.InitFlags.RegisterClasses);
+ Assert.IsTrue(linker2.GetClassType("Model") == typeof(UModel));
+ linker2.AddClassType("Model", typeof(MyUModel));
+ Assert.IsTrue(linker2.GetClassType("Model") == typeof(MyUModel));
+ }
+
internal static void AssertTestClass(UnrealPackage linker)
{
var testClass = linker.FindObject("Test");
diff --git a/Test/UnrealPackageUtilities.cs b/Test/UnrealPackageUtilities.cs
new file mode 100644
index 00000000..fa9dac5b
--- /dev/null
+++ b/Test/UnrealPackageUtilities.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+using UELib;
+
+namespace Eliot.UELib.Test
+{
+ internal static class UnrealPackageUtilities
+ {
+ // HACK: Ugly workaround the issues with UPackageStream
+ public static UPackageStream CreateTempPackageStream()
+ {
+ string tempFilePath = Path.Join(Path.GetTempFileName());
+ File.WriteAllBytes(tempFilePath, BitConverter.GetBytes(UnrealPackage.Signature));
+
+ var stream = new UPackageStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite);
+ return stream;
+ }
+ }
+}
diff --git a/Test/UnrealStreamTests.cs b/Test/UnrealStreamTests.cs
index 6082e92f..60e0e61f 100644
--- a/Test/UnrealStreamTests.cs
+++ b/Test/UnrealStreamTests.cs
@@ -12,16 +12,6 @@ namespace Eliot.UELib.Test
[TestClass]
public class UnrealStreamTests
{
- // HACK: Ugly workaround the issues with UPackageStream
- private static UPackageStream CreateTempStream()
- {
- string tempFilePath = Path.Join(Path.GetTempFileName());
- File.WriteAllBytes(tempFilePath, BitConverter.GetBytes(UnrealPackage.Signature));
-
- var stream = new UPackageStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite);
- return stream;
- }
-
[DataTestMethod]
[DataRow(PackageObjectLegacyVersion.Undefined, 1, +0b0000000000000000000000100001)]
[DataRow(PackageObjectLegacyVersion.Undefined, 1, -0b0000000000000000000000100001)]
@@ -32,7 +22,7 @@ private static UPackageStream CreateTempStream()
[DataRow(PackageObjectLegacyVersion.CompactIndexDeprecated, 4, int.MaxValue)]
public void SerializeCompactIndex(PackageObjectLegacyVersion version, int count, int compactIndex)
{
- using var stream = CreateTempStream();
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
using var linker = new UnrealPackage(stream);
linker.Build = new UnrealPackage.GameBuild(linker);
linker.Summary = new UnrealPackage.PackageFileSummary
@@ -65,7 +55,7 @@ public void SerializeCompactIndex(PackageObjectLegacyVersion version, int count,
[DataRow(PackageObjectLegacyVersion.Undefined, "语言处理")]
public void SerializeString(PackageObjectLegacyVersion version, string text)
{
- using var stream = CreateTempStream();
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
using var linker = new UnrealPackage(stream);
linker.Build = new UnrealPackage.GameBuild(linker);
linker.Summary = new UnrealPackage.PackageFileSummary
@@ -86,7 +76,7 @@ public void SerializeString(PackageObjectLegacyVersion version, string text)
[TestMethod]
public void SerializeStruct()
{
- using var stream = CreateTempStream();
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
using var linker = new UnrealPackage(stream);
linker.Build = new UnrealPackage.GameBuild(linker);
linker.Summary = new UnrealPackage.PackageFileSummary();
@@ -113,7 +103,7 @@ public void SerializeStruct()
[TestMethod]
public void SerializeStructMarshal()
{
- using var stream = CreateTempStream();
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
using var linker = new UnrealPackage(stream);
linker.Build = new UnrealPackage.GameBuild(linker);
linker.Summary = new UnrealPackage.PackageFileSummary();
@@ -156,7 +146,7 @@ public void SerializeStructMarshal()
[DataRow(PackageObjectLegacyVersion.LazyArrayReplacedWithBulkData)]
public void SerializeBulkData(PackageObjectLegacyVersion version)
{
- using var stream = CreateTempStream();
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
using var linker = new UnrealPackage(stream);
linker.Build = new UnrealPackage.GameBuild(linker);
linker.Summary = new UnrealPackage.PackageFileSummary
@@ -192,7 +182,7 @@ public void SerializeBulkData(PackageObjectLegacyVersion version)
[DataRow(PackageObjectLegacyVersion.VerticalOffsetAddedToUFont)]
public void SerializeDataTypes(PackageObjectLegacyVersion version)
{
- using var stream = CreateTempStream();
+ using var stream = UnrealPackageUtilities.CreateTempPackageStream();
using var linker = new UnrealPackage(stream);
linker.Build = new UnrealPackage.GameBuild(linker);
linker.Summary = new UnrealPackage.PackageFileSummary
diff --git a/src/Branch/DefaultEngineBranch.cs b/src/Branch/DefaultEngineBranch.cs
index fe74cc6f..3ff54b57 100644
--- a/src/Branch/DefaultEngineBranch.cs
+++ b/src/Branch/DefaultEngineBranch.cs
@@ -378,6 +378,10 @@ protected override TokenMap BuildTokenMap(UnrealPackage linker)
tokenMap[0x5B] = typeof(ByteConstToken);
break;
+
+ case UnrealPackage.GameBuild.BuildName.Borderlands_GOTYE:
+ tokenMap[0x5B] = typeof(BLVariableToken);
+ break;
#endif
#if BIOSHOCK
case UnrealPackage.GameBuild.BuildName.BioShock:
diff --git a/src/Branch/PackageObjectLegacyVersion.cs b/src/Branch/PackageObjectLegacyVersion.cs
index 5d13e128..064f7107 100644
--- a/src/Branch/PackageObjectLegacyVersion.cs
+++ b/src/Branch/PackageObjectLegacyVersion.cs
@@ -162,23 +162,27 @@ public enum PackageObjectLegacyVersion
CompressionAdded = 334,
NumberAddedToName = 343,
+
+ [Discardable] GameGOW = 374, // Engine Version: 2451
+ [Discardable] GameStranglehold = 375, // Engine Version: 2605
- // FIXME: Version 374-491; Delegate source type changed from Name to Object
- ChangedDelegateSourceFromNameToObject = 376,
-
- [Discardable] GameGOW = 374,
+ ///
+ /// Possibly attested first with Stranglehold (v375)
+ /// FIXME: Version 375-491; Delegate source type changed from Name to Object
+ ///
+ ChangedDelegateSourceFromNameToObject = GameStranglehold,
///
- /// Not attested in (GoW v374, oldest attest (v421)
+ /// Not attested in GoW v374), oldest attests (v375,v421)
/// FIXME: Version
///
SkipSizeAddedToArrayFindTokenIntrinsics = GameGOW + 1,
///
- /// Not attested in GoW (v374), oldest attest (v421)
+ /// Not attested in GoW (v374), oldest attests (v375,v421)
/// FIXME: Unknown version
///
- StructReferenceAddedToStructMember = GameGOW + 1,
+ StructReferenceAddedToStructMember = GameStranglehold,
// 417 according to the GoW client
LightingChannelsAddedToPoly = 417,
@@ -189,9 +193,9 @@ public enum PackageObjectLegacyVersion
/// Oldest attest MOHA (v421), but not MKKE (v472, non standard)
/// FIXME: Unknown version
///
- IsCopyAddedToStructMember = GameGOW + 1,
+ IsCopyAddedToStructMember = GameStranglehold + 1,
- [Discardable] GameFFOW = 433,
+ [Discardable] GameFFOW = 433, // Engine Version: 2917
///
/// Oldest attest FFOW (v433), but not MKKE (v472, non standard)
diff --git a/src/Branch/UE3/BL2/Tokens/BLVariableToken.cs b/src/Branch/UE3/BL2/Tokens/BLVariableToken.cs
new file mode 100644
index 00000000..e07e2c1a
--- /dev/null
+++ b/src/Branch/UE3/BL2/Tokens/BLVariableToken.cs
@@ -0,0 +1,12 @@
+using UELib.Core;
+using UELib.ObjectModel.Annotations;
+using UELib.Tokens;
+
+namespace UELib.Branch.UE3.BL2.Tokens
+{
+ [ExprToken(ExprToken.InstanceVariable)]
+ public class BLVariableToken : UStruct.UByteCodeDecompiler.FieldToken
+ {
+
+ }
+}
diff --git a/src/Branch/UE3/RSS/EngineBranch.RSS.cs b/src/Branch/UE3/RSS/EngineBranch.RSS.cs
index 0d6fb7e9..6e5bfc31 100644
--- a/src/Branch/UE3/RSS/EngineBranch.RSS.cs
+++ b/src/Branch/UE3/RSS/EngineBranch.RSS.cs
@@ -12,11 +12,17 @@ public EngineBranchRSS(BuildGeneration generation) : base(BuildGeneration.RSS)
protected override TokenMap BuildTokenMap(UnrealPackage linker)
{
var tokenMap = base.BuildTokenMap(linker);
+
+ // Identical to ContextToken and ClassContextToken. Spotted in BM 1, 2, and 4
+ tokenMap[0x50] = typeof(RSSContextToken);
+
if (linker.Build == UnrealPackage.GameBuild.BuildName.Batman4)
{
- tokenMap[0x2B] = typeof(NameConstNoNumberToken); // FIXME: NameConst but without the Int32 number at the end
+ // FIXME: NameConst but without the Int32 number at the end
+ tokenMap[0x2B] = typeof(NameConstNoNumberToken);
}
+
return tokenMap;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Branch/UE3/RSS/Tokens/Bm4ContextToken.cs b/src/Branch/UE3/RSS/Tokens/Bm4ContextToken.cs
deleted file mode 100644
index 86d19950..00000000
--- a/src/Branch/UE3/RSS/Tokens/Bm4ContextToken.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using UELib.Core;
-
-namespace UELib.Branch.UE3.RSS.Tokens
-{
- public class Bm4ContextToken : UStruct.UByteCodeDecompiler.ContextToken
- {
-
- }
-}
\ No newline at end of file
diff --git a/src/Branch/UE3/RSS/Tokens/RSSContextToken.cs b/src/Branch/UE3/RSS/Tokens/RSSContextToken.cs
new file mode 100644
index 00000000..bfee8bfd
--- /dev/null
+++ b/src/Branch/UE3/RSS/Tokens/RSSContextToken.cs
@@ -0,0 +1,12 @@
+using UELib.Core;
+using UELib.ObjectModel.Annotations;
+using UELib.Tokens;
+
+namespace UELib.Branch.UE3.RSS.Tokens
+{
+ [ExprToken(ExprToken.Context)]
+ public class RSSContextToken : UStruct.UByteCodeDecompiler.ContextToken
+ {
+
+ }
+}
diff --git a/src/Core/Classes/Props/UProperty.cs b/src/Core/Classes/Props/UProperty.cs
index f3bda59f..095015a2 100644
--- a/src/Core/Classes/Props/UProperty.cs
+++ b/src/Core/Classes/Props/UProperty.cs
@@ -170,7 +170,11 @@ protected override void Deserialize()
Record(nameof(CategoryName), CategoryName);
}
- if (_Buffer.Version >= (uint)PackageObjectLegacyVersion.AddedArrayEnumToUProperty)
+ if (_Buffer.Version >= (uint)PackageObjectLegacyVersion.AddedArrayEnumToUProperty
+#if MIDWAY
+ || Package.Build == UnrealPackage.GameBuild.BuildName.Stranglehold
+#endif
+ )
{
ArrayEnum = _Buffer.ReadObject();
Record(nameof(ArrayEnum), ArrayEnum);
diff --git a/src/Core/Classes/UClassDecompiler.cs b/src/Core/Classes/UClassDecompiler.cs
index dba2021e..35b26ea6 100644
--- a/src/Core/Classes/UClassDecompiler.cs
+++ b/src/Core/Classes/UClassDecompiler.cs
@@ -544,7 +544,7 @@ public string FormatReplication()
: string.Empty;
string statementFormat = statementType != string.Empty
? $"{statementType} if({statementCode})"
- : $"if({statementCode}";
+ : $"if({statementCode})";
output.Append(statementFormat);
UDecompilingState.AddTab();
diff --git a/src/Core/Classes/UComponent.cs b/src/Core/Classes/UComponent.cs
index ad03d8f5..35c6373f 100644
--- a/src/Core/Classes/UComponent.cs
+++ b/src/Core/Classes/UComponent.cs
@@ -1,4 +1,5 @@
-using UELib.Branch;
+using UELib.Annotations;
+using UELib.Branch;
namespace UELib.Core
{
@@ -6,7 +7,7 @@ namespace UELib.Core
[BuildGeneration(BuildGeneration.UE3)]
public class UComponent : UObject
{
- public UClass TemplateOwnerClass;
+ [CanBeNull] public UClass TemplateOwnerClass;
public UName TemplateName;
public UComponent()
@@ -14,4 +15,4 @@ public UComponent()
ShouldDeserializeOnDemand = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/Classes/UDefaultProperty.cs b/src/Core/Classes/UDefaultProperty.cs
index 777e0c6c..fef49176 100644
--- a/src/Core/Classes/UDefaultProperty.cs
+++ b/src/Core/Classes/UDefaultProperty.cs
@@ -499,7 +499,11 @@ private void DeserializeTypeDataUE3()
break;
case PropertyType.ByteProperty:
- if (_Buffer.Version >= (uint)PackageObjectLegacyVersion.EnumNameAddedToBytePropertyTag)
+ if (_Buffer.Version >= (uint)PackageObjectLegacyVersion.EnumNameAddedToBytePropertyTag
+#if BATMAN
+ && _Buffer.Package.Build.Generation != BuildGeneration.RSS
+#endif
+ )
{
_Buffer.Read(out _TypeData.EnumName);
Record(nameof(_TypeData.EnumName), _TypeData.EnumName);
@@ -659,7 +663,7 @@ private string DeserializeDefaultPropertyValue(PropertyType type, ref Deserializ
propertyValue = PropertyDisplay.FormatLiteral(index);
break;
}
-
+
case PropertyType.BioMask4Property:
{
_Buffer.Read(out byte value);
@@ -1069,7 +1073,8 @@ private string DeserializeDefaultPropertyValue(PropertyType type, ref Deserializ
string tagExpr = tag.Name;
if (tag.ArrayIndex > 0)
{
- tagExpr += PropertyDisplay.FormatT3DElementAccess(tag.ArrayIndex.ToString(), _Buffer.Version);
+ tagExpr += PropertyDisplay.FormatT3DElementAccess(tag.ArrayIndex.ToString(),
+ _Buffer.Version);
}
propertyValue += $"{tagExpr}={tag.Value}";
@@ -1154,7 +1159,7 @@ private string DeserializeDefaultPropertyValue(PropertyType type, ref Deserializ
for (var i = 0; i < arraySize; ++i)
{
string elementAccessText =
- PropertyDisplay.FormatT3DElementAccess(i.ToString(), _Buffer.Version);
+ PropertyDisplay.FormatT3DElementAccess(i.ToString(), _Buffer.Version);
string elementValue = DeserializeDefaultPropertyValue(arrayType, ref deserializeFlags);
if ((_TempFlags & ReplaceNameMarker) != 0)
{
diff --git a/src/Core/Classes/UObject.cs b/src/Core/Classes/UObject.cs
index 585fbd6a..eff6944b 100644
--- a/src/Core/Classes/UObject.cs
+++ b/src/Core/Classes/UObject.cs
@@ -176,7 +176,7 @@ public void BeginDeserializing()
DeserializationState |= ObjectState.Errorlized;
Console.Error.WriteLine($"\r\n> Object deserialization error for {GetReferencePath()} as {GetType()}" +
- $"\r\n> Exception: {ThrownException}");
+ $"\r\n> Exception: {ThrownException}");
}
finally
{
@@ -195,7 +195,8 @@ private void InitBuffer()
// Bypass the terrible and slow endian reverse call
int read = Package.Stream.EndianAgnosticRead(buffer, 0, ExportTable.SerialSize);
- Contract.Assert(ExportTable.SerialOffset + ExportTable.SerialSize <= Package.Stream.Length, "Exceeded file's length");
+ Contract.Assert(ExportTable.SerialOffset + ExportTable.SerialSize <= Package.Stream.Length,
+ "Exceeded file's length");
//Debug.Assert(read == ExportTable.SerialSize, $"Incomplete read; expected a total bytes of {ExportTable.SerialSize} but got {read}");
}
@@ -311,6 +312,7 @@ protected virtual void Deserialize()
if (header.Item2 == 2)
{
_Buffer.ReadInt32();
+ _Buffer.ConformRecordPosition();
}
}
}
@@ -319,6 +321,7 @@ protected virtual void Deserialize()
if (HasObjectFlag(ObjectFlagsLO.HasStack))
{
_Buffer.ReadClass(out StateFrame);
+ Record(nameof(StateFrame), StateFrame);
}
#if MKKE || BATMAN
if (Package.Build == UnrealPackage.GameBuild.BuildName.MKKE ||
@@ -338,15 +341,32 @@ protected virtual void Deserialize()
// HACK: Ugly work around for unregistered component classes...
// Simply for checking for the parent's class is not reliable without importing objects.
- case UnknownObject _ when _Buffer.Length >= 12 && IsTemplate():
+ case UnknownObject _ when _Buffer.Length >= 12 && Archetype != null && IsTemplate():
{
+ long backupPosition = _Buffer.Position;
+
var fakeComponent = new UComponent();
- DeserializeTemplate(fakeComponent);
+ try
+ {
+ DeserializeTemplate(fakeComponent);
+ }
+ catch (InvalidCastException exception)
+ {
+ Console.Error.WriteLine("Failed attempt to interpret object as a template {0}",
+ exception);
+
+ _Buffer.Position = backupPosition;
+ _Buffer.ConformRecordPosition();
+
+ // ISSUE: If the above recorded any data, the data will not be undone.
+ }
+
break;
}
}
}
- skipNetIndex:
+
+ skipNetIndex:
DeserializeNetIndex();
#if THIEF_DS || DEUSEX_IW
diff --git a/src/Core/Classes/UObjectDecompiler.cs b/src/Core/Classes/UObjectDecompiler.cs
index de848d06..edeef035 100644
--- a/src/Core/Classes/UObjectDecompiler.cs
+++ b/src/Core/Classes/UObjectDecompiler.cs
@@ -24,19 +24,26 @@ public virtual string Decompile()
return output + $"\r\n{UDecompilingState.Tabs}// Cannot decompile an imported object";
}
+ // FIXME: Won't be detected, an UnknownObject might be a UComponent.
+ if (this is UComponent uComponent)
+ {
+ output += $"{UDecompilingState.Tabs}// TemplateOwnerClass: {PropertyDisplay.FormatLiteral(uComponent.TemplateOwnerClass)}\r\n";
+ output += $"{UDecompilingState.Tabs}// TemplateOwnerName: {PropertyDisplay.FormatLiteral(uComponent.TemplateName)}\r\n";
+ }
+
+ if (Archetype != null)
+ {
+ output += $"{UDecompilingState.Tabs}// Archetype: {PropertyDisplay.FormatLiteral(Archetype)}\r\n";
+ }
+
output += UDecompilingState.Tabs;
- output += $"begin object name={Name}";
+ output += $"begin object name=\"{Name}\"";
// If null then we have a new sub-object (not an override)
if (Archetype == null)
{
Debug.Assert(Class != null);
output += $" class={Class.GetReferencePath()}";
}
- else
- {
- // Commented out, too noisy but useful.
- //output += $" /*archetype={Archetype.GetReferencePath()}*/";
- }
output += "\r\n";
UDecompilingState.AddTabs(1);
diff --git a/src/Core/Tokens/ArrayTokens.cs b/src/Core/Tokens/ArrayTokens.cs
index 3b1d0303..a51c684e 100644
--- a/src/Core/Tokens/ArrayTokens.cs
+++ b/src/Core/Tokens/ArrayTokens.cs
@@ -1,4 +1,5 @@
-using UELib.Branch;
+using System.IO;
+using UELib.Branch;
using UELib.ObjectModel.Annotations;
using UELib.Tokens;
@@ -145,6 +146,13 @@ protected string DecompileOneParamMethod(string functionName)
Decompiler._CanAddSemicolon = true;
string context = DecompileNext();
string param1 = DecompileNext();
+
+ if (Package.Version >= (uint)PackageObjectLegacyVersion.EndTokenAppendedToArrayTokenIntrinsics)
+ {
+ // EndParms
+ AssertSkipCurrentToken();
+ }
+
return $"{context}.{functionName}({param1})";
}
@@ -154,6 +162,13 @@ protected string DecompileTwoParamMethod(string functionName)
string context = DecompileNext();
string param1 = DecompileNext();
string param2 = DecompileNext();
+
+ if (Package.Version >= (uint)PackageObjectLegacyVersion.EndTokenAppendedToArrayTokenIntrinsics)
+ {
+ // EndParms
+ AssertSkipCurrentToken();
+ }
+
return $"{context}.{functionName}({param1}, {param2})";
}
}
@@ -203,6 +218,7 @@ public override string Decompile()
[ExprToken(ExprToken.DynArrayAdd)]
public class DynamicArrayAddToken : DynamicArrayMethodToken
{
+ // Ugly copy, but this is the only array token that always has EndParms regardless of engine version.
public override void Deserialize(IUnrealStream stream)
{
// Array
@@ -217,9 +233,16 @@ public override void Deserialize(IUnrealStream stream)
Decompiler.DeserializeDebugToken();
}
+ // Ugly copy, but this is the only array token that always has EndParms regardless of engine version.
public override string Decompile()
{
- return DecompileOneParamMethod("Add");
+ Decompiler._CanAddSemicolon = true;
+ string context = DecompileNext();
+ string param1 = DecompileNext();
+
+ AssertSkipCurrentToken();
+
+ return $"{context}.Add({param1})";
}
}
diff --git a/src/Eliot.UELib.csproj b/src/Eliot.UELib.csproj
index a42d1896..21c2b706 100644
--- a/src/Eliot.UELib.csproj
+++ b/src/Eliot.UELib.csproj
@@ -1,7 +1,8 @@
- DECOMPILE;BINARYMETADATA;UE1;UE2;UE3;UE4;VENGEANCE;SWAT4;UNREAL2;INFINITYBLADE;BORDERLANDS2;GOW2;APB;SPECIALFORCE2;XIII;SINGULARITY;THIEF_DS;DEUSEX_IW;BORDERLANDS;MIRRORSEDGE;BIOSHOCK;HAWKEN;UT;DISHONORED;REMEMBERME;ALPHAPROTOCOL;VANGUARD;TERA;MKKE;TRANSFORMERS;XCOM2;DD2;DCUO;AA2;SPELLBORN;BATMAN;MOH;ROCKETLEAGUE;DNF;LSGAME;UNDYING;HP;DEVASTATION;BATTLEBORN;SPLINTERCELL;AHIT;GIGANTIC;ENDWAR;SG1;MASS_EFFECT;MOV
+ DECOMPILE;BINARYMETADATA;UE1;UE2;UE3;UE4;VENGEANCE;SWAT4;UNREAL2;INFINITYBLADE;BORDERLANDS2;GOW2;APB;SPECIALFORCE2;XIII;SINGULARITY;THIEF_DS;DEUSEX_IW;BORDERLANDS;MIRRORSEDGE;BIOSHOCK;HAWKEN;UT;DISHONORED;REMEMBERME;ALPHAPROTOCOL;VANGUARD;TERA;MKKE;TRANSFORMERS;XCOM2;DD2;DCUO;AA2;SPELLBORN;BATMAN;MOH;ROCKETLEAGUE;DNF;LSGAME;UNDYING;HP;DEVASTATION;BATTLEBORN;SPLINTERCELL;AHIT;GIGANTIC;ENDWAR;SG1;MASS_EFFECT;MOV;MIDWAY
net48;netstandard2.0;netstandard2.1;net8.0
+ 12.0
Library
UELib
true
@@ -70,7 +71,7 @@
Eliot.UELib
$(AssemblyName)
- $(VersionPrefix)1.7.0
+ $(VersionPrefix)1.8.0
EliotVU
$(AssemblyName)
UnrealScript decompiler library for Unreal package files (.upk, .u, .uasset; etc), with support for Unreal Engine 1, 2, and 3.
diff --git a/src/UnrealBuild.cs b/src/UnrealBuild.cs
index 9b766045..2998d14e 100644
--- a/src/UnrealBuild.cs
+++ b/src/UnrealBuild.cs
@@ -100,10 +100,17 @@ public enum BuildGeneration
///
/// Gearbox Software
///
- /// Modified Unreal Engine 3 for Borderlands
+ /// Modified Unreal Engine 3 for Borderlands.
///
GB,
+ ///
+ /// Midway
+ ///
+ /// Modified Unreal Engine3 for Midway games.
+ ///
+ Midway3,
+
///
/// Unreal Engine 4
///
diff --git a/src/UnrealPackage.cs b/src/UnrealPackage.cs
index a55e5041..349f2705 100644
--- a/src/UnrealPackage.cs
+++ b/src/UnrealPackage.cs
@@ -26,6 +26,7 @@ namespace UELib
using Branch.UE3.RL;
using Branch.UE3.SFX;
using Branch.UE2.ShadowStrike;
+ using System.Text;
///
/// Represents the method that will handle the UELib.UnrealPackage.NotifyObjectAdded
@@ -378,6 +379,13 @@ public enum BuildName
///
[Build(369, 6)] RoboBlitz,
+ ///
+ /// Stranglehold
+ ///
+ /// 375/025
+ ///
+ [Build(375, 25, BuildGeneration.Midway3)] Stranglehold,
+
///
/// Medal of Honor: Airborne
///
@@ -456,10 +464,10 @@ public enum BuildName
///
/// Batman: Arkham Asylum
///
- /// 576/021
- /// No Special support, but there's no harm in recognizing this build.
+ /// 576/021 (Missing most changes guarded by )
///
- [Build(576, 21)] Batman1,
+ [Build(576, 21)] [BuildEngineBranch(typeof(EngineBranchRSS))]
+ Batman1,
///
/// 576/100
@@ -1142,6 +1150,8 @@ public void Deserialize(IUnrealStream stream)
Version &= 0xFFFFU;
Console.WriteLine("Package Version:" + Version + "/" + LicenseeVersion);
+ Contract.Assert(Version != 0, "Bad package version 0!");
+
SetupBuild(stream.Package);
Debug.Assert(stream.Package.Build != null);
Console.WriteLine("Build:" + stream.Package.Build);
@@ -1173,7 +1183,23 @@ public void Deserialize(IUnrealStream stream)
HeaderSize = stream.ReadInt32();
Console.WriteLine("Header Size: " + HeaderSize);
}
+#if MIDWAY
+ if (stream.Package.Build == BuildGeneration.Midway3 &&
+ stream.LicenseeVersion >= 2)
+ {
+ stream.Read(out int abbrev);
+
+ string codename = Encoding.UTF8.GetString(BitConverter.GetBytes(abbrev));
+ Console.WriteLine($"Midway game codename: {codename}");
+
+ stream.Read(out int customVersion);
+ if (customVersion >= 256)
+ {
+ stream.Read(out int _);
+ }
+ }
+#endif
if (stream.Version >= VFolderName)
{
FolderName = stream.ReadString();
@@ -1268,7 +1294,13 @@ public void Deserialize(IUnrealStream stream)
return;
}
-
+#if MIDWAY
+ if (stream.Package.Build == GameBuild.BuildName.Stranglehold &&
+ stream.Version >= 375)
+ {
+ stream.Read(out int _);
+ }
+#endif
if (stream.Version >= VDependsOffset)
{
DependsOffset = stream.ReadInt32();
@@ -2244,7 +2276,7 @@ public void RegisterClass(string className, Type classObject)
[PublicAPI]
public void AddClassType(string className, Type classObject)
{
- _ClassTypes.Add(className.ToLower(), classObject);
+ _ClassTypes[className.ToLower()] = classObject;
}
[PublicAPI]