diff --git a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P3P/P3PTblPatcher.cs b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P3P/P3PTblPatcher.cs index 67f4ce8..9c5bf62 100644 --- a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P3P/P3PTblPatcher.cs +++ b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P3P/P3PTblPatcher.cs @@ -13,6 +13,7 @@ using Persona.Merger.Patching.Tbl.FieldResolvers.P3P.AICalc; using Persona.Merger.Patching.Tbl.FieldResolvers.P3P.Item; using Persona.Merger.Patching.Tbl.FieldResolvers.P3P.Message; +using static Persona.Merger.Patching.Tbl.FieldResolvers.TblPatcherCommon; namespace Persona.Merger.Patching.Tbl.FieldResolvers.P3P; @@ -161,13 +162,7 @@ public unsafe byte[] Apply(List patches, TblType type, byte[]?[]? over P3PTblSegmentFinder.Populate(tblData, segmentCount, originalSegments); // Convert original segments into Memory. - var segments = new Memory[segmentCount]; - for (int x = 0; x < segmentCount; x++) - { - ref var originalSegment = ref originalSegments[x]; - var offset = originalSegment.Pointer - tblData; - segments[x] = new Memory(TblData, (int)offset, originalSegment.Length); - } + var segments = ConvertSegmentsToMemory(segmentCount, originalSegments, tblData, TblData); // Apply Patch(es). for (int x = 0; x < segmentCount; x++) @@ -180,17 +175,10 @@ public unsafe byte[] Apply(List patches, TblType type, byte[]?[]? over foreach (var patch in CollectionsMarshal.AsSpan(patches)) { - if (patch.SegmentDiffs.Count <= x) continue; - var destination = GC.AllocateUninitializedArray(Math.Max(patch.SegmentDiffs[x].LengthAfterPatch, segments[x].Length)); - var patchDiff = patch.SegmentDiffs[x].Data; - - fixed (byte* destinationPtr = &destination[0]) - fixed (byte* currentSegmentPtr = segments[x].Span) - fixed (byte* patchPtr = patchDiff.Span) - { - S56DiffDecoder.Decode(currentSegmentPtr, patchPtr, destinationPtr, (nuint)patch.SegmentDiffs[x].Data.Length, out var numWritten); - segments[x] = new Memory(destination, 0, (int)numWritten); - } + if (patch.SegmentDiffs.Count <= x) + continue; + + ApplyPatch(patches, x, segments); } } @@ -218,38 +206,17 @@ public unsafe byte[] Apply(List patches, TblType type, byte[]?[]? over memoryStream.Write(segments[1].Length / 56); foreach(var segment in segments) memoryStream.Write(segment.Span); + return result; } foreach (var segment in segments) - { - memoryStream.Write(segment.Length); - memoryStream.Write(segment.Span); - memoryStream.AddPadding(P3PTblSegmentFinder.TblSegmentAlignment); - } + WriteSegment(memoryStream, segment, P3PTblSegmentFinder.TblSegmentAlignment); return result; } } - /// - /// Creates a diff for an individual segment and adds it to the patch. - /// - private unsafe void DiffSegment(TblPatch patch, PointerLengthTuple newSegment, PointerLengthTuple originalSegment, T resolver) where T : IEncoderFieldResolver - { - var destination = GC.AllocateUninitializedArray((int)S56DiffEncoder.CalculateMaxDestinationLength(newSegment.Length)); - fixed (byte* destinationPtr = destination) - { - var numEncoded = S56DiffEncoder.Encode(originalSegment.Pointer, newSegment.Pointer, - destinationPtr, (nuint)originalSegment.Length, (nuint)newSegment.Length, resolver); - patch.SegmentDiffs.Add(new TblPatch.SegmentDiff() - { - Data = destination.AsMemory(0, (int)numEncoded), - LengthAfterPatch = newSegment.Length - }); - } - } - public static unsafe Memory? GetSegment(byte[] data, TblType type, int segment) { var segmentCount = P3PTblSegmentFinder.GetSegmentCount(type); diff --git a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P4G/P4GTblPatcher.cs b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P4G/P4GTblPatcher.cs index 5961ee4..1315aa4 100644 --- a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P4G/P4GTblPatcher.cs +++ b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P4G/P4GTblPatcher.cs @@ -13,6 +13,7 @@ using Persona.Merger.Patching.Tbl.FieldResolvers.P4G.AICalc; using Persona.Merger.Patching.Tbl.FieldResolvers.P4G.Item; using Persona.Merger.Patching.Tbl.FieldResolvers.P4G.Message; +using static Persona.Merger.Patching.Tbl.FieldResolvers.TblPatcherCommon; namespace Persona.Merger.Patching.Tbl.FieldResolvers.P4G; @@ -143,13 +144,7 @@ public unsafe byte[] Apply(List patches, TblType type, byte[]?[]? over P4GTblSegmentFinder.Populate(tblData, segmentCount, originalSegments); // Convert original segments into Memory. - var segments = new Memory[segmentCount]; - for (int x = 0; x < segmentCount; x++) - { - ref var originalSegment = ref originalSegments[x]; - var offset = originalSegment.Pointer - tblData; - segments[x] = new Memory(TblData, (int)offset, originalSegment.Length); - } + var segments = ConvertSegmentsToMemory(segmentCount, originalSegments, tblData, TblData); // Apply Patch(es). for (int x = 0; x < segmentCount; x++) @@ -159,19 +154,13 @@ public unsafe byte[] Apply(List patches, TblType type, byte[]?[]? over segments[x] = new Memory(overrides[x]); continue; } + foreach (var patch in CollectionsMarshal.AsSpan(patches)) { - if (patch.SegmentDiffs.Count <= x) continue; - var destination = GC.AllocateUninitializedArray(Math.Max(patch.SegmentDiffs[x].LengthAfterPatch, segments[x].Length)); - var patchDiff = patch.SegmentDiffs[x].Data; - - fixed (byte* destinationPtr = &destination[0]) - fixed (byte* currentSegmentPtr = segments[x].Span) - fixed (byte* patchPtr = patchDiff.Span) - { - S56DiffDecoder.Decode(currentSegmentPtr, patchPtr, destinationPtr, (nuint)patch.SegmentDiffs[x].Data.Length, out var numWritten); - segments[x] = new Memory(destination, 0, (int)numWritten); - } + if (patch.SegmentDiffs.Count <= x) + continue; + + ApplyPatch(patches, x, segments); } } @@ -203,34 +192,12 @@ public unsafe byte[] Apply(List patches, TblType type, byte[]?[]? over } foreach (var segment in segments) - { - memoryStream.Write(segment.Length); - memoryStream.Write(segment.Span); - memoryStream.AddPadding(P4GTblSegmentFinder.TblSegmentAlignment); - } + WriteSegment(memoryStream, segment, P4GTblSegmentFinder.TblSegmentAlignment); return result; } } - /// - /// Creates a diff for an individual segment and adds it to the patch. - /// - private unsafe void DiffSegment(TblPatch patch, PointerLengthTuple newSegment, PointerLengthTuple originalSegment, T resolver) where T : IEncoderFieldResolver - { - var destination = GC.AllocateUninitializedArray((int)S56DiffEncoder.CalculateMaxDestinationLength(newSegment.Length)); - fixed (byte* destinationPtr = destination) - { - var numEncoded = S56DiffEncoder.Encode(originalSegment.Pointer, newSegment.Pointer, - destinationPtr, (nuint)originalSegment.Length, (nuint)newSegment.Length, resolver); - patch.SegmentDiffs.Add(new TblPatch.SegmentDiff() - { - Data = destination.AsMemory(0, (int)numEncoded), - LengthAfterPatch = newSegment.Length - }); - } - } - public static unsafe Memory? GetSegment(byte[] data, TblType type, int segment) { var segmentCount = P4GTblSegmentFinder.GetSegmentCount(type); diff --git a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P5R/P5RTblPatcher.cs b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P5R/P5RTblPatcher.cs index b12f178..6718f4c 100644 --- a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P5R/P5RTblPatcher.cs +++ b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/P5R/P5RTblPatcher.cs @@ -14,6 +14,7 @@ using Reloaded.Memory.Streams; using Sewer56.StructuredDiff; using Sewer56.StructuredDiff.Interfaces; +using static Persona.Merger.Patching.Tbl.FieldResolvers.TblPatcherCommon; namespace Persona.Merger.Patching.Tbl.FieldResolvers.P5R; @@ -148,29 +149,11 @@ public unsafe byte[] Apply(List patches) P5RTblSegmentFinder.Populate(tblData, segmentCount, originalSegments); // Convert original segments into Memory. - var segments = new Memory[segmentCount]; - for (int x = 0; x < segmentCount; x++) - { - ref var originalSegment = ref originalSegments[x]; - var offset = originalSegment.Pointer - tblData; - segments[x] = new Memory(TblData, (int)offset, originalSegment.Length); - } + var segments = ConvertSegmentsToMemory(segmentCount, originalSegments, tblData, TblData); // Apply Patch(es). for (int x = 0; x < segmentCount; x++) - foreach (var patch in CollectionsMarshal.AsSpan(patches)) - { - var destination = GC.AllocateUninitializedArray(Math.Max(patch.SegmentDiffs[x].LengthAfterPatch, segments[x].Length)); - var patchDiff = patch.SegmentDiffs[x].Data; - - fixed (byte* destinationPtr = &destination[0]) - fixed (byte* currentSegmentPtr = segments[x].Span) - fixed (byte* patchPtr = patchDiff.Span) - { - S56DiffDecoder.Decode(currentSegmentPtr, patchPtr, destinationPtr, (nuint)patch.SegmentDiffs[x].Data.Length, out var numWritten); - segments[x] = new Memory(destination, 0, (int)numWritten); - } - } + ApplyPatch(patches, x, segments); // Produce new file. var fileSize = 0; @@ -189,24 +172,6 @@ public unsafe byte[] Apply(List patches) return result; } } - - /// - /// Creates a diff for an individual segment and adds it to the patch. - /// - private unsafe void DiffSegment(TblPatch patch, PointerLengthTuple newSegment, PointerLengthTuple originalSegment, T resolver) where T : IEncoderFieldResolver - { - var destination = GC.AllocateUninitializedArray((int)S56DiffEncoder.CalculateMaxDestinationLength(newSegment.Length)); - fixed (byte* destinationPtr = destination) - { - var numEncoded = S56DiffEncoder.Encode(originalSegment.Pointer, newSegment.Pointer, - destinationPtr, (nuint)originalSegment.Length, (nuint)newSegment.Length, resolver); - patch.SegmentDiffs.Add(new TblPatch.SegmentDiff() - { - Data = destination.AsMemory(0, (int)numEncoded), - LengthAfterPatch = newSegment.Length - }); - } - } } /// diff --git a/Persona.Merger.Common/Patching/Tbl/FieldResolvers/TblPatcherCommon.cs b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/TblPatcherCommon.cs new file mode 100644 index 0000000..b7eb872 --- /dev/null +++ b/Persona.Merger.Common/Patching/Tbl/FieldResolvers/TblPatcherCommon.cs @@ -0,0 +1,78 @@ +using System.Runtime.InteropServices; +using Persona.Merger.Utilities; +using Reloaded.Memory.Streams; +using Sewer56.StructuredDiff; +using Sewer56.StructuredDiff.Interfaces; + +namespace Persona.Merger.Patching.Tbl.FieldResolvers; + +/// +/// Common functionality used in multiple TBL patchers. +/// +internal class TblPatcherCommon +{ + /// + /// Converts a list of segments and their respective slices to Memory{byte} objects. + /// + internal static unsafe Memory[] ConvertSegmentsToMemory(int segmentCount, PointerLengthTuple* originalSegments, byte* tblData, byte[] tblDataArr) + { + var segments = new Memory[segmentCount]; + for (int x = 0; x < segmentCount; x++) + { + ref var originalSegment = ref originalSegments[x]; + var offset = originalSegment.Pointer - tblData; + segments[x] = new Memory(tblDataArr, (int)offset, originalSegment.Length); + } + + return segments; + } + + /// + /// Applies a given list of table patches to the current file's TBL segments. + /// + internal static unsafe void ApplyPatch(List patches, int x, Memory[] segments) + { + foreach (var patch in CollectionsMarshal.AsSpan(patches)) + { + var destination = GC.AllocateUninitializedArray(Math.Max(patch.SegmentDiffs[x].LengthAfterPatch, segments[x].Length)); + var patchDiff = patch.SegmentDiffs[x].Data; + + fixed (byte* destinationPtr = &destination[0]) + fixed (byte* currentSegmentPtr = segments[x].Span) + fixed (byte* patchPtr = patchDiff.Span) + { + S56DiffDecoder.Decode(currentSegmentPtr, patchPtr, destinationPtr, (nuint)patch.SegmentDiffs[x].Data.Length, + out var numWritten); + segments[x] = new Memory(destination, 0, (int)numWritten); + } + } + } + + /// + /// Writes the segment to the stream. + /// + internal static void WriteSegment(ExtendedMemoryStream memoryStream, Memory segment, int alignment) + { + memoryStream.Write(segment.Length); + memoryStream.Write(segment.Span); + memoryStream.AddPadding(alignment); + } + + /// + /// Creates a diff for an individual segment and adds it to the patch. + /// + internal static unsafe void DiffSegment(TblPatch patch, PointerLengthTuple newSegment, PointerLengthTuple originalSegment, T resolver) where T : IEncoderFieldResolver + { + var destination = GC.AllocateUninitializedArray((int)S56DiffEncoder.CalculateMaxDestinationLength(newSegment.Length)); + fixed (byte* destinationPtr = destination) + { + var numEncoded = S56DiffEncoder.Encode(originalSegment.Pointer, newSegment.Pointer, + destinationPtr, (nuint)originalSegment.Length, (nuint)newSegment.Length, resolver); + patch.SegmentDiffs.Add(new TblPatch.SegmentDiff() + { + Data = destination.AsMemory(0, (int)numEncoded), + LengthAfterPatch = newSegment.Length + }); + } + } +} \ No newline at end of file diff --git a/p5rpc.modloader/Merging/BfMerger.cs b/p5rpc.modloader/Merging/BfMerger.cs index 350ad3c..cd0a8a3 100644 --- a/p5rpc.modloader/Merging/BfMerger.cs +++ b/p5rpc.modloader/Merging/BfMerger.cs @@ -186,10 +186,9 @@ await Task.Run(async () => if (aiCalc == null) return null; - if(_game == Game.P4G) - return P4GTblPatcher.GetSegment(aiCalc.Value.ToArray(), Persona.Merger.Patching.Tbl.FieldResolvers.P4G.TblType.AiCalc, index); - else - return P3PTblPatcher.GetSegment(aiCalc.Value.ToArray(), Persona.Merger.Patching.Tbl.FieldResolvers.P3P.TblType.AiCalc, index); + return _game == Game.P4G ? + P4GTblPatcher.GetSegment(aiCalc.Value.ToArray(), Persona.Merger.Patching.Tbl.FieldResolvers.P4G.TblType.AiCalc, index) : + P3PTblPatcher.GetSegment(aiCalc.Value.ToArray(), Persona.Merger.Patching.Tbl.FieldResolvers.P3P.TblType.AiCalc, index); } private async ValueTask CacheBf(Dictionary> pathToFileMap, string route, string[] cpks, List flowPaths, string bindDirectory)