diff --git a/src/HexManiac.Core/Core/Autos/StubDataModel.cs b/src/HexManiac.Core/Core/Autos/StubDataModel.cs index d13e5260..3f170604 100644 --- a/src/HexManiac.Core/Core/Autos/StubDataModel.cs +++ b/src/HexManiac.Core/Core/Autos/StubDataModel.cs @@ -753,7 +753,7 @@ namespace HavenSoft.HexManiac.Core.Models } } - public Func GetScriptLength; - int IDataModel.GetScriptLength(IScriptStartRun run) => GetScriptLength?.Invoke(run) ?? default; + public Func, int> GetScriptLength; + int IDataModel.GetScriptLength(IScriptStartRun run, IDictionary destinationLengths) => GetScriptLength?.Invoke(run, destinationLengths) ?? default; } } diff --git a/src/HexManiac.Core/Models/Code/ScriptParser.cs b/src/HexManiac.Core/Models/Code/ScriptParser.cs index 6dc29440..0f671ebe 100644 --- a/src/HexManiac.Core/Models/Code/ScriptParser.cs +++ b/src/HexManiac.Core/Models/Code/ScriptParser.cs @@ -19,7 +19,7 @@ namespace HavenSoft.HexManiac.Core.Models.Code { public ScriptParser(IReadOnlyList engine, byte endToken) => (this.engine, this.endToken) = (engine, endToken); - public int GetScriptSegmentLength(IDataModel model, int address) => engine.GetScriptSegmentLength(model, address); + public int GetScriptSegmentLength(IDataModel model, int address) => engine.GetScriptSegmentLength(model, address, new Dictionary()); public string Parse(IDataModel data, int start, int length) { var builder = new StringBuilder(); @@ -493,13 +493,13 @@ namespace HavenSoft.HexManiac.Core.Models.Code { return string.Join(Environment.NewLine, candidates.Select(line => line.Usage)); } - public static int GetArgLength(IDataModel model, IScriptArg arg, int start) { + public static int GetArgLength(IDataModel model, IScriptArg arg, int start, IDictionary destinationLengths) { if (arg.Type == ArgType.Pointer && arg.PointerType != ExpectedPointerType.Unknown) { var destination = model.ReadPointer(start); if (destination >= 0 && destination < model.Count) { var run = model.GetNextRun(destination); if (run is IScriptStartRun scriptStart && scriptStart.Start == destination && scriptStart.Start > start) { - return model.GetScriptLength(scriptStart); + return model.GetScriptLength(scriptStart, destinationLengths); } else if (run.Start == destination) { // we only want to add this run's length as part of the script if: // (1) the run has no name @@ -665,7 +665,7 @@ namespace HavenSoft.HexManiac.Core.Models.Code { public int CompiledByteLength(IDataModel model, int start, IDictionary destinationLengths) { var length = LineCode.Count; foreach (var arg in Args) { - var argLength = ScriptParser.GetArgLength(model, arg, start + length); + var argLength = ScriptParser.GetArgLength(model, arg, start + length, destinationLengths); if (argLength > 0) destinationLengths[model.ReadPointer(start + length)] = argLength; length += arg.Length(default, -1); } @@ -807,7 +807,7 @@ namespace HavenSoft.HexManiac.Core.Models.Code { public int CompiledByteLength(IDataModel model, int start, IDictionary destinationLengths) { var length = LineCode.Count; foreach (var arg in Args) { - var argLength = ScriptParser.GetArgLength(model, arg, start + length); + var argLength = ScriptParser.GetArgLength(model, arg, start + length, destinationLengths); if (argLength > 0) destinationLengths[model.ReadPointer(start + length)] = argLength; length += arg.Length(model, start + length); } @@ -1301,16 +1301,15 @@ namespace HavenSoft.HexManiac.Core.Models.Code { return (ScriptLine)self.FirstOrDefault(option => option is ScriptLine && option.Matches(data, start)); } - public static int GetScriptSegmentLength(this IReadOnlyList self, IDataModel model, int address) { + public static int GetScriptSegmentLength(this IReadOnlyList self, IDataModel model, int address, IDictionary destinationLengths) { int length = 0; - var destinations = new Dictionary(); while (true) { var line = self.GetMatchingLine(model, address + length); if (line == null) break; - length += line.CompiledByteLength(model, address + length, destinations); + length += line.CompiledByteLength(model, address + length, destinationLengths); if (line.IsEndingCommand) break; } - while (destinations.TryGetValue(address + length, out int argLength)) { + while (destinationLengths.TryGetValue(address + length, out int argLength)) { length += argLength; } return length; diff --git a/src/HexManiac.Core/Models/Code/scriptReference.txt b/src/HexManiac.Core/Models/Code/scriptReference.txt index caff1389..47513679 100644 --- a/src/HexManiac.Core/Models/Code/scriptReference.txt +++ b/src/HexManiac.Core/Models/Code/scriptReference.txt @@ -33,6 +33,7 @@ msgbox.item 0F 00 msg<""> 1A 00 80 item:data.items.stats 1A 01 80 count: 1A # followed by a standard 'put away' message. msgbox.fanfare 31 song:songnames 67 ptr<""> 66 32 register.matchcall trainer:data.trainers.stats -> register.matchcall 16 04 80 trainer:data.trainers.stats 25 EA 01 1A 00 80 trainer:data.trainers.stats 09 08 +if.compare variable: condition.script_compare value: pointer<`xse`> -> if.compare 21 variable: value: 06 condition.script_compare pointer<`xse`> 00 nop # does nothing diff --git a/src/HexManiac.Core/Models/IDataModel.cs b/src/HexManiac.Core/Models/IDataModel.cs index 0a36d8f8..98053759 100644 --- a/src/HexManiac.Core/Models/IDataModel.cs +++ b/src/HexManiac.Core/Models/IDataModel.cs @@ -97,7 +97,7 @@ namespace HavenSoft.HexManiac.Core.Models { bool WriteValue(ModelDelta changeToken, int address, int value); int ReadPointer(int address); int ReadValue(int address); - int GetScriptLength(IScriptStartRun run); + int GetScriptLength(IScriptStartRun run, IDictionary destinationLengths); SortedSpan GetUnmappedSourcesToAnchor(string anchor); void SetUnmappedConstant(ModelDelta changeToken, string name, int value); @@ -294,7 +294,7 @@ namespace HavenSoft.HexManiac.Core.Models { public abstract void ClearPointer(ModelDelta currentChange, int source, int destination); - public virtual int GetScriptLength(IScriptStartRun run) => 1; + public virtual int GetScriptLength(IScriptStartRun run, IDictionary destinationLengths) => 1; public int ReadValue(int index) => BitConverter.ToInt32(RawData, index); diff --git a/src/HexManiac.Core/Models/PokemonModel.cs b/src/HexManiac.Core/Models/PokemonModel.cs index 84f7df9a..505140da 100644 --- a/src/HexManiac.Core/Models/PokemonModel.cs +++ b/src/HexManiac.Core/Models/PokemonModel.cs @@ -1553,19 +1553,19 @@ namespace HavenSoft.HexManiac.Core.Models { } } - public override int GetScriptLength(IScriptStartRun run) { + public override int GetScriptLength(IScriptStartRun run, IDictionary destinationLengths) { IReadOnlyList lines = null; if (run is XSERun) lines = singletons.ScriptLines; if (run is BSERun) lines = singletons.BattleScriptLines; if (run is ASERun) lines = singletons.AnimationScriptLines; if (run is TSERun) lines = singletons.BattleAIScriptLines; - return Math.Max(1, lines.GetScriptSegmentLength(this, run.Start)); + return Math.Max(1, lines.GetScriptSegmentLength(this, run.Start, destinationLengths)); } public override T RelocateForExpansion(ModelDelta changeToken, T run, int minimumLength) { int currentLength = run.Length; if (run is IScriptStartRun scriptStart) { - currentLength = GetScriptLength(scriptStart); + currentLength = GetScriptLength(scriptStart, new Dictionary()); } if (minimumLength <= currentLength) return run; if (CanSafelyUse(run.Start + currentLength, run.Start + minimumLength)) return run; diff --git a/src/HexManiac.Tests/CodeToolTests.cs b/src/HexManiac.Tests/CodeToolTests.cs index 657e7757..cf9b7610 100644 --- a/src/HexManiac.Tests/CodeToolTests.cs +++ b/src/HexManiac.Tests/CodeToolTests.cs @@ -505,6 +505,38 @@ applymovement 0 Assert.Contains("", script); } + [Fact] + public void ScriptWithTextAfterBranch_Decompile_IncludedInLength() { + EventScript = @" + lock + msgbox.yesno +{ +Is the answer yes? +} + if.compare 0x800D = 1 + if.compare 0x800D = 0 + release + end +yes: + msgbox.default +{ +You said yes! +} + release + end +no: + msgbox.default +{ +You said no! +} + release + end"; + + var length = Tool.ScriptParser.FindLength(Model, 0); + + Assert.Equal(99, length); + } + // TODO test that we get an error (not an exception) if we do auto on an unformatted pointer } }