diff --git a/src/HexManiac.Core/HexManiac.Core.csproj b/src/HexManiac.Core/HexManiac.Core.csproj index 94f03abc..896e1cf5 100644 --- a/src/HexManiac.Core/HexManiac.Core.csproj +++ b/src/HexManiac.Core/HexManiac.Core.csproj @@ -158,6 +158,9 @@ + + Always + Always diff --git a/src/HexManiac.Core/Models/Code/ScriptParser.cs b/src/HexManiac.Core/Models/Code/ScriptParser.cs index 8c18da5c..3b9dc202 100644 --- a/src/HexManiac.Core/Models/Code/ScriptParser.cs +++ b/src/HexManiac.Core/Models/Code/ScriptParser.cs @@ -66,7 +66,10 @@ namespace HavenSoft.HexManiac.Core.Models.Code { } // TODO refactor to rely on CollectScripts rather than duplicate code - public void FormatScript(ModelDelta token, IDataModel model, int address, IReadOnlyList sources = null) { + public void FormatScript(ModelDelta token, IDataModel model, int address, IReadOnlyList sources = null) where TSERun : IScriptStartRun { + Func, IScriptStartRun> constructor = (a, s) => new XSERun(a, s); + if (typeof(TSERun) == typeof(BSERun)) constructor = (a, s) => new BSERun(a, s); + var processed = new List(); var toProcess = new List { address }; while (toProcess.Count > 0) { @@ -74,9 +77,9 @@ namespace HavenSoft.HexManiac.Core.Models.Code { toProcess.RemoveAt(toProcess.Count - 1); if (processed.Contains(address)) continue; var existingRun = model.GetNextRun(address); - if (!(existingRun is XSERun && existingRun.Start == address)) { + if (!(existingRun is TSERun && existingRun.Start == address)) { if (sources == null && existingRun.Start != address) sources = model.SearchForPointersToAnchor(token, address); - model.ObserveAnchorWritten(token, string.Empty, new XSERun(address, sources)); + model.ObserveAnchorWritten(token, string.Empty, constructor(address, sources)); sources = null; } int length = 0; @@ -265,7 +268,7 @@ namespace HavenSoft.HexManiac.Core.Models.Code { string Decompile(IDataModel data, int start); } - public class ScriptLine : IScriptLine { + public abstract class ScriptLine : IScriptLine { private readonly List documentation = new List(); public const string Hex = "0123456789ABCDEF"; @@ -276,12 +279,11 @@ namespace HavenSoft.HexManiac.Core.Models.Code { public IReadOnlyList Documentation => documentation; public string Usage { get; } - private static readonly byte[] endCodes = new byte[] { 0x02, 0x03, 0x05, 0x08, 0x0A, 0x0C, 0x0D }; - public bool IsEndingCommand { get; } - public bool PointsToNextScript => LineCode.Count == 1 && LineCode[0].IsAny(4, 5, 6, 7); - public bool PointsToText => LineCode.Count == 1 && LineCode[0].IsAny(0x0F, 0x67); - public bool PointsToMovement => LineCode.Count == 1 && LineCode[0].IsAny(0x4F, 0x50); - public bool PointsToMart => LineCode.Count == 1 && LineCode[0].IsAny(0x86, 0x87, 0x88); + public virtual bool IsEndingCommand { get; } + public virtual bool PointsToNextScript { get; } + public virtual bool PointsToText { get; } + public virtual bool PointsToMovement { get; } + public virtual bool PointsToMart { get; } public ScriptLine(string engineLine) { var docSplit = engineLine.Split(new[] { '#' }, 2); @@ -308,7 +310,6 @@ namespace HavenSoft.HexManiac.Core.Models.Code { LineCode = lineCode; Args = args; CompiledByteLength = LineCode.Count + Args.Sum(arg => arg.Length); - IsEndingCommand = LineCode.Count == 1 && endCodes.Contains(LineCode[0]); } public void AddDocumentation(string doc) => documentation.Add(doc); @@ -410,6 +411,26 @@ namespace HavenSoft.HexManiac.Core.Models.Code { } } + public class XSEScriptLine : ScriptLine { + public XSEScriptLine(string engineLine) : base(engineLine) { } + + public override bool IsEndingCommand => LineCode.Count == 1 && LineCode[0].IsAny(0x02, 0x03, 0x05, 0x08, 0x0A, 0x0C, 0x0D); + public override bool PointsToNextScript => LineCode.Count == 1 && LineCode[0].IsAny(4, 5, 6, 7); + public override bool PointsToText => LineCode.Count == 1 && LineCode[0].IsAny(0x0F, 0x67); + public override bool PointsToMovement => LineCode.Count == 1 && LineCode[0].IsAny(0x4F, 0x50); + public override bool PointsToMart => LineCode.Count == 1 && LineCode[0].IsAny(0x86, 0x87, 0x88); + } + + public class BSEScriptLine : ScriptLine { + public BSEScriptLine(string engineLine) : base(engineLine) { } + + public override bool IsEndingCommand => true; + public override bool PointsToNextScript => false; + public override bool PointsToText => false; + public override bool PointsToMovement => false; + public override bool PointsToMart => false; + } + public class ScriptArg { public ArgType Type { get; } public string Name { get; } diff --git a/src/HexManiac.Core/Models/Code/battleScriptReference.txt b/src/HexManiac.Core/Models/Code/battleScriptReference.txt new file mode 100644 index 00000000..8e25ce7a --- /dev/null +++ b/src/HexManiac.Core/Models/Code/battleScriptReference.txt @@ -0,0 +1,331 @@ +00 attackcanceler +01 accuracycheck param0:: param1: +02 attackstring +03 ppreduce +04 critcalc +05 damagecalc +06 typecalc +07 adjustnormaldamage +08 adjustnormaldamage2 +09 attackanimation +0a waitanimation +0b healthbarupdate battler. +0c datahpupdate battler. +0d critmessage +0e effectivenesssound +0f resultmessage +10 printstring id: +11 printselectionstring id: +12 waitmessage param0: +13 printfromtable ptr<> +14 printselectionstringfromtable ptr<> +15 seteffectwithchance +16 seteffectprimary +17 seteffectsecondary +18 clearstatusfromeffect battler. +19 tryfaintmon battler. fromMove. ptr<> +1a dofaintanimation battler. +1b cleareffectsonfaint battler. +1c jumpifstatus battler. status1:: ptr<> +1d jumpifstatus2 battler. status2:: ptr<> +1e jumpifability param0. ability. ptr<> +1f jumpifsideaffecting battler. sidestatus: ptr<> +20 jumpifstat battler. ifflag. stat. value. ptr<> +21 jumpifstatus3condition battler. status3:: param2. ptr<> +22 jumpiftype battler. type. ptr<> +23 getexp battler. +24 atk24 ptr<> +25 movevaluescleanup +26 setmultihit value. +27 decrementmultihit value:: +28 goto ptr<> +29 jumpifbyte ifflag. param1:: param2. param3:: +2a jumpifhalfword ifflag. param1:: param2: param3:: +2b jumpifword ifflag. param1:: param2:: param3:: +2c jumpifarrayequal param0:: param1:: param2. param3:: +2d jumpifarraynotequal param0:: param1:: param2. param3:: +2e setbyte ptr<> param1. +2f addbyte ptr<> param1. +30 subbyte ptr<> param1. +31 copyarray param0:: param1:: param2. +32 copyarraywithindex param0:: param1:: param2:: param3. +33 orbyte ptr<> param1. +34 orhalfword ptr<> param1: +35 orword ptr<> param1:: +36 bicbyte ptr<> param1. +37 bichalfword ptr<> param1: +38 bicword ptr<> param1:: +39 pause param0: +3a waitstate +3b healthbar_update battler. +3c return +3d end +3e end2 +3f end3 +40 jumpifaffectedbyprotect ptr<> +41 call ptr<> +42 jumpiftype2 battler. type. ptr<> +43 jumpifabilitypresent ability. ptr<> +44 endselectionscript +45 playanimation battler. param1. param2:: +46 playanimation2 battler. param1:: param2:: +47 setgraphicalstatchangevalues +48 playstatchangeanimation battler. param1. param2. +49 moveend param0. param1. + +# Help macros for 5 uses of moveend command +#XX moveendall # All cases +# moveend 0, 0 +#XX se moveendcase case # Chosen case +# moveend 1, 0 +#XX om moveendfrom from # All cases from (inclusive) +# moveend 0, 0 +#XX moveendto {to} # All cases from 0 to (not inclusive) +# moveend 2, {to} +#XX om moveendfromto from, {to} # Cases from (inclusive) to (not inclusive) +# moveend 2, {to} + +4a typecalc2 +4b returnatktoball +4c getswitchedmondata battler. +4d switchindataupdate battler. +4e switchinanim battler. dontclearsubstitutebit. +4f jumpifcantswitch battler. ptr<> +50 openpartyscreen param0. param1:: +51 switchhandleorder battler. param1. +52 switchineffects battler. +53 trainerslidein battler. +54 playse song: +55 fanfare song: +56 playfaintcry battler. +57 atk57 +58 returntoball battler. +59 handlelearnnewmove param0:: param1:: param2. +5a yesnoboxlearnmove param0:: +5b yesnoboxstoplearningmove param0:: +5c hitanimation battler. +5d getmoneyreward addr:: +5e atk5E battler. +5f swapattackerwithtarget +60 incrementgamestat param0. +61 drawpartystatussummary battler. +62 hidepartystatussummary battler. +63 jumptocalledmove param0. +64 statusanimation battler. +65 status2animation battler. status2:: +66 chosenstatusanimation battler. param1. param2:: +67 yesnobox +68 cancelallactions +69 adjustsetdamage +6a removeitem battler. +6b atknameinbuff1 +6c drawlvlupbox +6d resetsentmonsvalue +6e setatktoplayer0 +6f makevisible battler. +70 recordlastability battler. +71 buffermovetolearn +72 jumpifplayerran ptr<> +73 hpthresholds battler. +74 hpthresholds2 battler. +75 useitemonopponent +76 various battler. param1. +77 setprotectlike +78 faintifabilitynotdamp +79 setatkhptozero +7a jumpifnexttargetvalid ptr<> +7b tryhealhalfhealth param0:: battler. +7c trymirrormove +7d setrain +7e setreflect +7f setseeded +80 manipulatedamage param0. +81 trysetrest param0:: +82 jumpifnotfirstturn ptr<> +83 nop +84 jumpifcantmakeasleep param0:: +85 stockpile +86 stockpiletobasedamage param0:: +87 stockpiletohpheal param0:: +88 negativedamage +89 statbuffchange param0. param1:: +8a normalisebuffs +8b setbide +8c confuseifrepeatingattackends +8d setmultihitcounter param0. +8e initmultihitstring +8f forcerandomswitch param0:: +90 tryconversiontypechange param0:: +91 givepaydaymoney +92 setlightscreen +93 tryKO param0:: +94 damagetohalftargethp +95 setsandstorm +96 weatherdamage +97 tryinfatuating param0:: +98 updatestatusicon battler. +99 setmist +9a setfocusenergy +9b transformdataexecution +9c setsubstitute +9d mimicattackcopy param0:: +9e metronome +9f dmgtolevel +a0 psywavedamageeffect +a1 counterdamagecalculator param0:: +a2 mirrorcoatdamagecalculator param0:: +a3 disablelastusedattack param0:: +a4 trysetencore param0:: +a5 painsplitdmgcalc param0:: +a6 settypetorandomresistance param0:: +a7 setalwayshitflag +a8 copymovepermanently param0:: +a9 trychoosesleeptalkmove param0:: +aa setdestinybond +ab trysetdestinybondtohappen +ac remaininghptopower +ad tryspiteppreduce param0:: +ae healpartystatus +af cursetarget param0:: +b0 trysetspikes param0:: +b1 setforesight +b2 trysetperishsong param0:: +b3 rolloutdamagecalculation +b4 jumpifconfusedandstatmaxed stat. ptr<> +b5 furycuttercalc +b6 happinesstodamagecalculation +b7 presentdamagecalculation +b8 setsafeguard +b9 magnitudedamagecalculation +ba jumpifnopursuitswitchdmg param0:: +bb setsunny +bc maxattackhalvehp param0:: +bd copyfoestats param0:: +be rapidspinfree +bf setdefensecurlbit +c0 recoverbasedonsunlight param0:: +c1 hiddenpowercalc +c2 selectfirstvalidtarget +c3 trysetfutureattack param0:: +c4 trydobeatup param0:: param1:: +c5 setsemiinvulnerablebit +c6 clearsemiinvulnerablebit +c7 setminimize +c8 sethail +c9 jumpifattackandspecialattackcannotfall ptr<> +ca setforcedtarget +cb setcharge +cc callterrainattack +cd cureifburnedparalysedorpoisoned param0:: +ce settorment param0:: +cf jumpifnodamage param0:: +d0 settaunt param0:: +d1 trysethelpinghand param0:: +d2 tryswapitems param0:: +d3 trycopyability param0:: +d4 trywish param0. param1:: +d5 trysetroots param0:: +d6 doubledamagedealtifdamaged +d7 setyawn param0:: +d8 setdamagetohealthdifference param0:: +d9 scaledamagebyhealthratio +da tryswapabilities param0:: +db tryimprison param0:: +dc trysetgrudge param0:: +dd weightdamagecalculation +de assistattackselect param0:: +df trysetmagiccoat param0:: +e0 trysetsnatch param0:: +e1 trygetintimidatetarget param0:: +e2 switchoutabilities battler. +e3 jumpifhasnohp battler. param1:: +e4 getsecretpowereffect +e5 pickup +e6 docastformchangeanimation +e7 trycastformdatachange +e8 settypebasedhalvers param0:: +e9 setweatherballtype +ea tryrecycleitem param0:: +eb settypetoterrain param0:: +ec pursuitrelated param0:: +ed snatchsetbattlers +ee removelightscreenreflect +ef handleballthrow +f0 givecaughtmon +f1 trysetcaughtmondexflags param0:: +f2 displaydexinfo +f3 trygivecaughtmonnick param0:: +f4 subattackerhpbydmg +f5 removeattackerstatus1 +f6 finishaction +f7 finishturn + +# various command changed to more readable macros +#XX cancelmultiturnmoves {battler} +# various {battler} 0 +#XX setmagiccoattarget {battler} +# various {battler} 1 +#XX getifcantrunfrombattle {battler} +# various {battler} 2 +#XX getmovetarget {battler} +# various {battler} 3 +#XX various4 {battler} +# various {battler} 4 +#XX resetintimidatetracebits {battler} +# various {battler} 5 +#XX updatechoicemoveonlvlup {battler} +# various {battler} 6 +#XX various7 {battler} +# various {battler} 7 +#XX various8 {battler} +# various {battler} 8 +#XX returnopponentmon1toball {battler} +# various {battler} 9 +#XX returnopponentmon2toball {battler} +# various {battler} 10 +#XX checkpokeflute {battler} +# various {battler} 11 +#XX waitfanfare {battler} +# various {battler} 12 + +# helpful macros +#XX setstatchanger {stat} {stages} {down} +# setbyte sSTATCHANGER {stat} | {stages} << 4 | {down} << 7 +#XX setmoveeffect {effect} +# setbyte cEFFECT_CHOOSER {effect} +#XX chosenstatus1animation {battler} {status} +# chosenstatusanimation {battler} 0x0 {status} +#XX chosenstatus2animation {battler} {status} +# chosenstatusanimation {battler} 0x1 {status} +#XX sethword {dst} {value} +# setbyte {dst} {value} & 0xFF +# setbyte {dst} + 1 ({value} >> 8) & 0xFF +#XX setword {dst} {value} +# setbyte {dst}, {value} & 0xFF +# setbyte {dst} + 1 ({value} >> 8) & 0xFF +# setbyte {dst} + 2 ({value} >> 16) & 0xFF +# setbyte {dst} + 3 ({value} >> 24) & 0xFF +#XX copybyte {dst} {src} +# copyarray {dst} {src} 0x1 +#XX copyhword {dst} {src} +# copyarray {dst} {src} 0x2 +#XX copyword {dst} {src} +# copyarray {dst} {src} 0x4 +#XX jumpifbytenotequal {byte1} {byte2} {jumpptr} +# jumpifarraynotequal {byte1} {byte2} 1 {jumpptr} +#XX jumpifbyteequal {byte1} {byte2} {jumpptr} +# jumpifarrayequal {byte1} {byte2} 1 {jumpptr} +#XX jumpifmove {move} {jumpptr} +# jumpifhalfword CMP_EQUAL gCurrentMove {move} {jumpptr} +#XX jumpifnotmove {move} {jumpptr} +# jumpifhalfword CMP_NOT_EQUAL gCurrentMove {move} {jumpptr} +#XX jumpifstatus3 {battler} {status} {jumpptr} +# jumpifstatus3condition {battler} {status} 0 {jumpptr} +#XX jumpifnostatus3 {battler} {status} {jumpptr} +# jumpifstatus3condition {battler} {status} 1 {jumpptr} +#XX jumpifmovehadnoeffect {jumpptr} +# jumpifbyte CMP_COMMON_BITS gMoveResultFlags MOVE_RESULT_NO_EFFECT {jumpptr} +#XX jumpifbattletype {flags} {jumpptr} +# jumpifword CMP_COMMON_BITS gBattleTypeFlags {flags} {jumpptr} +#XX jumpifnotbattletype {flags} {jumpptr} +# jumpifword CMP_NO_COMMON_BITS gBattleTypeFlags {flags} {jumpptr} diff --git a/src/HexManiac.Core/Models/Runs/XSERun.cs b/src/HexManiac.Core/Models/Runs/XSERun.cs index 7f4aff8f..e4d220a7 100644 --- a/src/HexManiac.Core/Models/Runs/XSERun.cs +++ b/src/HexManiac.Core/Models/Runs/XSERun.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; namespace HavenSoft.HexManiac.Core.Models.Runs { - public class XSERun : BaseRun { + public interface IScriptStartRun : IFormattedRun { } + + public class XSERun : BaseRun, IScriptStartRun { public static string SharedFormatString => "`xse`"; public override int Length => 1; @@ -15,4 +17,18 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { protected override BaseRun Clone(IReadOnlyList newPointerSources) => new XSERun(Start, newPointerSources); } + + public class BSERun : BaseRun, IScriptStartRun { + public static string SharedFormatString => "`bse`"; + + public override int Length => 1; + + public override string FormatString => SharedFormatString; + + public BSERun(int start, IReadOnlyList sources = null) : base(start, sources) { } + + public override IDataFormat CreateDataFormat(IDataModel data, int index) => None.Instance; + + protected override BaseRun Clone(IReadOnlyList newPointerSources) => new BSERun(Start, newPointerSources); + } } diff --git a/src/HexManiac.Core/Models/Singletons.cs b/src/HexManiac.Core/Models/Singletons.cs index 3b6a1d41..99ebba04 100644 --- a/src/HexManiac.Core/Models/Singletons.cs +++ b/src/HexManiac.Core/Models/Singletons.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -17,23 +18,29 @@ namespace HavenSoft.HexManiac.Core.Models { private const string TableReferenceFileName = "resources/tableReference.txt"; private const string ThumbReferenceFileName = "resources/armReference.txt"; private const string ScriptReferenceFileName = "resources/scriptReference.txt"; + private const string BattleScriptReferenceFileName = "resources/battleScriptReference.txt"; public IMetadataInfo MetadataInfo { get; } public IReadOnlyDictionary GameReferenceTables { get; } public IReadOnlyList ThumbConditionalCodes { get; } public IReadOnlyList ThumbInstructionTemplates { get; } public IReadOnlyList ScriptLines { get; } + public IReadOnlyList BattleScriptLines { get; } public Singletons() { GameReferenceTables = CreateGameReferenceTables(); (ThumbConditionalCodes, ThumbInstructionTemplates) = LoadThumbReference(); - ScriptLines = LoadScriptReference(); + ScriptLines = LoadScriptReference(ScriptReferenceFileName); + BattleScriptLines = LoadScriptReference(BattleScriptReferenceFileName); MetadataInfo = new MetadataInfo(); } - private IReadOnlyList LoadScriptReference() { - if (!File.Exists(ScriptReferenceFileName)) return new List(); - var lines = File.ReadAllLines(ScriptReferenceFileName); + private IReadOnlyList LoadScriptReference(string file) where TLine : ScriptLine { + if (!File.Exists(file)) return new List(); + Func factory = line => new XSEScriptLine(line); + if (typeof(TLine) == typeof(BSEScriptLine)) factory = line => new BSEScriptLine(line); + + var lines = File.ReadAllLines(file); var scriptLines = new List(); ScriptLine active = null; foreach (var line in lines) { @@ -43,7 +50,7 @@ namespace HavenSoft.HexManiac.Core.Models { if (line.Trim().StartsWith("#") && active != null) { active.AddDocumentation(line.Trim()); } else { - active = new ScriptLine(line); + active = factory(line); scriptLines.Add(active); } } @@ -120,6 +127,7 @@ namespace HavenSoft.HexManiac.Core.Models { var assembly = Assembly.GetExecutingAssembly(); var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); VersionNumber = $"{fvi.FileMajorPart}.{fvi.FileMinorPart}.{fvi.FileBuildPart}"; + if (fvi.FilePrivatePart != 0) VersionNumber += "." + fvi.FilePrivatePart; } } } diff --git a/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs b/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs index 7aa6eefb..4c96b454 100644 --- a/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs +++ b/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs @@ -2,6 +2,7 @@ using HavenSoft.HexManiac.Core.Models.Code; using HavenSoft.HexManiac.Core.Models.Runs; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; @@ -9,7 +10,7 @@ using System.Linq; using System.Text; namespace HavenSoft.HexManiac.Core.ViewModels.Tools { - public enum CodeMode { Thumb, Script, Raw } + public enum CodeMode { Thumb, Script, BattleScript, Raw } public class CodeTool : ViewModelCore, IToolViewModel { public string Name => "Code Tool"; @@ -17,7 +18,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { private string content; private CodeMode mode; private readonly ThumbParser thumb; - private readonly ScriptParser script; + private readonly ScriptParser script, battleScript; private readonly IDataModel model; private readonly Selection selection; private readonly ChangeHistory history; @@ -26,7 +27,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { public bool IsReadOnly => Mode == CodeMode.Raw; public bool UseSingleContent => !UseMultiContent; - public bool UseMultiContent => Mode == CodeMode.Script; + public bool UseMultiContent => Mode.IsAny(CodeMode.Script, CodeMode.BattleScript); private bool showErrorText; public bool ShowErrorText { get => showErrorText; private set => TryUpdate(ref showErrorText, value); } @@ -61,12 +62,16 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { public ScriptParser ScriptParser => script; + public ScriptParser BattleScriptParser => battleScript; + public event EventHandler<(int originalLocation, int newLocation)> ModelDataMoved; public CodeTool(Singletons singletons, IDataModel model, Selection selection, ChangeHistory history) { thumb = new ThumbParser(singletons); script = new ScriptParser(singletons.ScriptLines); + battleScript = new ScriptParser(singletons.BattleScriptLines); script.CompileError += ObserveCompileError; + battleScript.CompileError += ObserveCompileError; this.model = model; this.selection = selection; this.history = history; @@ -92,9 +97,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { Content = RawParse(model, start, end - start + 1); } else if (length < 2 && mode != CodeMode.Script) { TryUpdate(ref content, string.Empty, nameof(Content)); - UpdateContents(-1); + UpdateContents(-1, null); } else if (mode == CodeMode.Script) { - UpdateContents(start); + UpdateContents(start, script); + } else if (mode == CodeMode.BattleScript) { + UpdateContents(start, battleScript); } else if (mode == CodeMode.Thumb) { TryUpdate(ref content, thumb.Parse(model, start, end - start + 1), nameof(Content)); } else { @@ -109,14 +116,14 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { /// /// /// - private void UpdateContents(int start, int currentScriptStart = -1) { - var scripts = script.CollectScripts(model, start); + private void UpdateContents(int start, ScriptParser parser, int currentScriptStart = -1) { + var scripts = parser?.CollectScripts(model, start) ?? new List(); for (int i = 0; i < scripts.Count; i++) { var scriptStart = scripts[i]; if (scriptStart == currentScriptStart && Contents.Count > i && Contents[i].Address == scriptStart) continue; - var scriptLength = script.FindLength(model, scriptStart); + var scriptLength = parser.FindLength(model, scriptStart); var label = scriptStart.ToString("X6"); - var content = script.Parse(model, scriptStart, scriptLength); + var content = parser.Parse(model, scriptStart, scriptLength); var body = new CodeBody { Address = scriptStart, Label = label, Content = content }; if (Contents.Count > i) { @@ -141,15 +148,16 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { } private void ScriptChanged(object viewModel, EventArgs e) { + var parser = mode == CodeMode.Script ? script : battleScript; var body = (CodeBody)viewModel; var codeContent = body.Content; var run = model.GetNextRun(body.Address) as XSERun; if (run == null || run.Start != body.Address) Debug.Fail("How did this happen?"); - int length = script.FindLength(model, run.Start); + int length = parser.FindLength(model, run.Start); using (ModelCacheScope.CreateScope(model)) { - CompileScriptChanges(run, length, ref codeContent, body == Contents[0]); + CompileScriptChanges(run, length, ref codeContent, parser, body == Contents[0]); body.ContentChanged -= ScriptChanged; body.HelpSourceChanged -= UpdateScriptHelpFromLine; @@ -161,7 +169,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { var start = Math.Min(model.Count - 1, selection.Scroll.ViewPointToDataIndex(selection.SelectionStart)); var end = Math.Min(model.Count - 1, selection.Scroll.ViewPointToDataIndex(selection.SelectionEnd)); if (start > end) (start, end) = (end, start); - UpdateContents(start, body.Address); + UpdateContents(start, parser, body.Address); } } @@ -193,29 +201,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { ModelDataChanged?.Invoke(this, ErrorInfo.NoError); } - private void CompileScriptChanges() { - var start = Math.Min(model.Count - 1, selection.Scroll.ViewPointToDataIndex(selection.SelectionStart)); - var end = Math.Min(model.Count - 1, selection.Scroll.ViewPointToDataIndex(selection.SelectionEnd)); - if (start > end) (start, end) = (end, start); - - var run = model.GetNextRun(start) as XSERun; - if (run == null || run.Start != start) return; - int length = end - start + 1; - - string codeContent = Content; - CompileScriptChanges(run, length, ref codeContent, true); - TryUpdate(ref content, codeContent, nameof(Content)); - } - - private void CompileScriptChanges(XSERun run, int length, ref string codeContent, bool updateSelection) { + private void CompileScriptChanges(XSERun run, int length, ref string codeContent, ScriptParser parser, bool updateSelection) { ShowErrorText = false; ErrorText = string.Empty; int start = run.Start; ignoreContentUpdates = true; { - var oldScripts = script.CollectScripts(model, run.Start); - var code = script.Compile(history.CurrentChange, model, ref codeContent, out var movedData); + var oldScripts = parser.CollectScripts(model, run.Start); + var code = parser.Compile(history.CurrentChange, model, ref codeContent, out var movedData); if (code == null) { ignoreContentUpdates = false; return; @@ -229,15 +223,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { model.ClearAnchor(history.CurrentChange, start, length); for (int i = 0; i < code.Length; i++) history.CurrentChange.ChangeData(model, run.Start + i, code[i]); for (int i = code.Length; i < length; i++) history.CurrentChange.ChangeData(model, run.Start + i, 0xFF); - script.FormatScript(history.CurrentChange, model, run.Start, run.PointerSources); + parser.FormatScript(history.CurrentChange, model, run.Start, run.PointerSources); foreach (var source in run.PointerSources) model.ObserveRunWritten(history.CurrentChange, new PointerRun(source)); // this change may have orphaned some existing scripts. Don't lose them! - var newScripts = script.CollectScripts(model, run.Start); + var newScripts = parser.CollectScripts(model, run.Start); foreach (var orphan in oldScripts.Except(newScripts)) { var orphanRun = model.GetNextRun(orphan); if (orphanRun.Start == orphan && string.IsNullOrEmpty(model.GetAnchorFromAddress(-1, orphan))) { - script.FormatScript(history.CurrentChange, model, orphan); + parser.FormatScript(history.CurrentChange, model, orphan); model.ObserveAnchorWritten(history.CurrentChange, $"xse{orphan:X6}", new XSERun(orphan)); } } diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs index 362da683..84e2c53d 100644 --- a/src/HexManiac.Core/ViewModels/ViewPort.cs +++ b/src/HexManiac.Core/ViewModels/ViewPort.cs @@ -640,7 +640,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels { var address = Model.GetAddressFromAnchor(noChange, -1, anchor); var run = Model.GetNextRun(address); if (run is XSERun) { - tools.CodeTool.ScriptParser.FormatScript(noChange, Model, address); + tools.CodeTool.ScriptParser.FormatScript(noChange, Model, address); } } } @@ -1403,7 +1403,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels { Model.ClearFormat(CurrentChange, address, length - 1); using (ModelCacheScope.CreateScope(Model)) { - tools.CodeTool.ScriptParser.FormatScript(CurrentChange, Model, address); + tools.CodeTool.ScriptParser.FormatScript(CurrentChange, Model, address); } SelectionStart = scroll.DataIndexToViewPoint(address); diff --git a/src/HexManiac.WPF/Controls/TabView.xaml.cs b/src/HexManiac.WPF/Controls/TabView.xaml.cs index 25d7e1f1..a4f7ce96 100644 --- a/src/HexManiac.WPF/Controls/TabView.xaml.cs +++ b/src/HexManiac.WPF/Controls/TabView.xaml.cs @@ -6,7 +6,6 @@ using HavenSoft.HexManiac.Core.ViewModels.Tools; using HavenSoft.HexManiac.WPF.Implementations; using System; using System.ComponentModel; -using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -45,7 +44,7 @@ namespace HavenSoft.HexManiac.WPF.Controls { public IFileSystem FileSystem => (IFileSystem)Application.Current.MainWindow.Resources["FileSystem"]; public TabView() { InitializeComponent(); - CodeModeSelector.ItemsSource = Enum.GetValues(typeof(CodeMode)).Cast(); + CodeModeSelector.ItemsSource = new[] { CodeMode.Thumb, CodeMode.Script, CodeMode.Raw }; // Enum.GetValues(typeof(CodeMode)).Cast().ToList(); timer = new DispatcherTimer(TimeSpan.FromSeconds(.6), DispatcherPriority.ApplicationIdle, BlinkCursor, Dispatcher); timer.Stop(); }