Skip to content

Commit 8ddf115

Browse files
committed
Importing exports preservers latent action and netindex for same-game transfers. Change resynthesization experiment to reload the window as we cannot release a package easily.
1 parent 26d4b12 commit 8ddf115

4 files changed

Lines changed: 75 additions & 32 deletions

File tree

LegendaryExplorer/LegendaryExplorer/Packages/SharedPackageTools.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,8 @@ public static void CompareToPackageWrapper(WPFBase wpfBase, Action<EntryStringPa
5858
return $"{nameof(CompareToPackageWrapper)}() requires at least one parameter be set!";
5959
}
6060
}
61-
var diff = PackageDiff.Create(wpfBase.Pcc, package);
62-
var list = new List<EntryStringPair>();
63-
list.AddRange(diff.ChangedEntries.Select(ed => new EntryStringPair(ed.A, $"{(ed.A.UIndex > 0 ? "Export" : "Import")} Changed #{ed.A.UIndex} {ed.A.InstancedFullPath}")));
64-
list.AddRange(diff.AOnlyEntries.Select(entry => new EntryStringPair(entry, $"{(entry.UIndex > 0 ? "Export" : "Import")} only exists in this file #{entry.UIndex} {entry.InstancedFullPath}")));
65-
list.AddRange(diff.BOnlyEntries.Select(entry => new EntryStringPair(entry, $"{(entry.UIndex > 0 ? "Export" : "Import")} only exists in other file #{entry.UIndex} {entry.InstancedFullPath}")));
66-
list.AddRange(diff.AOnlyNames.Select(name => new EntryStringPair($"Name only exists in this file: {name}")));
67-
list.AddRange(diff.BOnlyNames.Select(name => new EntryStringPair($"Name only exists in other file: {name}")));
68-
return list;
61+
62+
return StructrualPackageCompare(wpfBase.Pcc, package);
6963
}
7064
if (package != null) return wpfBase.Pcc.CompareToPackage(package);
7165
if (diskPath != null) return wpfBase.Pcc.CompareToPackage(diskPath);
@@ -100,6 +94,28 @@ public static void CompareToPackageWrapper(WPFBase wpfBase, Action<EntryStringPa
10094
});
10195
}
10296

97+
/// <summary>
98+
/// Compares two ME packages and returns a list of differences between their entries and names.
99+
/// </summary>
100+
/// <remarks>The returned list provides a human-readable summary of differences, including whether
101+
/// an entry is an export or import, and whether it is unique to one package or changed between the two. This
102+
/// method does not modify the input packages.</remarks>
103+
/// <param name="package1">The first ME package to compare.</param>
104+
/// <param name="package2">The second ME package to compare.</param>
105+
/// <returns>A list of entry string pairs describing the differences between the two packages. The list includes changed
106+
/// entries, entries unique to each package, and names that exist only in one package.</returns>
107+
public static List<EntryStringPair> StructrualPackageCompare(IMEPackage package1, IMEPackage package2)
108+
{
109+
var diff = PackageDiff.Create(package1, package2);
110+
var list = new List<EntryStringPair>();
111+
list.AddRange(diff.ChangedEntries.Select(ed => new EntryStringPair(ed.A, $"{(ed.A.UIndex > 0 ? "Export" : "Import")} Changed #{ed.A.UIndex} {ed.A.InstancedFullPath}")));
112+
list.AddRange(diff.AOnlyEntries.Select(entry => new EntryStringPair(entry, $"{(entry.UIndex > 0 ? "Export" : "Import")} only exists in this file #{entry.UIndex} {entry.InstancedFullPath}")));
113+
list.AddRange(diff.BOnlyEntries.Select(entry => new EntryStringPair(entry, $"{(entry.UIndex > 0 ? "Export" : "Import")} only exists in other file #{entry.UIndex} {entry.InstancedFullPath}")));
114+
list.AddRange(diff.AOnlyNames.Select(name => new EntryStringPair($"Name only exists in this file: {name}")));
115+
list.AddRange(diff.BOnlyNames.Select(name => new EntryStringPair($"Name only exists in other file: {name}")));
116+
return list;
117+
}
118+
103119
/// <summary>
104120
/// Opens a compare to package
105121
/// </summary>

LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/Experiments/PackageEditorExperimentsM.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4139,13 +4139,20 @@ public static void CompilePackageUScriptFromFolder(PackageEditorWindow window)
41394139

