From 08b2da4694ade8d20693ae13fd55fa67799c2c53 Mon Sep 17 00:00:00 2001 From: Benjamin Popp Date: Sun, 31 Mar 2019 15:15:56 -0500 Subject: [PATCH] matched arrays extend together --- src/HexManiac.Core/Models/IDataModel.cs | 3 ++ src/HexManiac.Core/Models/PokemonModel.cs | 2 ++ src/HexManiac.Core/Models/Runs/ArrayRun.cs | 23 +++++++------ src/HexManiac.Core/ViewModels/ViewPort.cs | 40 ++++++++++++++++++++-- src/HexManiac.Tests/ArrayRunTests.cs | 7 ++-- 5 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/HexManiac.Core/Models/IDataModel.cs b/src/HexManiac.Core/Models/IDataModel.cs index 724d3cd2..ec7f585e 100644 --- a/src/HexManiac.Core/Models/IDataModel.cs +++ b/src/HexManiac.Core/Models/IDataModel.cs @@ -9,6 +9,7 @@ namespace HavenSoft.HexManiac.Core.Models { public interface IDataModel : IReadOnlyList { byte[] RawData { get; } new byte this[int index] { get; set; } + IReadOnlyList Arrays { get; } /// /// If dataIndex is in the middle of a run, returns that run. @@ -58,6 +59,8 @@ namespace HavenSoft.HexManiac.Core.Models { public BaseModel(byte[] data) => RawData = data; + public virtual IReadOnlyList Arrays { get; } = new List(); + public byte this[int index] { get => RawData[index]; set => RawData[index] = value; } byte IReadOnlyList.this[int index] => RawData[index]; diff --git a/src/HexManiac.Core/Models/PokemonModel.cs b/src/HexManiac.Core/Models/PokemonModel.cs index 7d16a72a..64af5c9e 100644 --- a/src/HexManiac.Core/Models/PokemonModel.cs +++ b/src/HexManiac.Core/Models/PokemonModel.cs @@ -27,6 +27,8 @@ namespace HavenSoft.HexManiac.Core.Models { public virtual int EarliestAllowedAnchor => 0; + public override IReadOnlyList Arrays => runs.OfType().ToList(); + #region Constructor public PokemonModel(byte[] data, StoredMetadata metadata = null) : base(data) { diff --git a/src/HexManiac.Core/Models/Runs/ArrayRun.cs b/src/HexManiac.Core/Models/Runs/ArrayRun.cs index 4629d087..01b349a2 100644 --- a/src/HexManiac.Core/Models/Runs/ArrayRun.cs +++ b/src/HexManiac.Core/Models/Runs/ArrayRun.cs @@ -284,6 +284,8 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { var nameWithQuotes = PCSString.Convert(owner, nameAddress, sourceArray.ElementContent[0].Length).Trim(); if (nameWithQuotes.Contains(' ')) { names.Add(nameWithQuotes); + } else if (nameWithQuotes.Length < 2) { // final name could just be a single closing quote and nothing else + names.Add(nameWithQuotes); } else { var nameWithoutQuotes = nameWithQuotes.Substring(1, nameWithQuotes.Length - 2); names.Add(nameWithoutQuotes); @@ -330,15 +332,14 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { Length = ElementLength * ElementCount; } - private ArrayRun(IDataModel data, string format, int start, int elementCount, IReadOnlyList segments, IReadOnlyList pointerSources, IReadOnlyList> pointerSourcesForInnerElements) : base(start, pointerSources) { + private ArrayRun(IDataModel data, string format, string lengthFromAnchor, int start, int elementCount, IReadOnlyList segments, IReadOnlyList pointerSources, IReadOnlyList> pointerSourcesForInnerElements) : base(start, pointerSources) { owner = data; FormatString = format; ElementContent = segments; ElementLength = ElementContent.Sum(e => e.Length); ElementCount = elementCount; var closeArray = format.LastIndexOf(ArrayEnd.ToString()); - var length = format.Substring(closeArray + 1); - LengthFromAnchor = length; + LengthFromAnchor = lengthFromAnchor; Length = ElementLength * ElementCount; SupportsPointersToElements = pointerSourcesForInnerElements != null; PointerSourcesForInnerElements = pointerSourcesForInnerElements; @@ -371,11 +372,11 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { if (string.IsNullOrEmpty(length)) { var bestAddress = StandardSearch(data, elementContent, elementLength, out int bestLength); if (bestAddress == Pointer.NULL) return false; - self = new ArrayRun(data, originalFormat + bestLength, bestAddress, bestLength, elementContent, data.GetNextRun(bestAddress).PointerSources, null); + self = new ArrayRun(data, originalFormat + bestLength, string.Empty, bestAddress, bestLength, elementContent, data.GetNextRun(bestAddress).PointerSources, null); } else { var bestAddress = KnownLengthSearch(data, elementContent, elementLength, length, out int bestLength, runFilter); if (bestAddress == Pointer.NULL) return false; - self = new ArrayRun(data, originalFormat, bestAddress, bestLength, elementContent, data.GetNextRun(bestAddress).PointerSources, null); + self = new ArrayRun(data, originalFormat, string.Empty, bestAddress, bestLength, elementContent, data.GetNextRun(bestAddress).PointerSources, null); } if (allowPointersToEntries) self = self.AddSourcesPointingWithinArray(changeToken); @@ -504,7 +505,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { var lastArrayCharacterIndex = FormatString.LastIndexOf(ArrayEnd); var newFormat = FormatString.Substring(0, lastArrayCharacterIndex + 1); if (newFormat != FormatString) newFormat += ElementCount + elementCount; - return new ArrayRun(owner, newFormat, Start, ElementCount + elementCount, ElementContent, PointerSources, PointerSourcesForInnerElements); + return new ArrayRun(owner, newFormat, LengthFromAnchor, Start, ElementCount + elementCount, ElementContent, PointerSources, PointerSourcesForInnerElements); } public ArrayRun AddSourcePointingWithinArray(int source) { @@ -514,7 +515,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { if (index == 0) throw new NotImplementedException(); var newInnerPointerSources = PointerSourcesForInnerElements.ToList(); newInnerPointerSources[index] = newInnerPointerSources[index].Concat(new[] { source }).ToList(); - return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, PointerSources, newInnerPointerSources); + return new ArrayRun(owner, FormatString, LengthFromAnchor, Start, ElementCount, ElementContent, PointerSources, newInnerPointerSources); } public ArrayRun AddSourcesPointingWithinArray(ModelDelta changeToken) { @@ -536,7 +537,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { } var pointerSourcesForInnerElements = results.Cast>().ToList(); - return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, PointerSources, pointerSourcesForInnerElements); + return new ArrayRun(owner, FormatString, LengthFromAnchor, Start, ElementCount, ElementContent, PointerSources, pointerSourcesForInnerElements); } /// @@ -575,7 +576,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { } public IFormattedRun Move(int newStart) { - return new ArrayRun(owner, FormatString, newStart, ElementCount, ElementContent, PointerSources, PointerSourcesForInnerElements); + return new ArrayRun(owner, FormatString, LengthFromAnchor, newStart, ElementCount, ElementContent, PointerSources, PointerSourcesForInnerElements); } public override IFormattedRun RemoveSource(int source) { @@ -586,7 +587,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { newInnerPointerSources.Add(list.Where(item => item != source).ToList()); } - return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, newPointerSources, newInnerPointerSources); + return new ArrayRun(owner, FormatString, LengthFromAnchor, Start, ElementCount, ElementContent, newPointerSources, newInnerPointerSources); } protected override IFormattedRun Clone(IReadOnlyList newPointerSources) { @@ -598,7 +599,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { for (int i = 1; i < PointerSourcesForInnerElements.Count; i++) newInnerPointerSources.Add(PointerSourcesForInnerElements[i]); } - return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, newPointerSources, newInnerPointerSources); + return new ArrayRun(owner, FormatString, LengthFromAnchor, Start, ElementCount, ElementContent, newPointerSources, newInnerPointerSources); } private static List ParseSegments(string segments) { diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs index 071bcea0..25673c89 100644 --- a/src/HexManiac.Core/ViewModels/ViewPort.cs +++ b/src/HexManiac.Core/ViewModels/ViewPort.cs @@ -986,14 +986,48 @@ namespace HavenSoft.HexManiac.Core.ViewModels { } private void CompleteArrayExtension(ArrayRun arrayRun) { - var newRun = (ArrayRun)Model.RelocateForExpansion(history.CurrentChange, arrayRun, arrayRun.Length + arrayRun.ElementLength); - if (newRun != arrayRun) { + var originalArray = arrayRun; + var currentArrayName = Model.GetAnchorFromAddress(-1, arrayRun.Start); + + int depth = 0; + while (arrayRun.LengthFromAnchor != string.Empty) { + depth++; + if (depth == 10) { + // We kept going up the chain of tables but didn't find a top table. Either the table length definitions are circular or very deep. + OnError?.Invoke(this, "Could not extend table safely. Make sure you don't have a circular or deep dependency in your table lengths."); + return; + } + var address = Model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, arrayRun.LengthFromAnchor); + arrayRun = (ArrayRun)Model.GetNextRun(address); + } + + ExtendArrayAndChildren(arrayRun); + + var newRun = (ArrayRun)Model.GetNextRun(Model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, currentArrayName)); + if (newRun.Start != originalArray.Start) { ScrollFromRunMove(arrayRun.Start + arrayRun.Length, arrayRun.Length, newRun); } - Model.ObserveRunWritten(history.CurrentChange, arrayRun.Append(1)); + RefreshBackingData(); } + private void ExtendArrayAndChildren(ArrayRun array) { + var newRun = (ArrayRun)Model.RelocateForExpansion(history.CurrentChange, array, array.Length + array.ElementLength); + newRun = newRun.Append(1); + Model.ObserveRunWritten(history.CurrentChange, newRun); + + foreach(var child in GetDependantArrays(newRun)) { + ExtendArrayAndChildren(child); + } + } + + private IEnumerable GetDependantArrays(ArrayRun parent) { + var anchor = Model.GetAnchorFromAddress(-1, parent.Start); + foreach (var array in Model.Arrays) { + if (array.LengthFromAnchor == anchor) yield return array; + } + } + private void CompletePointerEdit(Point point) { var element = currentView[point.X, point.Y]; var underEdit = (UnderEdit)element.Format; diff --git a/src/HexManiac.Tests/ArrayRunTests.cs b/src/HexManiac.Tests/ArrayRunTests.cs index 1cbca800..72ea9905 100644 --- a/src/HexManiac.Tests/ArrayRunTests.cs +++ b/src/HexManiac.Tests/ArrayRunTests.cs @@ -411,9 +411,10 @@ namespace HavenSoft.HexManiac.Tests { Assert.Equal(0x100, model.GetNextRun(0).Length); } - [Fact(Skip = "Feature not implement yet. Feature is now prioritized beneath array support for pointers.")] + [Fact] public void ArrayExtendsIfBasedOnAnotherNameWhichIsExtended() { var buffer = new byte[0x200]; + for (int i = 0; i < buffer.Length; i++) buffer[i] = 0xFF; var model = new PokemonModel(buffer); var viewPort = new ViewPort("file.txt", model) { Width = 0x10, Height = 0x10 }; var errors = new List(); @@ -427,12 +428,12 @@ namespace HavenSoft.HexManiac.Tests { // test 1: enbiggen derived should enbiggen sample viewPort.SelectionStart = new Point(8, 8); viewPort.Edit("+"); - Assert.Equal(8 * 8 + 8, model.GetNextRun(0).Length); + Assert.Equal(8 * 9, model.GetNextRun(0).Length); // test 2: enbiggen sample should enbiggen derived viewPort.SelectionStart = new Point(8, 4); viewPort.Edit("+"); - Assert.Equal(10, model.GetNextRun(0x80).Length); + Assert.Equal(1 * 10, model.GetNextRun(0x80).Length); } [Fact]