From a78ca56744bffe0fc8ecf9a50ca4116115ef274d Mon Sep 17 00:00:00 2001 From: haven1433 Date: Sun, 23 Oct 2022 20:09:47 -0500 Subject: [PATCH] appending to a table should refresh the table view so that new elements can be seen when in single-table-focus don't write runs into other runs when changing pointers in text-pointer tables. --- src/HexManiac.Core/Models/PokemonModel.cs | 14 +++++++------- .../Models/Runs/ArrayRunElementSegment.cs | 7 ++++++- .../Models/Runs/Factory/PCSRunContentStrategy.cs | 9 ++++++++- src/HexManiac.Core/ViewModels/Tools/TableTool.cs | 2 +- src/HexManiac.Core/ViewModels/ViewPort.cs | 7 ++++++- .../Before_Baseclass/ViewPortScrollTests.cs | 15 +++++++++++++++ src/HexManiac.Tests/TableTests.cs | 13 +++++++++++++ 7 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/HexManiac.Core/Models/PokemonModel.cs b/src/HexManiac.Core/Models/PokemonModel.cs index 642a2287..07591eac 100644 --- a/src/HexManiac.Core/Models/PokemonModel.cs +++ b/src/HexManiac.Core/Models/PokemonModel.cs @@ -1018,15 +1018,15 @@ namespace HavenSoft.HexManiac.Core.Models { IFormattedRun existingRun = null; if (index < 0) { index = ~index; - if (runs.Count == index || (runs[index].Start >= run.Start + run.Length && (index == 0 || runs[index - 1].Start + runs[index - 1].Length <= run.Start))) { + + // check for conflict with previous run / next run + if (index > 0 && run.Start < runs[index - 1].Start + runs[index - 1].Length) { + Debug.Fail($"Trying to add a run at {run.Start:X6} which overlaps a run at {runs[index - 1].Start:X6}"); + } else if (index < runs.Count && runs[index].Start < run.Start + run.Length) { + Debug.Fail($"Trying to add a run at {run.Start:X6} which overlaps a run at {runs[index].Start:X6}"); + } else { runs.Insert(index, run); changeToken.AddRun(run); - } else { - // there's a conflict: the new run was written in a space already being used, but not where another run starts - // I'll need to do something here eventually... but for now, just error - // the right thing to do is probably to erase the existing format in favor of the new thing the user just tried to add. - // if the existing format was an anchor, clear all the pointers that pointed to it, since the writer is declaring that that address is not a valid anchor. - Debug.Fail($"Trying to add a run at {run.Start:X6} which overlaps a run at {runs[index].Start:X6}"); } } else { // replace / merge with existing diff --git a/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs b/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs index 5fed98d5..42e02135 100644 --- a/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs +++ b/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs @@ -136,7 +136,12 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { if (oldRun.PointerSources.Count == 0 && model.GetAnchorFromAddress(-1, oldRun.Start) == string.Empty) model.ClearFormat(token, oldDestination, 1); } var changed = model.WritePointer(token, start, address); - model.ObserveRunWritten(token, new NoInfoRun(address, new SortedSpan(start))); + var existingRun = model.GetNextRun(address); + if (existingRun.Start < address) { + // can't actually add a NoInfoRun here + } else { + model.ObserveRunWritten(token, new NoInfoRun(address, new SortedSpan(start))); + } return changed; } default: diff --git a/src/HexManiac.Core/Models/Runs/Factory/PCSRunContentStrategy.cs b/src/HexManiac.Core/Models/Runs/Factory/PCSRunContentStrategy.cs index 97952ba9..f093097a 100644 --- a/src/HexManiac.Core/Models/Runs/Factory/PCSRunContentStrategy.cs +++ b/src/HexManiac.Core/Models/Runs/Factory/PCSRunContentStrategy.cs @@ -15,7 +15,14 @@ namespace HavenSoft.HexManiac.Core.Models.Runs.Factory { // our token will be a no-change token if we're in the middle of exploring the data. // If so, don't actually add the run. It's enough to know that we _can_ add the run. - if (!(token is NoDataChangeDeltaModel)) owner.ObserveRunWritten(token, new PCSRun(owner, destination, length)); + if (token is not NoDataChangeDeltaModel) { + var existingRun = owner.GetNextRun(destination); + + // don't add a format if it would conflict with an existing format + if (existingRun.Start < destination) return false; + if (existingRun.Start > destination && existingRun.Start < destination + length) return false; + owner.ObserveRunWritten(token, new PCSRun(owner, destination, length)); + } // even if we didn't add the format, we're _capable_ of adding it... so return true return true; diff --git a/src/HexManiac.Core/ViewModels/Tools/TableTool.cs b/src/HexManiac.Core/ViewModels/Tools/TableTool.cs index 72db953f..bcd6dff4 100644 --- a/src/HexManiac.Core/ViewModels/Tools/TableTool.cs +++ b/src/HexManiac.Core/ViewModels/Tools/TableTool.cs @@ -222,8 +222,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { } } else { append.RaiseCanExecuteChanged(); - viewPort.Refresh(); } + viewPort.Refresh(); RequestMenuClose?.Invoke(this, EventArgs.Empty); if (model is PokemonModel pModel) pModel.ResolveConflicts(); } diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs index 253fec5b..928a183e 100644 --- a/src/HexManiac.Core/ViewModels/ViewPort.cs +++ b/src/HexManiac.Core/ViewModels/ViewPort.cs @@ -90,6 +90,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels { public event EventHandler PreviewScrollChanged; + public int DataStart => scroll.DataStart; + + public int DataLength => scroll.DataLength - scroll.DataStart; + public int Width { get => scroll.Width; set { @@ -1252,8 +1256,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels { scroll.DataLength = Model.Count; var selectionStart = ConvertViewPointToAddress(SelectionStart); if (selectionStart > Model.Count + 1) SelectionStart = ConvertAddressToViewPoint(Model.Count + 1); - RefreshBackingData(); scroll.UpdateHeaders(); + if (Model.GetNextRun(selectionStart) is ITableRun table) scroll.SetTableMode(table.Start, table.Length); + RefreshBackingData(); Tools?.TableTool.DataForCurrentRunChanged(); Tools?.SpriteTool.DataForCurrentRunChanged(); UpdateAnchorText(ConvertViewPointToAddress(SelectionStart)); diff --git a/src/HexManiac.Tests/Before_Baseclass/ViewPortScrollTests.cs b/src/HexManiac.Tests/Before_Baseclass/ViewPortScrollTests.cs index b9df96be..c42e57e3 100644 --- a/src/HexManiac.Tests/Before_Baseclass/ViewPortScrollTests.cs +++ b/src/HexManiac.Tests/Before_Baseclass/ViewPortScrollTests.cs @@ -342,5 +342,20 @@ namespace HavenSoft.HexManiac.Tests { Model.ResolveConflicts(); Assert.Equal(int.MaxValue, Model.GetNextRun(0x0).Start); } + + [Fact] + public void SingleTableFocus_ExpandTableStreamWithoutRepoint_SeeFullStream() { + SetFullModel(0xFF); + ViewPort.Edit("@100 00 00 11 11 22 22 @100 ^table[a. b.]!FFFF @000 "); + ViewPort.AllowSingleTableMode = true; + ViewPort.Goto.Execute("table"); + + Assert.Equal(8, ViewPort.DataLength); + + ViewPort.SelectionStart = ViewPort.ConvertAddressToViewPoint(0x104); + ViewPort.Tools.TableTool.Append.Execute(); + + Assert.Equal(10, ViewPort.DataLength); + } } } diff --git a/src/HexManiac.Tests/TableTests.cs b/src/HexManiac.Tests/TableTests.cs index f566c4e1..9de1995c 100644 --- a/src/HexManiac.Tests/TableTests.cs +++ b/src/HexManiac.Tests/TableTests.cs @@ -1290,6 +1290,19 @@ namespace HavenSoft.HexManiac.Tests { Assert.Single(table.Search(Model, "names", 0)); } + [Fact] + public void PointersToTextTable_ChangeToPointToMiddleOfText_NoErrorsRedPointer() { + SetFullModel(0xFF); + ViewPort.Edit(" @000 ^table[ptr<\"\">]3 <100> @100 Content "); + + ViewPort.Edit("@004 <102>"); // points to middle of existing content + + ViewPort.Goto.Execute(0); + var pFormat = (Pointer)ViewPort[4, 0].Format; + Assert.True(pFormat.HasError); // points to the middle of existing content = error pointer + Assert.Equal(0x100, Model.GetNextRun(0x102).Start); // text run still starts at 0x100 + } + private void ArrangeTrainerPokemonTeamData(byte structType, byte pokemonCount, int trainerCount) { CreateTextTable(HardcodeTablesModel.PokemonNameTable, 0x180, "ABCDEFGHIJKLMNOP".Select(c => c.ToString()).ToArray()); CreateTextTable(HardcodeTablesModel.MoveNamesTable, 0x1B0, "qrstuvwxyz".Select(c => c.ToString()).ToArray());