41404140
public static void ResynthesizePackage(PackageEditorWindow peWindow)
41414141
{
4142-
var result = MessageBox.Show("WARNING: This will save the package to disk and may break it!! Continue?",
4142+
var result = MessageBox.Show("WARNING: This will save the package to disk and may break it!! It will also close this window and re-open it. Continue?",
41434143
"Destructive operation", MessageBoxButton.YesNo, MessageBoxImage.Warning);
41444144

41454145
if (result == MessageBoxResult.Yes)
41464146
{
41474147
var p = PackageResynthesizer.ResynthesizePackage(peWindow.Pcc, new PackageCache(), true);
41484148
p.Save();
4149+
peWindow.Close();
4150+
4151+
PackageEditorWindow peWpf = new PackageEditorWindow();
4152+
peWpf.LoadFile(p.FilePath); // Reload file from disk
4153+
peWpf.ShowActivated = true;
4154+
peWpf.Show();
4155+
MessageBox.Show("Package has been resynthesized. If the package was open in multiple windows, the new window may not reflect the changes.");
41494156
}
41504157
}
41514158

LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/EntryImporter.cs

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Diagnostics;
4-
using System.IO;
5-
using System.Linq;
6-
using System.Runtime.InteropServices;
7-
using LegendaryExplorerCore.GameFilesystem;
1+
using LegendaryExplorerCore.GameFilesystem;
82
using LegendaryExplorerCore.Gammtek.IO;
93
using LegendaryExplorerCore.Helpers;
104
using LegendaryExplorerCore.Memory;
@@ -14,6 +8,12 @@
148
using LegendaryExplorerCore.Unreal.BinaryConverters;
159
using LegendaryExplorerCore.Unreal.ObjectInfo;
1610
using LegendaryExplorerCore.UnrealScript;
11+
using System;
12+
using System.Collections.Generic;
13+
using System.Diagnostics;
14+
using System.IO;
15+
using System.Linq;
16+
using System.Runtime.InteropServices;
1717

1818
namespace LegendaryExplorerCore.Packages.CloningImportingAndRelinking
1919
{
@@ -203,7 +203,7 @@ public static List<EntryStringPair> ImportAndRelinkEntries(PortingOption porting
203203
}
204204

205205
if (!rop.ForceAllowMaterialPorting && !allowedToPortShaders)
206-
{
206+
{
207207
rop.ErrorOccurredCallback?.Invoke($"You cannot port Materials from {sourcePcc.Game} into {destPcc.Game}");
208208
}
209209

@@ -431,7 +431,7 @@ public static IEntry ImportExport(IMEPackage destPackage, ExportEntry sourceExpo
431431
}
432432

433433
// If IFP is the same but class changed the lookup earlier failed. We should retry here (used at least in Crossgen)
434-
if (destPackage.FindExport(seIFP, testExp.ClassName) is {} existingExport)
434+
if (destPackage.FindExport(seIFP, testExp.ClassName) is { } existingExport)
435435
{
436436
// Export already in destination package
437437
return existingExport;
@@ -546,10 +546,32 @@ public static IEntry ImportExport(IMEPackage destPackage, ExportEntry sourceExpo
546546
byte[] prePropBinary;
547547
if (sourceExport.HasStack)
548548
{
549-
var dummy = GetStackDummy(destPackage.Game);
550-
prePropBinary = new byte[8 + dummy.Length];
551-
sourceExport.DataReadOnly[..8].CopyTo(prePropBinary);
552-
dummy.CopyTo(prePropBinary.AsSpan(8));
549+
if (sourceExport.Game == destPackage.Game)
550+
{
551+
// Same game - preserve latent action and net index so
552+
// comparisons are better and latent action is preserved
553+
// (whatever that does...)
554+
StateFrame oStack = StateFrame.FromExport(sourceExport);
555+
var newStack = oStack.ToBytes(destPackage);
556+
prePropBinary = new byte[newStack.Length + 4];
557+
newStack.CopyTo(prePropBinary);
558+
559+
// Create a span representing the target slice of the byte array
560+
Span<byte> destination = prePropBinary.AsSpan().Slice(newStack.Length, 4);
561+
562+
// Write the integer to that span. This handles platform endianness automatically.
563+
MemoryMarshal.Write(destination, sourceExport.NetIndex);
564+
}
565+
else
566+
{
567+
// Use the old code when porting across games since latent
568+
// actinos may or may not be the same.
569+
var dummy = GetStackDummy(destPackage.Game);
570+
prePropBinary = new byte[8 + dummy.Length];
571+
sourceExport.DataReadOnly[..8].CopyTo(prePropBinary);
572+
dummy.CopyTo(prePropBinary.AsSpan(8));
573+
574+
}
553575
}
554576
else
555577
{
@@ -862,6 +884,8 @@ private static bool CanDonateObject(ExportEntry export)
862884
public static bool ReplaceExportDataWithAnother(ExportEntry incomingExport, ExportEntry targetExport, RelinkerOptionsPackage rop)
863885
{
864886
using var res = new EndianReader(MemoryManager.GetMemoryStream()) { Endian = targetExport.FileRef.Endian };
887+
888+
// Write Pre-Properties binary =======================================================
865889
if (incomingExport.HasStack)
866890
{
867891
// Seems like this doesn't work, maybe stack has already been written
@@ -886,10 +910,13 @@ public static bool ReplaceExportDataWithAnother(ExportEntry incomingExport, Expo
886910
List<string> names = [.. targetExport.FileRef.Names];
887911
try
888912
{
913+
// Write Properties ============================================================================
889914
PropertyCollection props = incomingExport.GetProperties();
890915
ApplyCrossGamePropertyFixes(incomingExport, targetExport.FileRef, props);
891916
ObjectBinary binary = ExportBinaryConverter.ConvertPostPropBinary(incomingExport, targetExport.Game, props);
892917
props.WriteTo(res.Writer, targetExport.FileRef);
918+
919+
// Write Binary ================================================================================
893920
// 11/12/2024 - Set file offset to start of binary data so it can accurately serialize offsets for class types that depend on it being proper (ShaderCache) - Mgamerz
894921
res.Writer.WriteFromBuffer(binary.ToBytes(targetExport.FileRef, fileOffset: targetExport.DataOffset + (int)res.Writer.BaseStream.Length));
895922
}
@@ -1699,7 +1726,7 @@ public static List<string> GetPossibleImportFiles(IMEPackage package, ImportEntr
16991726
// 01/14/2024 - Support looking in package by name if our source package doesn't indicate needing it already loaded
17001727
else if ((package.Flags & UnrealFlags.EPackageFlags.RequireImportsAlreadyLoaded) == 0)
17011728
{
1702-
filesToCheck.Add($"{entry.GetRootName()}{Path.GetExtension(package.FilePath)}" );
1729+
filesToCheck.Add($"{entry.GetRootName()}{Path.GetExtension(package.FilePath)}");
17031730
}
17041731

17051732
//add base definition files that are always loaded (Core, Engine, etc.)
@@ -2252,7 +2279,7 @@ public static ImportEntry ConvertExportToImport(ExportEntry export, string force
22522279

22532280
// Move any children to the new import
22542281
var children = export.FileRef.Exports.Where(x => x.idxLink == export.UIndex).OfType<IEntry>().Concat(export.FileRef.Imports.Where(x => x.idxLink == export.UIndex)).ToList();
2255-
foreach(var child in children)
2282+
foreach (var child in children)
22562283
{
22572284
child.idxLink = convertedItem.UIndex;
22582285
}

LegendaryExplorer/LegendaryExplorerCore/Packages/PackageResynthesizer.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public static IMEPackage ResynthesizePackage(IMEPackage package, PackageCache ca
121121
return newPackage;
122122
}
123123

124-
private static IMEPackage DumpUnreferencedObjects(IMEPackage package, PackageCache cache)
124+
public static IMEPackage DumpUnreferencedObjects(IMEPackage package, PackageCache cache)
125125
{
126126
// Package must be inventoried before dumping objects because otherwise we will try to resolve array types
127127
// when package may not yet be fully setup, which causes problems.
@@ -269,13 +269,6 @@ private static void PortOrdering(EntryOrdering ordering, IMEPackage newPackage,
269269
new RelinkerOptionsPackage() { RelinkAllowDifferingClassesInRelink = true, ImportExportDependencies = false }, out _);
270270

271271

272-
// Restore NetIndex and LatentAction
273-
//destExp.NetIndex = oExp.NetIndex;
274-
//if (destExp.HasStack)
275-
//{
276-
// StateFrame oStack = StateFrame.FromExport(oExp);
277-
// destExp.WritePrePropsAndProperties(oStack, destExp.GetProperties());
278-
//}
279272
}
280273
}
281274
}

0 commit comments

Comments
 (0)