diff --git a/src/HexManiac.Core/Core/SystemExtensions.cs b/src/HexManiac.Core/Core/SystemExtensions.cs index 261a638e..55990149 100644 --- a/src/HexManiac.Core/Core/SystemExtensions.cs +++ b/src/HexManiac.Core/Core/SystemExtensions.cs @@ -28,6 +28,10 @@ namespace HavenSoft.HexManiac.Core { return value; } + public static bool InRange(this T value, T min, T max) where T : IComparable { + return min.CompareTo(value) <= 0 && value.CompareTo(max) < 0; + } + public static bool TryParseInt(this string str, out int result) { if (str.StartsWith("0x") && int.TryParse(str.Substring(2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out result)) return true; if (int.TryParse(str, out result)) return true; diff --git a/src/HexManiac.Core/Models/Code/ScriptParser.cs b/src/HexManiac.Core/Models/Code/ScriptParser.cs index 61ed79e9..f857562e 100644 --- a/src/HexManiac.Core/Models/Code/ScriptParser.cs +++ b/src/HexManiac.Core/Models/Code/ScriptParser.cs @@ -229,24 +229,31 @@ namespace HavenSoft.HexManiac.Core.Models.Code { } } else { var destination = model.ReadPointer(address + length); - if (destination >= 0 && destination < model.Count) { - if (model.GetNextRun(address + length) is PointerRun pointerRun && pointerRun.Start == address + length) { - // no need to clear/update + if (destination.InRange(0, model.Count) && !destination.InRange(address + length, address + length + 4)) { + var destinationRun = model.GetNextRun(destination); + if (destinationRun.Start < destination) { + // we're trying to point into already formatted data + // don't add the pointer source or destination } else { - model.ClearFormat(token, address + length, 4); - model.ObserveRunWritten(token, new PointerRun(address + length)); - } - if (arg.PointerType == ExpectedPointerType.Script) toProcess.Add(destination); - if (arg.PointerType == ExpectedPointerType.Text) { - WriteTextStream(model, token, destination, address + length); - } else if (arg.PointerType == ExpectedPointerType.Movement) { - WriteMovementStream(model, token, destination, address + length); - } else if (arg.PointerType == ExpectedPointerType.Mart) { - WriteMartStream(model, token, destination, address + length); - } else if (arg.PointerType == ExpectedPointerType.Decor) { - WriteDecorStream(model, token, destination, address + length); - } else if (arg.PointerType == ExpectedPointerType.SpriteTemplate) { - WriteSpriteTemplateStream(model, token, destination, address + length); + var pointerSource = model.GetNextRun(address + length); + if (pointerSource is PointerRun pointerRun && pointerRun.Start == address + length) { + // no need to clear/update + } else { + model.ClearFormat(token, address + length, 4); + model.ObserveRunWritten(token, new PointerRun(address + length)); + } + if (arg.PointerType == ExpectedPointerType.Script) toProcess.Add(destination); + if (arg.PointerType == ExpectedPointerType.Text) { + WriteTextStream(model, token, destination, address + length); + } else if (arg.PointerType == ExpectedPointerType.Movement) { + WriteMovementStream(model, token, destination, address + length); + } else if (arg.PointerType == ExpectedPointerType.Mart) { + WriteMartStream(model, token, destination, address + length); + } else if (arg.PointerType == ExpectedPointerType.Decor) { + WriteDecorStream(model, token, destination, address + length); + } else if (arg.PointerType == ExpectedPointerType.SpriteTemplate) { + WriteSpriteTemplateStream(model, token, destination, address + length); + } } } } diff --git a/src/HexManiac.Tests/CodeToolTests.cs b/src/HexManiac.Tests/CodeToolTests.cs index d49164ca..974f15f4 100644 --- a/src/HexManiac.Tests/CodeToolTests.cs +++ b/src/HexManiac.Tests/CodeToolTests.cs @@ -706,7 +706,48 @@ label2:;goto <000050>;end"; Assert.Single(Messages); } - // TODO trainer ai script change -> no auto repoint? + [Theory] + [InlineData("02")] + [InlineData("03")] + [InlineData("04")] + [InlineData("05")] + public void ScriptWithPointerToPointer_Format_NoPointerFormat(string destination) { + SetFullModel(0xFF); + $"06 00 {destination} 00 00 08 02".ToByteArray().WriteInto(Model.RawData, 0); + ViewPort.Edit("^script`xse`"); + + EventScript += " "; // force formatting + + var run = Model.GetNextRun(1); + Assert.Equal(int.MaxValue, run.Start); + } + + [Theory] + [InlineData("09")] + [InlineData("0A")] + [InlineData("0B")] + public void ScriptWithPointerToFuturePointer_Format_OnlySecondPointerKept(string destination) { + SetFullModel(0xFF); + $"06 00 {destination} 00 00 08 06 00 00 01 00 08 02".ToByteArray().WriteInto(Model.RawData, 0); + ViewPort.Edit("^script`xse`"); + + EventScript += " "; // force formatting + + var run = Model.GetNextRun(1); + Assert.Equal(8, run.Start); + } + + [Fact] + public void ScriptWithPointerIntoExistingData_Format_DoNotFormatPointer() { + SetFullModel(0xFF); + CreateTextTable("names", 0x100, "Adam", "Brad", "Carl", "Dave", "Eric", "Fred"); + "06 00 02 01 00 08 02".ToByteArray().WriteInto(Model.RawData, 0); + ViewPort.Edit("@000 ^script`xse`"); + + EventScript += " "; // force formatting + + Model.ResolveConflicts(); // no conflicts = pass + } // TODO test that we get an error (not an exception) if we do auto on an unformatted pointer }