matched arrays extend together

This commit is contained in:
Benjamin Popp
2019-03-31 15:15:56 -05:00
parent 87f270301c
commit 08b2da4694
5 changed files with 58 additions and 17 deletions

View File

@@ -9,6 +9,7 @@ namespace HavenSoft.HexManiac.Core.Models {
public interface IDataModel : IReadOnlyList<byte> {
byte[] RawData { get; }
new byte this[int index] { get; set; }
IReadOnlyList<ArrayRun> Arrays { get; }
/// <summary>
/// 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<ArrayRun> Arrays { get; } = new List<ArrayRun>();
public byte this[int index] { get => RawData[index]; set => RawData[index] = value; }
byte IReadOnlyList<byte>.this[int index] => RawData[index];

View File

@@ -27,6 +27,8 @@ namespace HavenSoft.HexManiac.Core.Models {
public virtual int EarliestAllowedAnchor => 0;
public override IReadOnlyList<ArrayRun> Arrays => runs.OfType<ArrayRun>().ToList();
#region Constructor
public PokemonModel(byte[] data, StoredMetadata metadata = null) : base(data) {

View File

@@ -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<ArrayRunElementSegment> segments, IReadOnlyList<int> pointerSources, IReadOnlyList<IReadOnlyList<int>> pointerSourcesForInnerElements) : base(start, pointerSources) {
private ArrayRun(IDataModel data, string format, string lengthFromAnchor, int start, int elementCount, IReadOnlyList<ArrayRunElementSegment> segments, IReadOnlyList<int> pointerSources, IReadOnlyList<IReadOnlyList<int>> 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<IReadOnlyList<int>>().ToList();
return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, PointerSources, pointerSourcesForInnerElements);
return new ArrayRun(owner, FormatString, LengthFromAnchor, Start, ElementCount, ElementContent, PointerSources, pointerSourcesForInnerElements);
}
/// <summary>
@@ -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<int> 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<ArrayRunElementSegment> ParseSegments(string segments) {

View File

@@ -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<ArrayRun> 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;

View File

@@ -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<string>();
@@ -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]