diff --git a/.gitignore b/.gitignore index 8804cc5871..3d63e72564 100644 --- a/.gitignore +++ b/.gitignore @@ -226,3 +226,4 @@ Release_x64/ # Ignore Doxygen working folder and Doxygen output /Documentation/Output /Documentation/Doxygen +/LegendaryExplorer/TexConverter/TexConverter/WinRelease/TexConverter.vcxproj.FileListAbsolute.txt diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs index bd1cd6a0de..d3b3fa15a1 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PackageEditor/PackageEditorWindow.xaml.cs @@ -47,6 +47,7 @@ using LegendaryExplorerCore.Localization; using LegendaryExplorerCore.UnrealScript.Language.Tree; using GongSolutions.Wpf.DragDrop; +using LegendaryExplorerCore.Unreal.Collections; namespace LegendaryExplorer.Tools.PackageEditor { @@ -1635,9 +1636,13 @@ static IEntry GetExternallyReferencedEntry(List entriesToTrash) { return pcc.GetEntry(exp.idxSuperClass); } - if (exp.HasComponentMap && exp.ComponentMap.Any(kvp => uIndexes.Contains(kvp.Value))) + if (exp.HasComponentMap) { - return pcc.GetEntry(exp.ComponentMap.Values.First(uIdx => uIndexes.Contains(uIdx))); + var componentMap = exp.ComponentMap; + if (componentMap.Any(kvp => uIndexes.Contains(kvp.Value + 1))) + { + return pcc.GetEntry(componentMap.Values.First(idx => uIndexes.Contains(idx + 1))); + } } //find stack references diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/ActorNodes.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/ActorNodes.cs index 17b9d719e8..42ba8866c4 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/ActorNodes.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/ActorNodes.cs @@ -164,19 +164,23 @@ public void SetShape(bool polygon, bool cylinder = false) outlinePen = new Pen(GetDefaultShapeColor()); //Can't put this in a class variable becuase it doesn't seem to work for some reason. if (polygon) { - PointF[] polygonShape = get3DBrushShape(); + var polygonShape = get3DBrushShape(); int calculatedHeight = get3DBrushHeight(); if (polygonShape != null) { - shape = PPath.CreatePolygon(polygonShape); - var AveragePoint = GetAveragePoint(polygonShape); - val.X = AveragePoint.X - val.Width / 2; - val.Y = AveragePoint.Y - val.Height / 2; + shape = new PPath(); + foreach (PointF[] polyPoints in polygonShape) + { + shape.AddPolygon(polyPoints); + } + var averagePoint = GetAveragePoint(shape.PathReference.PathPoints); + val.X = averagePoint.X - val.Width / 2; + val.Y = averagePoint.Y - val.Height / 2; if (calculatedHeight >= 0) { SText brushText = new SText($"Brush total height: {calculatedHeight}"); - brushText.X = AveragePoint.X - brushText.Width / 2; - brushText.Y = AveragePoint.Y + 20 - brushText.Height / 2; + brushText.X = averagePoint.X - brushText.Width / 2; + brushText.Y = averagePoint.Y + 20 - brushText.Height / 2; brushText.Pickable = false; brushText.TextAlignment = StringAlignment.Center; shape.AddChild(brushText); diff --git a/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/PathfindingNodeMaster.cs b/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/PathfindingNodeMaster.cs index ce0c95f2bb..d6c40c49f0 100644 --- a/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/PathfindingNodeMaster.cs +++ b/LegendaryExplorer/LegendaryExplorer/Tools/PathfindingEditor/PathfindingNodeMaster.cs @@ -181,7 +181,7 @@ protected int get3DBrushHeight() return -1; } - protected PointF[] get3DBrushShape() + protected IEnumerable get3DBrushShape() { try { @@ -220,7 +220,6 @@ protected PointF[] get3DBrushShape() return null; } ExportEntry brush = export.FileRef.GetUExport(brushComponent.Value); - var graphVertices = new List(); PropertyCollection brushProps = brush.GetProperties(); var brushAggGeom = brushProps.GetProp("BrushAggGeom"); if (brushAggGeom == null) @@ -228,44 +227,61 @@ protected PointF[] get3DBrushShape() return null; } var convexList = brushAggGeom.GetProp>("ConvexElems"); - + if (convexList.Count is 0) + { + return null; + } + var polys = new List(convexList.Count); foreach (StructProperty convexElem in convexList) { - var brushVertices = new List(); - //Vertices var verticiesList = convexElem.GetProp>("VertexData"); - foreach (StructProperty vertex in verticiesList) + var verts = new PointF[verticiesList.Count]; + //Vertices + for (int i = 0; i < verticiesList.Count; i++) { - brushVertices.Add(new Vector3 + StructProperty vertex = verticiesList[i]; + verts[i] = new PointF { X = vertex.GetProp("X") * xScalar, Y = vertex.GetProp("Y") * yScalar, - Z = vertex.GetProp("Z") - }); + }; } - - //FaceTris - var faceTriData = convexElem.GetProp>("FaceTriData"); - float prevX = float.MinValue; - float prevY = float.MinValue; - foreach (IntProperty triPoint in faceTriData) + //2D convex hull algorithm from https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain + int n = verts.Length; + if (n <= 3) { - Vector3 vertex = brushVertices[triPoint]; - if (vertex.X == prevX && vertex.Y == prevY) - { - continue; //Z is on the difference - } + polys.Add(verts); + continue; + } + // Sort points lexicographically + Array.Sort(verts, new PointFLexicalComparer()); + int k = 0; + var hullVerts = new PointF[n * 2]; - float x = vertex.X; - float y = vertex.Y; + // Build lower hull + for (int i = 0; i < n; ++i) + { + while (k >= 2 && Cross(hullVerts[k - 2], hullVerts[k - 1], verts[i]) <= 0) --k; + hullVerts[k++] = verts[i]; + } - prevX = x; - prevY = y; - var graphPoint = new PointF(x, y); - graphVertices.Add(graphPoint); + // Build upper hull + for (int i = n - 1, t = k + 1; i > 0; --i) + { + while (k >= t && Cross(hullVerts[k - 2], hullVerts[k - 1], verts[i - 1]) <= 0) --k; + hullVerts[k++] = verts[i - 1]; } + + var hullPoints = new PointF[k]; + Array.Copy(hullVerts, hullPoints, k); + polys.Add(hullPoints); + } + return polys; + + float Cross(PointF p1, PointF p2, PointF p3) + { + return (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X); } - return graphVertices.ToArray(); } catch (Exception) { @@ -273,6 +289,15 @@ protected PointF[] get3DBrushShape() } } + private struct PointFLexicalComparer : IComparer + { + public int Compare(PointF x, PointF y) + { + int xComparison = x.X.CompareTo(y.X); + return xComparison != 0 ? xComparison : x.Y.CompareTo(y.Y); + } + } + protected Tuple getCylinderDimensions() { try diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/EntryMetadataExportLoader.xaml.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/EntryMetadataExportLoader.xaml.cs index 2e63e66743..235e55d322 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/EntryMetadataExportLoader.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/EntryMetadataExportLoader.xaml.cs @@ -285,9 +285,9 @@ public override void LoadExport(ExportEntry exportEntry) var componentMap = exportEntry.ComponentMap; string components = $"ComponentMap: 0x{40:X2} {componentMap.Count} items\n"; int pairOffset = 44; - foreach ((NameReference name, int uIndex) in componentMap) + foreach ((NameReference name, int index) in componentMap) { - components += $"0x{pairOffset:X2} {name.Instanced} => {uIndex} {exportEntry.FileRef.GetEntryString(uIndex + 1)}\n"; // +1 because it appears to be 0 based? + components += $"0x{pairOffset:X2} {name.Instanced} => {index} {exportEntry.FileRef.GetEntryString(index + 1)}\n"; // +1 because it appears to be 0 based? pairOffset += 12; } diff --git a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs index e2da86644c..ed4ffa12c8 100644 --- a/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs +++ b/LegendaryExplorer/LegendaryExplorer/UserControls/ExportLoaderControls/FaceFXAnimSetEditorControl.xaml.cs @@ -426,6 +426,7 @@ private void linesListBox_Drop(object sender, DragEventArgs e) lineEntry.Line.NameIndex = FaceFX.Names.FindOrAdd(sourceNames[lineEntry.Line.NameIndex]); if (FaceFX.Binary is FaceFXAnimSet animSet) animSet.FixNodeTable(); lineEntry.Line.AnimationNames = lineEntry.Line.AnimationNames.Select(idx => FaceFX.Names.FindOrAdd(sourceNames[idx])).ToList(); + lineEntry.Line.Index = FaceFX.Lines.Count; FaceFX.Lines.Add(lineEntry.Line); if (int.TryParse(lineEntry.Line.ID, out int tlkID)) @@ -750,7 +751,8 @@ private void AddLine_Click(object sender, RoutedEventArgs e) private void CloneLine_Click(object sender, RoutedEventArgs e) { // HenBagle: We don't need to do anything with names here because we're cloning within the same file - FaceFXLineEntry newEntry = new FaceFXLineEntry(SelectedLine.Clone()); + var newEntry = new FaceFXLineEntry(SelectedLine.Clone()); + newEntry.Line.Index = FaceFX.Lines.Count; FaceFX.Lines.Add(newEntry.Line); if (int.TryParse(newEntry.Line.ID, out int tlkID)) diff --git a/LegendaryExplorer/LegendaryExplorerCore/Coalesced/CoalescedConverter.cs b/LegendaryExplorer/LegendaryExplorerCore/Coalesced/CoalescedConverter.cs index 865ad59ccc..388af403b4 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Coalesced/CoalescedConverter.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Coalesced/CoalescedConverter.cs @@ -36,11 +36,16 @@ public static class CoalescedConverter "BioCompat", "BioCredits", "BioDifficulty", + "BioEditor", // LE1 + "BioEditorKeyBindings", // LE1 + "BIoEditorUserSettings", // LE1 "BioEngine", "BioGame", "BioGuiResources", // PC Main Menu PC New Character "BioInput", "BioLightmass", + "BioParty", // LE1 + "BioStringTypeMap", // LE1 "BioTest", "BioUI", "BioQA", diff --git a/LegendaryExplorer/LegendaryExplorerCore/Libraries/Windows/TexConverter.dll b/LegendaryExplorer/LegendaryExplorerCore/Libraries/Windows/TexConverter.dll index 9a8f6bb85f..cd7a4ee347 100644 Binary files a/LegendaryExplorer/LegendaryExplorerCore/Libraries/Windows/TexConverter.dll and b/LegendaryExplorer/LegendaryExplorerCore/Libraries/Windows/TexConverter.dll differ diff --git a/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/EntryChecker.cs b/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/EntryChecker.cs index ed923fda78..9cd739e6ff 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/EntryChecker.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/EntryChecker.cs @@ -159,12 +159,13 @@ public static void CheckReferences(ReferenceCheckPackage item, IMEPackage packag if (exp.HasComponentMap) { - foreach (var c in exp.ComponentMap) + foreach ((_, int index) in exp.ComponentMap) { - if (c.Value != 0 && !package.IsEntry(c.Value)) + int uindex = index + 1; + if (uindex != 0 && !package.IsEntry(uindex)) { // Can components point to 0? I don't think so - item.AddSignificantIssue(localizationDelegate(LECLocalizationShim.string_interp_warningComponentMapItemOutsideTables, prefix, c.Value), exp); + item.AddSignificantIssue(localizationDelegate(LECLocalizationShim.string_interp_warningComponentMapItemOutsideTables, prefix, uindex), exp); } } } diff --git a/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/Relinker.cs b/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/Relinker.cs index 6aec5c103d..ab56ccaa5e 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/Relinker.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Packages/CloningImportingAndRelinking/Relinker.cs @@ -240,11 +240,11 @@ public static void Relink(ExportEntry sourceExport, ExportEntry relinkingExport, if (relinkingExport.HasComponentMap && relinkingExport.ComponentMap.Count > 0) { var newComponentMap = new UMultiMap(); - foreach (var cmk in sourceExport.ComponentMap) + foreach ((NameReference componentName, int componentIndex) in sourceExport.ComponentMap) { // 04/07/2024 - Remove temp cross package mapping and have this call skip its internal relink step since this call is already in a relink - Mgamerz - EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport.FileRef.GetUExport(cmk.Value + 1), relinkingExport.FileRef, relinkingExport, false, rop, out var newComponent); - newComponentMap.Add(cmk.Key, newComponent.UIndex - 1); // TODO: Relink the + EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport.FileRef.GetUExport(componentIndex + 1), relinkingExport.FileRef, relinkingExport, false, rop, out var newComponent); + newComponentMap.Add(componentName, newComponent.UIndex - 1); // TODO: Relink the } relinkingExport.ComponentMap = newComponentMap; } diff --git a/LegendaryExplorer/LegendaryExplorerCore/Packages/ExportEntry.cs b/LegendaryExplorer/LegendaryExplorerCore/Packages/ExportEntry.cs index 21b5dabff3..4cf339aace 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Packages/ExportEntry.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Packages/ExportEntry.cs @@ -556,6 +556,8 @@ public int DataOffset //me1 and me2 only private byte[] _componentMap; + + //Does not contain UIndexes! The ints in this are indexes into the exports array. +1 to get the UIndex public UMultiMap ComponentMap { get diff --git a/LegendaryExplorer/LegendaryExplorerCore/Packages/PackageExtensions.cs b/LegendaryExplorer/LegendaryExplorerCore/Packages/PackageExtensions.cs index 0c71825e22..d0b8422300 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Packages/PackageExtensions.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Packages/PackageExtensions.cs @@ -434,9 +434,9 @@ public static HashSet GetReferencedEntries(this IMEPackage pcc, bool getref } if (exp.HasComponentMap) { - foreach (var kvp in exp.ComponentMap) + foreach ((_, int index) in exp.ComponentMap) { - //theserefs.Add(pcc.GetEntry(kvp.Value)); //THIS IS INCORRECT SHOULD NOT BE ON UINDEX + theserefs.Add(pcc.GetEntry(index + 1)); } } } @@ -742,7 +742,7 @@ public static Dictionary> GetEntriesThatReferenceThisOne(th { result.AddToListAt(exp, "Header: SuperClass"); } - if (exp.HasComponentMap && exp.ComponentMap.Any(kvp => kvp.Value == baseUIndex)) + if (exp.HasComponentMap && exp.ComponentMap.Any(kvp => kvp.Value + 1 == baseUIndex)) { result.AddToListAt(exp, "Header: ComponentMap"); } diff --git a/LegendaryExplorer/LegendaryExplorerCore/Sound/Wwise/WwiseHelper.cs b/LegendaryExplorer/LegendaryExplorerCore/Sound/Wwise/WwiseHelper.cs index eb6a6ad58c..dc01630c60 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Sound/Wwise/WwiseHelper.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Sound/Wwise/WwiseHelper.cs @@ -54,6 +54,7 @@ public static void UpdateReferencedWwiseEventLengths(ExportEntry wwiseStreamExpo tlkId = parsed; specifyByGender = wwiseStreamExport.ObjectName.Name.Contains("player_", StringComparison.OrdinalIgnoreCase); isFemaleStream = splits[i + 1] == "f"; + break; // assume first int we find is the tlk id } } if (tlkId == 0) return; diff --git a/LegendaryExplorer/LegendaryExplorerCore/Unreal/BinaryConverters/AnimSequence.cs b/LegendaryExplorer/LegendaryExplorerCore/Unreal/BinaryConverters/AnimSequence.cs index 5fc7686294..7db5d26f4a 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Unreal/BinaryConverters/AnimSequence.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Unreal/BinaryConverters/AnimSequence.cs @@ -321,10 +321,11 @@ static float getW(float x, float y, float z) private void CompressAnimationData(MEGame game, AnimationCompressionFormat newRotationCompression) { - if (RawAnimationData is null) - { - DecompressAnimationData(); - } + /* SirCxyrtyx 8/12/24: Always decompress, do not use pre-existing RawAnimationData if from a upk. + * In same cases, the RawAnimationData is wrong for unknown reasons ¯\_(ツ)_/¯ + * The compressed data should be regarded as the source of truth + */ + DecompressAnimationData(); keyEncoding = AnimationKeyFormat.AKF_ConstantKeyLerp; posCompression = AnimationCompressionFormat.ACF_None; diff --git a/LegendaryExplorer/LegendaryExplorerCore/Unreal/PSA.cs b/LegendaryExplorer/LegendaryExplorerCore/Unreal/PSA.cs index 2b64b9f0aa..5e7e0e0413 100644 --- a/LegendaryExplorer/LegendaryExplorerCore/Unreal/PSA.cs +++ b/LegendaryExplorer/LegendaryExplorerCore/Unreal/PSA.cs @@ -146,10 +146,11 @@ public static PSA CreateFrom(List animSeqs) }); frameCount += numFrames; - if (animSeq.RawAnimationData is null) - { - animSeq.DecompressAnimationData(); - } + /* SirCxyrtyx 8/12/24: Always decompress, do not use pre-existing RawAnimationData if from a upk. + * In same cases, the RawAnimationData is wrong for unknown reasons ¯\_(ツ)_/¯ + * The compressed data should be regarded as the source of truth + */ + animSeq.DecompressAnimationData(); for (int frameIdx = 0; frameIdx < numFrames; frameIdx++) {