From b6f4dcda5594089d4a2da5372a59ca93751d0180 Mon Sep 17 00:00:00 2001 From: Benjamin Popp Date: Thu, 27 Jun 2019 22:05:08 -0500 Subject: [PATCH] Bug fixes Looking through Gaia, found some interactions that didn't quite work as expected. Fixed them. I've decided that supporting Gaia's method of doing move tutors would be overstepping my bounds. You can totally support them with some manual format inclusions, but doing that automatically would be outside the scope of the purpose of the search algorithm, since Gaia does something rather custom that I don't currently see being done anywhere else, and I don't know what tool did it. It might be custom work specific for Gaia. --- src/HexManiac.Core/Models/AutoSearchModel.cs | 1 + src/HexManiac.Core/Models/PokemonModel.cs | 13 ++++++- .../Tools/IArrayElementViewModel.cs | 7 ++-- .../ViewModels/Tools/TableTool.cs | 2 +- src/HexManiac.Core/ViewModels/ViewPort.cs | 38 ++++++++++--------- .../Visitors/CompleteEditOperation.cs | 5 ++- src/HexManiac.Tests/AutoSearchTests.cs | 3 +- 7 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/HexManiac.Core/Models/AutoSearchModel.cs b/src/HexManiac.Core/Models/AutoSearchModel.cs index 70addf74..82739a51 100644 --- a/src/HexManiac.Core/Models/AutoSearchModel.cs +++ b/src/HexManiac.Core/Models/AutoSearchModel.cs @@ -153,6 +153,7 @@ namespace HavenSoft.HexManiac.Core.Models { }; var list = Find(originalCode, 0x120B00, 0x120C00); + if (list.Count == 0) return; tutorMoves = ReadPointer(list[0] - 4); tutorCompatibility = ReadPointer(list[0] + originalCode.Length); if (tutorMoves < 0 || tutorMoves > Count || tutorCompatibility < 0 || tutorCompatibility > Count) return; diff --git a/src/HexManiac.Core/Models/PokemonModel.cs b/src/HexManiac.Core/Models/PokemonModel.cs index f1aa421f..7315a888 100644 --- a/src/HexManiac.Core/Models/PokemonModel.cs +++ b/src/HexManiac.Core/Models/PokemonModel.cs @@ -749,6 +749,17 @@ namespace HavenSoft.HexManiac.Core.Models { return; } + // case 1.5: unnamed anchor doesn't start where the delete starts, but the pointer to it lives in an array + // this anchor is real, but the format is wrong. Keep the anchor, lose the format. + if (run.Start != originalStart && run.PointerSources != null && run.PointerSources.Any(source => GetNextRun(source) is ArrayRun)) { + var newRun = new NoInfoRun(run.Start, run.PointerSources); + runIndex = BinarySearch(run.Start); + changeToken.RemoveRun(run); + changeToken.AddRun(newRun); + runs[runIndex] = newRun; + return; + } + // case 2: unnamed anchor doesn't start where the delete starts // this anchor shouldn't exist. The things that point to it aren't real pointers. if (run.Start != originalStart) { @@ -797,7 +808,7 @@ namespace HavenSoft.HexManiac.Core.Models { } else if (runs[~index - 1] is ArrayRun array) { ClearPointerWithinArray(changeToken, start, ~index - 1, array); } else { - throw new NotImplementedException(); + Debug.Fail($"Trying to clear a pointer that starts at {start:X6}, but the run in that area seems to be not be a pointer run."); } } else if (sourceToUnmappedName.TryGetValue(start, out var name)) { changeToken.RemoveUnmappedPointer(start, name); diff --git a/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs b/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs index 491725fc..e0a7ec7e 100644 --- a/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs @@ -262,10 +262,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { var destination = model.ReadPointer(start); - // by the time we get this far, we're guaranteed that this will be a PCSRun. - // if it's not a PCSRun, we shouldn't have asked to construct this object. + // by the time we get this far, we're nearly guaranteed that this will be a IStreamRun. + // if it's not an IStreamRun, it's because the pointer in the array doesn't actually point to a valid stream. + // at which point, we don't want to display any content. var run = (IStreamRun)model.GetNextRun(destination); - content = run.SerializeRun(); + content = run.SerializeRun() ?? string.Empty; } } diff --git a/src/HexManiac.Core/ViewModels/Tools/TableTool.cs b/src/HexManiac.Core/ViewModels/Tools/TableTool.cs index f5f0132e..560e9324 100644 --- a/src/HexManiac.Core/ViewModels/Tools/TableTool.cs +++ b/src/HexManiac.Core/ViewModels/Tools/TableTool.cs @@ -164,7 +164,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { viewModel.DataChanged += ForwardModelChanged; if (item is ArrayRunPointerSegment pointerSegment) { var destination = model.ReadPointer(itemAddress); - if (destination != Pointer.NULL && pointerSegment.DestinationDataMatchesPointerFormat(model, new NoDataChangeDeltaModel(), destination)) { + if (destination != Pointer.NULL && model.GetNextRun(destination) is IStreamRun && pointerSegment.DestinationDataMatchesPointerFormat(model, new NoDataChangeDeltaModel(), destination)) { if (pointerSegment.InnerFormat == PCSRun.SharedFormatString || pointerSegment.InnerFormat == PLMRun.SharedFormatString) { var streamElement = new StreamArrayElementViewModel(history, (FieldArrayElementViewModel)viewModel, model, item.Name, itemAddress); streamElement.DataChanged += ForwardModelChanged; diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs index 87d8a05b..138e154e 100644 --- a/src/HexManiac.Core/ViewModels/ViewPort.cs +++ b/src/HexManiac.Core/ViewModels/ViewPort.cs @@ -329,14 +329,16 @@ namespace HavenSoft.HexManiac.Core.ViewModels { OnError?.Invoke(this, string.Empty); var newRun = Model.GetNextRun(index); if (newRun is ArrayRun array) { - // if the format changed (ignoring length), run a goto to update the display width - if (run is ArrayRun array2 && !array.HasSameSegments(array2)) { - selection.PropertyChanged -= SelectionPropertyChanged; // to keep from double-updating the AnchorText - Goto.Execute(index.ToString("X2")); - selection.PropertyChanged += SelectionPropertyChanged; + using (ModelCacheScope.CreateScope(Model)) { + // if the format changed (ignoring length), run a goto to update the display width + if (run is ArrayRun array2 && !array.HasSameSegments(array2)) { + selection.PropertyChanged -= SelectionPropertyChanged; // to keep from double-updating the AnchorText + Goto.Execute(index.ToString("X2")); + selection.PropertyChanged += SelectionPropertyChanged; + } + UpdateColumnHeaders(); + Tools.RefreshContent(); } - UpdateColumnHeaders(); - Tools.RefreshContent(); } RefreshBackingData(); } else { @@ -941,17 +943,19 @@ namespace HavenSoft.HexManiac.Core.ViewModels { // open tool var byteOffset = scroll.ViewPointToDataIndex(new Point(x, y)); var currentRun = Model.GetNextRun(byteOffset); - if (currentRun is IStreamRun) { - Tools.StringTool.Address = currentRun.Start; - Tools.SelectedIndex = Tools.IndexOf(Tools.StringTool); - } else if (currentRun is ArrayRun array) { - var offsets = array.ConvertByteOffsetToArrayOffset(byteOffset); - if (format is PCS) { - Tools.StringTool.Address = offsets.SegmentStart - offsets.ElementIndex * array.ElementLength; + using (ModelCacheScope.CreateScope(Model)) { + if (currentRun is IStreamRun) { + Tools.StringTool.Address = currentRun.Start; Tools.SelectedIndex = Tools.IndexOf(Tools.StringTool); - } else { - Tools.TableTool.Address = array.Start + offsets.ElementIndex * array.ElementLength; - Tools.SelectedIndex = Tools.IndexOf(Tools.TableTool); + } else if (currentRun is ArrayRun array) { + var offsets = array.ConvertByteOffsetToArrayOffset(byteOffset); + if (format is PCS) { + Tools.StringTool.Address = offsets.SegmentStart - offsets.ElementIndex * array.ElementLength; + Tools.SelectedIndex = Tools.IndexOf(Tools.StringTool); + } else { + Tools.TableTool.Address = array.Start + offsets.ElementIndex * array.ElementLength; + Tools.SelectedIndex = Tools.IndexOf(Tools.TableTool); + } } } } diff --git a/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs b/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs index b1184e94..c15d259c 100644 --- a/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs +++ b/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs @@ -241,8 +241,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors { Model.ExpandData(CurrentChange, memoryLocation + 3); var currentRun = Model.GetNextRun(memoryLocation); - bool inArray = currentRun.Start <= memoryLocation && currentRun is ArrayRun; - var sources = currentRun.PointerSources; + if (currentRun.Start > memoryLocation) currentRun = null; + bool inArray = currentRun is ArrayRun && currentRun.Start <= memoryLocation; + var sources = currentRun?.PointerSources; if (!inArray) { if (destination != string.Empty) { diff --git a/src/HexManiac.Tests/AutoSearchTests.cs b/src/HexManiac.Tests/AutoSearchTests.cs index 4eac8d9b..47ccbdb3 100644 --- a/src/HexManiac.Tests/AutoSearchTests.cs +++ b/src/HexManiac.Tests/AutoSearchTests.cs @@ -209,7 +209,8 @@ namespace HavenSoft.HexManiac.Tests { var compatibilityLocation = model.GetAddressFromAnchor(noChange, -1, "tutorcompatibility"); // ruby and sapphire have no tutors - if (game.Contains("Ruby") || game.Contains("Sapphire")) { + // Gaia has move tutors, but it does a bunch of custom stuff (multiple tables) so I don't feel bad about not supporting it by default. + if (game.Contains("Ruby") || game.Contains("Sapphire") || game.Contains("Gaia")) { Assert.Equal(Pointer.NULL, movesLocation); Assert.Equal(Pointer.NULL, compatibilityLocation); return;