From 3e16f8ee2043ddc92c8db8ba072db04388139258 Mon Sep 17 00:00:00 2001 From: haven1433 Date: Tue, 20 Dec 2022 22:34:26 -0600 Subject: [PATCH] add stream content recursively for scripts This should make able to work even when there are scripts that point to scripts, as the content will appear after the last attached script. --- .../Core/Autos/StubDataModel.cs | 4 +-- .../Models/Code/ScriptParser.cs | 17 +++++----- .../Models/Code/scriptReference.txt | 1 + src/HexManiac.Core/Models/IDataModel.cs | 4 +-- src/HexManiac.Core/Models/PokemonModel.cs | 6 ++-- src/HexManiac.Tests/CodeToolTests.cs | 32 +++++++++++++++++++ 6 files changed, 48 insertions(+), 16 deletions(-) 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 } }