From 7921ab8338c583602680a7fcb2517a3dd1fef563 Mon Sep 17 00:00:00 2001 From: fl4tisjustice Date: Fri, 5 Sep 2025 12:44:30 +0100 Subject: [PATCH 1/3] Decouple data directives from vm instructions (WIP) --- mgs-spec-bank/data.yaml | 15 ++- mgs-spec-bank/datadirectives/base.yaml | 19 +++ src/MagesScriptTool/DataDirective.cs | 13 ++ src/MagesScriptTool/DataDirectiveEncoding.cs | 112 ++++++++++++++++++ src/MagesScriptTool/DataDirectiveSpec.cs | 13 ++ src/MagesScriptTool/DataDirectivesSpec.cs | 20 ++++ src/MagesScriptTool/ExpressionNodeString.cs | 4 +- src/MagesScriptTool/Program.cs | 13 +- src/MagesScriptTool/ScriptCompiler.cs | 49 ++------ src/MagesScriptTool/ScriptDecompiler.cs | 100 +++++++--------- src/MagesScriptTool/SpecBank.cs | 64 ++++++++++ .../UncompiledScriptElementDataDirective.cs | 9 ++ src/MagesScriptTool/UncompiledScriptSyntax.cs | 36 +++++- 13 files changed, 355 insertions(+), 112 deletions(-) create mode 100644 mgs-spec-bank/datadirectives/base.yaml create mode 100644 src/MagesScriptTool/DataDirective.cs create mode 100644 src/MagesScriptTool/DataDirectiveEncoding.cs create mode 100644 src/MagesScriptTool/DataDirectiveSpec.cs create mode 100644 src/MagesScriptTool/DataDirectivesSpec.cs create mode 100644 src/MagesScriptTool/UncompiledScriptElementDataDirective.cs diff --git a/mgs-spec-bank/data.yaml b/mgs-spec-bank/data.yaml index ef10c87..052ca7a 100644 --- a/mgs-spec-bank/data.yaml +++ b/mgs-spec-bank/data.yaml @@ -22,6 +22,9 @@ stringtags: - stringtags/base.yaml - stringtags/famicom_tantei_club.yaml +datadirectives: +- datadirectives/base.yaml + charset: chaos_head_lcc-extended: charset/chaos_head_lcc-extended.json chaos_head_noah: charset/chaos_head_noah.json @@ -68,6 +71,7 @@ flags: nonameid: yes multisystemmes: no option_default_arg: no + stringid_16: no memories_off_historia: new_bgmstop: yes @@ -101,6 +105,7 @@ flags: movie_01_27: yes return_labels: yes multisystemmes: no + stringid_16: no chaos_head_lcc_ps3: game_chlcc: yes @@ -135,6 +140,7 @@ flags: return_labels: no multisystemmes: no option_default_arg: yes + stringid_16: yes chaos_head_switch: game_chn: yes @@ -170,6 +176,7 @@ flags: return_labels: yes multisystemmes: no option_default_arg: no + stringid_16: no chaos_head_windows: game_chn: yes @@ -205,6 +212,7 @@ flags: return_labels: yes multisystemmes: no option_default_arg: no + stringid_16: no chaos_child_lcc_ps4: cc_common: yes @@ -243,9 +251,11 @@ flags: return_labels: yes multisystemmes: yes option_default_arg: no + stringid_16: yes famicom_tantei_club: game_ftc: yes + stringid_16: no iwakura_aria_switch: game_ia: yes @@ -275,6 +285,7 @@ flags: return_labels: yes multisystemmes: no option_default_arg: no + stringid_16: no iwakura_aria_windows: game_ia: yes @@ -307,6 +318,7 @@ flags: multilang: yes multisystemmes: no option_default_arg: no + stringid_16: no chaos_child_switch: cc_common: yes @@ -341,4 +353,5 @@ flags: movie_01_27: yes return_labels: yes multisystemmes: yes - option_default_arg: no \ No newline at end of file + option_default_arg: no + stringid_16: no \ No newline at end of file diff --git a/mgs-spec-bank/datadirectives/base.yaml b/mgs-spec-bank/datadirectives/base.yaml new file mode 100644 index 0000000..38b4418 --- /dev/null +++ b/mgs-spec-bank/datadirectives/base.yaml @@ -0,0 +1,19 @@ +- name: "StringID" + operands: [int32] + flags: [~stringid_16] + +- name: "StringID" + operands: [int16] + flags: [stringid_16] + +- name: "dw" + operands: [int16] + flags: [] + +- name: "Adr" + operands: [int16] + flags: [] + +- name: "dd" + operands: [int32] + flags: [] \ No newline at end of file diff --git a/src/MagesScriptTool/DataDirective.cs b/src/MagesScriptTool/DataDirective.cs new file mode 100644 index 0000000..b32cdff --- /dev/null +++ b/src/MagesScriptTool/DataDirective.cs @@ -0,0 +1,13 @@ +using System.Collections.Immutable; + +namespace MagesScriptTool; + +sealed class DataDirective { + public readonly string Name; + public readonly ImmutableArray Operands; + + public DataDirective(string name, ImmutableArray operands) { + Name = name; + Operands = operands; + } +} diff --git a/src/MagesScriptTool/DataDirectiveEncoding.cs b/src/MagesScriptTool/DataDirectiveEncoding.cs new file mode 100644 index 0000000..56fa890 --- /dev/null +++ b/src/MagesScriptTool/DataDirectiveEncoding.cs @@ -0,0 +1,112 @@ +using System.Collections.Immutable; +using System.Diagnostics; + +namespace MagesScriptTool; + +sealed class DataDirectiveEncoding { + readonly DataDirectivesSpec _spec; + + public DataDirectiveEncoding(DataDirectivesSpec spec) { + _spec = spec; + } + + public void Encode(Stream stream, DataDirective dataDirective) { + DataDirectiveSpec? spec = _spec.GetSpec(dataDirective.Name); + if (spec is null) { + throw new Exception($"Unrecognized data directive name: {dataDirective.Name}."); + } + ImmutableArray kinds = spec.Operands; + ImmutableArray operands = dataDirective.Operands; + if (operands.Length != kinds.Length) { + throw new Exception($"Expected {kinds.Length} arguments, got {operands.Length}."); + } + for (int i = 0; i < kinds.Length; i++) { + EncodeOperand(stream, kinds[i], operands[i]); + } + } + + public DataDirective Decode(Stream stream, string name) { + DataDirectiveSpec? spec = _spec.GetSpec(name); + Debug.Assert(spec is not null); + + ImmutableArray operandSpecs = spec.Operands; + List operands = []; + foreach (OperandKind operandSpec in operandSpecs) { + operands.Add(DecodeOperand(stream, operandSpec)); + } + return new(spec.Name, [..operands]); + } + + static void EncodeOperand(Stream stream, OperandKind kind, ExpressionNode operand) { + switch (kind) { + case OperandKind.Int16: { + EncodeOperandInt16(stream, operand); + break; + } + case OperandKind.Int32: { + EncodeOperandInt32(stream, operand); + break; + } + default: { + throw new NotImplementedException(kind.ToString()); + } + }; + } + + static void EncodeOperandInt16(Stream stream, ExpressionNode expression) { + int value = expression.GetInt(); + PutByte(stream, (byte)(value >> 0)); + PutByte(stream, (byte)(value >> 8)); + } + + static void EncodeOperandInt32(Stream stream, ExpressionNode expression) { + int value = expression.GetInt(); + PutByte(stream, (byte)(value >> 0)); + PutByte(stream, (byte)(value >> 8)); + PutByte(stream, (byte)(value >> 16)); + PutByte(stream, (byte)(value >> 24)); + } + + static ExpressionNode DecodeOperand(Stream stream, OperandKind kind) { + return kind switch { + OperandKind.Int16 => DecodeOperandInt16(stream), + OperandKind.Int32 => DecodeOperandInt32(stream), + _ => throw new NotImplementedException(kind.ToString()), + }; + } + + static ExpressionNodeNumber DecodeOperandInt16(Stream stream) { + int value = 0; + value |= GetByte(stream) << 0; + value |= GetByte(stream) << 8; + value = SignExtend(value, 16); + return new(value); + } + + static ExpressionNodeNumber DecodeOperandInt32(Stream stream) { + int value = 0; + value |= GetByte(stream) << 0; + value |= GetByte(stream) << 8; + value |= GetByte(stream) << 16; + value |= GetByte(stream) << 24; + value = SignExtend(value, 32); + return new(value); + } + + static byte GetByte(Stream stream) { + int value = stream.ReadByte(); + if (value < 0) { + throw new EndOfStreamException(); + } + return (byte)value; + } + + static int SignExtend(int value, int length) { + int mask = 1 << (length - 1); + int sign = value & mask; + return value | ~(sign - 1); + } + static void PutByte(Stream stream, byte value) { + stream.WriteByte(value); + } +} \ No newline at end of file diff --git a/src/MagesScriptTool/DataDirectiveSpec.cs b/src/MagesScriptTool/DataDirectiveSpec.cs new file mode 100644 index 0000000..edad419 --- /dev/null +++ b/src/MagesScriptTool/DataDirectiveSpec.cs @@ -0,0 +1,13 @@ +using System.Collections.Immutable; + +namespace MagesScriptTool; + +sealed class DataDirectiveSpec { + public readonly string Name; + public readonly ImmutableArray Operands; + + public DataDirectiveSpec(string name, ImmutableArray operands) { + Name = name; + Operands = operands; + } +} diff --git a/src/MagesScriptTool/DataDirectivesSpec.cs b/src/MagesScriptTool/DataDirectivesSpec.cs new file mode 100644 index 0000000..5a13c4e --- /dev/null +++ b/src/MagesScriptTool/DataDirectivesSpec.cs @@ -0,0 +1,20 @@ +using System.Collections.Immutable; + +namespace MagesScriptTool; + +sealed class DataDirectivesSpec { + readonly Dictionary _byName = []; + + public DataDirectivesSpec(ImmutableArray specs) { + foreach (DataDirectiveSpec spec in specs) { + if (_byName.ContainsKey(spec.Name)) { + throw new Exception($"Duplicate string tag name: {spec.Name}."); + } + _byName[spec.Name] = spec; + } + } + + public DataDirectiveSpec? GetSpec(string name) { + return _byName.GetValueOrDefault(name); + } +} diff --git a/src/MagesScriptTool/ExpressionNodeString.cs b/src/MagesScriptTool/ExpressionNodeString.cs index a5e9cc7..b964d13 100644 --- a/src/MagesScriptTool/ExpressionNodeString.cs +++ b/src/MagesScriptTool/ExpressionNodeString.cs @@ -7,7 +7,7 @@ public ExpressionNodeString(string value) { Value = value; } - public override string GetString() { + public override string GetString() { return Value; - } + } } diff --git a/src/MagesScriptTool/Program.cs b/src/MagesScriptTool/Program.cs index 9c3434c..29ef8dc 100644 --- a/src/MagesScriptTool/Program.cs +++ b/src/MagesScriptTool/Program.cs @@ -78,6 +78,7 @@ sealed class Tool { public readonly bool GenerateSdb; public readonly InstructionEncoding InstructionEncoding; + public readonly DataDirectiveEncoding DataDirectiveEncoding; public readonly UncompiledStringTableSyntax UncompiledStringTableSyntax; public readonly StringGlyphSyntax StringGlyphSyntax; public readonly CompiledScriptPackageEncoding CompiledScriptPackageEncoding; @@ -101,7 +102,7 @@ public Tool(ParseResult result) { ImmutableArray instructionSpecs = bank.GetInstructionSpecs(flags); InstructionEncoding = InstructionEncoding.BuildFrom(instructionSpecs); - UncompiledStringSyntax uncompiledStringSyntax = new(); + UncompiledStringSyntax uncompiledStringSyntax = new(); UncompiledStringTableSyntax = new(uncompiledStringSyntax); ImmutableArray glyphSpecs = bank.GetGlyphSpecs(charsetName); @@ -109,6 +110,12 @@ public Tool(ParseResult result) { ImmutableArray stringTagSpecs = bank.GetStringTagSpecs(flags); StringTagsSpec stringTagsSpec = new(stringTagSpecs); + + ImmutableArray dataDirectiveSpecs = bank.GetDataDirectiveSpecs(flags); + DataDirectivesSpec dataDirectivesSpec = new(dataDirectiveSpecs); + + DataDirectiveEncoding = new(dataDirectivesSpec); + CompiledStringEncoding compiledStringEncoding = new(stringTagsSpec); CompiledScriptPackageEncoding = new(compiledStringEncoding); CompiledStringTableEncoding = new(compiledStringEncoding); @@ -227,7 +234,7 @@ static async Task CompileScriptPackage(Tool tool, string uncompiledScriptN ImmutableArray uncompiledScriptElements = await ParseUncompiledScript(tool, uncompiledScriptPath); ImmutableArray uncompiledStringTableEntries = await ParseUncompiledStringTable(tool, uncompiledStringTablePath); - ScriptCompiler compiler = new(tool.InstructionEncoding); + ScriptCompiler compiler = new(tool.InstructionEncoding, tool.DataDirectiveEncoding); CompiledScript compiledScript = compiler.Compile(uncompiledScriptElements); List> compiledStrings = []; @@ -291,7 +298,7 @@ static async Task DecompileScriptPackage(Tool tool, string compiledScriptP try { CompiledScriptPackage compiledScriptPackage = await DecodeCompiledScriptPackage(tool, compiledScriptPackagePath); - ScriptDecompiler decompiler = new(tool.InstructionEncoding, compiledScriptPackage.Script); + ScriptDecompiler decompiler = new(tool.InstructionEncoding, tool.DataDirectiveEncoding, compiledScriptPackage.Script); (ImmutableArray uncompiledScriptElements, ImmutableDictionary instructionPositions) = decompiler.Decompile(); ImmutableArray> compiledStrings = compiledScriptPackage.Strings; diff --git a/src/MagesScriptTool/ScriptCompiler.cs b/src/MagesScriptTool/ScriptCompiler.cs index 7bd193b..5f95030 100644 --- a/src/MagesScriptTool/ScriptCompiler.cs +++ b/src/MagesScriptTool/ScriptCompiler.cs @@ -4,13 +4,14 @@ namespace MagesScriptTool; sealed class ScriptCompiler { readonly InstructionEncoding _instructionEncoding; - + readonly DataDirectiveEncoding _dataDirectiveEncoding; readonly MemoryStream _stream = new(); readonly SortedDictionary _labelTable = []; readonly SortedDictionary _returnLabelTable = []; - public ScriptCompiler(InstructionEncoding instructionEncoding) { + public ScriptCompiler(InstructionEncoding instructionEncoding, DataDirectiveEncoding dataDirectiveEncoding) { _instructionEncoding = instructionEncoding; + _dataDirectiveEncoding = dataDirectiveEncoding; } public CompiledScript Compile(ImmutableArray elements) { @@ -40,7 +41,11 @@ public CompiledScript Compile(ImmutableArray elements) void ProcessElement(UncompiledScriptElement element) { switch (element) { case UncompiledScriptElementInstruction { Value: Instruction instruction }: { - ProcessInstruction(instruction); + _instructionEncoding.Encode(_stream, instruction); + break; + } + case UncompiledScriptElementDataDirective { Value: DataDirective dataDirective }: { + _dataDirectiveEncoding.Encode(_stream, dataDirective); break; } case UncompiledScriptElementLabel { Index: int index }: { @@ -69,42 +74,4 @@ void ProcessElement(UncompiledScriptElement element) { } } - void ProcessInstruction(Instruction instruction) { - switch (instruction.Name.ToLowerInvariant()) { - case "dw" or "adr": { - foreach (ExpressionNode operand in instruction.Operands) { - EncodeInt16(operand); - } - break; - } - case "dd" or "stringid": { - foreach (ExpressionNode operand in instruction.Operands) { - EncodeInt32(operand); - } - break; - } - default: { - _instructionEncoding.Encode(_stream, instruction); - break; - } - } - } - - void EncodeInt16(ExpressionNode expression) { - int value = expression.GetInt(); - PutByte((byte)((value >> 00) & 0xFF)); - PutByte((byte)((value >> 08) & 0xFF)); - } - - void EncodeInt32(ExpressionNode expression) { - int value = expression.GetInt(); - PutByte((byte)((value >> 00) & 0xFF)); - PutByte((byte)((value >> 08) & 0xFF)); - PutByte((byte)((value >> 16) & 0xFF)); - PutByte((byte)((value >> 24) & 0xFF)); - } - - void PutByte(byte value) { - _stream.WriteByte(value); - } } diff --git a/src/MagesScriptTool/ScriptDecompiler.cs b/src/MagesScriptTool/ScriptDecompiler.cs index 4919166..9ae0a99 100644 --- a/src/MagesScriptTool/ScriptDecompiler.cs +++ b/src/MagesScriptTool/ScriptDecompiler.cs @@ -4,6 +4,7 @@ namespace MagesScriptTool; sealed class ScriptDecompiler { readonly InstructionEncoding _instructionEncoding; + readonly DataDirectiveEncoding _dataDirectiveEncoding; readonly ImmutableArray _code; readonly ImmutableArray _labels; @@ -14,8 +15,9 @@ sealed class ScriptDecompiler { readonly Dictionary _instructionPositions = []; - public ScriptDecompiler(InstructionEncoding instructionEncoding, CompiledScript compiledScript) { + public ScriptDecompiler(InstructionEncoding instructionEncoding, DataDirectiveEncoding dataDirectiveEncoding, CompiledScript compiledScript) { _instructionEncoding = instructionEncoding; + _dataDirectiveEncoding = dataDirectiveEncoding; _code = compiledScript.Code; _labels = compiledScript.Labels; @@ -142,6 +144,7 @@ void SetLabelKind(int label, ChunkKind kind) { sealed class Chunk : Stream { readonly ScriptDecompiler _parent; readonly InstructionEncoding _instructionEncoding; + readonly DataDirectiveEncoding _dataDirectiveEncoding; readonly ImmutableArray _code; readonly public int _index; @@ -171,6 +174,7 @@ public override long Position { public Chunk(ScriptDecompiler parent, int index, int start, int end) { _parent = parent; _instructionEncoding = parent._instructionEncoding; + _dataDirectiveEncoding = parent._dataDirectiveEncoding; _code = parent._code; _index = index; @@ -257,39 +261,39 @@ void Decode() { void DecodeInt16Table() { while (Position < Length) { - AddInstruction(new("dw", [DecodeInt16()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "dw")); } } void DecodeInt32Table() { while (Position < Length) { - AddInstruction(new("dd", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "dd")); } } void DecodeAdrTable() { while (Position < Length) { - AddInstruction(new("Adr", [DecodeInt16()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "Adr")); } } void DecodeTextTable() { while (Position < Length) { - AddInstruction(new("StringID", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); } } void DecodeNameIdTable() { SetIncomplete(); while (true) { - ExpressionNode id = DecodeInt16(); - AddInstruction(new("dw", [id])); - if (id.GetInt() == 0xFFFF) { + DataDirective id = _dataDirectiveEncoding.Decode(this, "dw"); + AddDataDirective(id); + if ((id.Operands[0].GetInt() & 0xFFFF) == 0xFFFF) { break; } - AddInstruction(new("StringID", [DecodeInt32()])); - AddInstruction(new("StringID", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); } SetComplete(); } @@ -298,9 +302,9 @@ void DecodeEncycDataTable() { SetIncomplete(); int index = 0; while (true) { - ExpressionNode value0 = DecodeInt16(); - AddInstruction(new("dw", [value0])); - if (value0.GetInt() == 0xFF) { + DataDirective pageCount = _dataDirectiveEncoding.Decode(this, "dw"); + AddDataDirective(pageCount); + if ((pageCount.Operands[0].GetInt() & 0xFFFF) == 0xFF) { break; } @@ -308,19 +312,21 @@ void DecodeEncycDataTable() { index++; // Category - AddInstruction(new("StringID", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); // Name - AddInstruction(new("StringID", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); // Pronounciation - AddInstruction(new("StringID", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); // Sorting key - AddInstruction(new("StringID", [DecodeInt32()])); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); // Content - AddInstruction(new("StringID", [DecodeInt32()])); + for (int i = 0; i < pageCount.Operands[0].GetInt(); i++) { + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + } } SetComplete(); } @@ -328,22 +334,16 @@ void DecodeEncycDataTable() { void DecodeEncycSortTable() { SetIncomplete(); - AddInstruction(new("StringID", [DecodeInt32()])); - AddInstruction(new("StringID", [DecodeInt32()])); - - while (true) { - ExpressionNode value0 = DecodeInt16(); - AddInstruction(new("dw", [value0])); - if (value0.GetInt() == 0xFFFF) { - break; - } - } + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); - while (true) { - ExpressionNode value0 = DecodeInt16(); - AddInstruction(new("dw", [value0])); - if (value0.GetInt() == 0xFFFF) { - break; + for (int i = 0; i < 2; i++) { + while (true) { + DataDirective value0 = _dataDirectiveEncoding.Decode(this, "dw"); + AddDataDirective(value0); + if ((value0.Operands[0].GetInt() & 0xFFFF) == 0xFFFF) { + break; + } } } @@ -353,7 +353,7 @@ void DecodeEncycSortTable() { void DecodeMesModeFormatTable() { int index = 0; while (Position < Length) { - ExpressionNode value0 = DecodeInt16(); + DataDirective value0 = _dataDirectiveEncoding.Decode(this, "dw"); string? comment = index switch { 0 => "display mode", 1 => "message window ID", @@ -380,35 +380,11 @@ void DecodeMesModeFormatTable() { if (comment is not null) { AddComment(comment); } - AddInstruction(new("dw", [value0])); + AddDataDirective(value0); index++; } } - ExpressionNodeNumber DecodeInt16() { - int value = 0; - value |= GetByte() << 0; - value |= GetByte() << 8; - return new(value); - } - - ExpressionNodeNumber DecodeInt32() { - int value = 0; - value |= GetByte() << 0; - value |= GetByte() << 8; - value |= GetByte() << 16; - value |= GetByte() << 24; - return new(value); - } - - byte GetByte() { - int value = ReadByte(); - if (value < 0) { - throw new EndOfStreamException(); - } - return (byte)value; - } - void Reset() { Error = null; Body.Clear(); @@ -429,6 +405,12 @@ public void AddInstruction(Instruction instruction) { UpdateLastPosition(); } + public void AddDataDirective(DataDirective dataDirective) { + UncompiledScriptElementDataDirective element = new(dataDirective); + Body.Add(element); + UpdateLastPosition(); + } + public void UpdateLastPosition() { LastPosition = _position; } diff --git a/src/MagesScriptTool/SpecBank.cs b/src/MagesScriptTool/SpecBank.cs index 327d5c9..e56b1ed 100644 --- a/src/MagesScriptTool/SpecBank.cs +++ b/src/MagesScriptTool/SpecBank.cs @@ -112,6 +112,20 @@ static string toText(JsonElement json) { } } + public ImmutableArray GetDataDirectiveSpecs(ImmutableDictionary flags) { + List result = []; + foreach (string path in _index.DataDirectives) { + SerializedDataDirectiveSpec[] specs = SerializedDataDirectiveSpec.LoadList(Path.Join(_path, path)); + foreach (SerializedDataDirectiveSpec spec in specs) { + if (!spec.CheckFlags(flags)) { + continue; + } + result.Add(new(spec.Name, spec.ParseOperands())); + } + } + return [..result]; + } + public ImmutableDictionary GetFlags(string key) { return _index.Flags[key].ToImmutableDictionary(); } @@ -128,6 +142,9 @@ sealed class SpecBankIndex { [YamlMember(Alias = "stringtags", ApplyNamingConventions = false)] public string[] StringTags { get; set; } = default!; + [YamlMember(Alias = "datadirectives", ApplyNamingConventions = false)] + public string[] DataDirectives { get; set; } = default!; + [YamlMember(Alias = "macros", ApplyNamingConventions = false)] public string[] Macros { get; set; } = default!; @@ -256,4 +273,51 @@ public static SerializedStringTagSpec[] LoadList(string path) { return deserializer.Deserialize(text); } } + + sealed class SerializedDataDirectiveSpec { + [YamlMember(Alias = "name", ApplyNamingConventions = false)] + public string Name { get; set; } = default!; + + [YamlMember(Alias = "operands", ApplyNamingConventions = false)] + public string[] Operands { get; set; } = default!; + + [YamlMember(Alias = "flags", ApplyNamingConventions = false)] + public string[] Flags { get; set; } = default!; + + public ImmutableArray ParseOperands() { + List operands = []; + for (int i = 0; i < Operands.Length; i++) { + operands.Add(Operands[i] switch { + "expr" => OperandKind.Expr, + "uint8" => OperandKind.UInt8, + "int8" => OperandKind.Int8, + "int16" => OperandKind.Int16, + "int32" => OperandKind.Int32, + "str" => OperandKind.Str, + _ => throw new Exception($"Unrecognized operand kind name: {Operands[i]}."), + }); + } + return [..operands]; + } + public bool CheckFlags(ImmutableDictionary flags) { + foreach (string flag in Flags) { + string name; + bool value; + if (flag.StartsWith('~')) { + (name, value) = (flag[1..], false); + } else { + (name, value) = (flag, true); + } + if (!flags.ContainsKey(name) || flags[name] != value) { + return false; + } + } + return true; + } + public static SerializedDataDirectiveSpec[] LoadList(string path) { + string text = File.ReadAllText(path, new UTF8Encoding(false, true)); + IDeserializer deserializer = new DeserializerBuilder().Build(); + return deserializer.Deserialize(text); + } + } } diff --git a/src/MagesScriptTool/UncompiledScriptElementDataDirective.cs b/src/MagesScriptTool/UncompiledScriptElementDataDirective.cs new file mode 100644 index 0000000..6044cb4 --- /dev/null +++ b/src/MagesScriptTool/UncompiledScriptElementDataDirective.cs @@ -0,0 +1,9 @@ +namespace MagesScriptTool; + +sealed class UncompiledScriptElementDataDirective : UncompiledScriptElement { + public readonly DataDirective Value; + + public UncompiledScriptElementDataDirective(DataDirective value) { + Value = value; + } +} diff --git a/src/MagesScriptTool/UncompiledScriptSyntax.cs b/src/MagesScriptTool/UncompiledScriptSyntax.cs index c8a2ef4..edcf3b3 100644 --- a/src/MagesScriptTool/UncompiledScriptSyntax.cs +++ b/src/MagesScriptTool/UncompiledScriptSyntax.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Data.Common; using System.Diagnostics; using System.Text; @@ -45,6 +46,11 @@ void FormatElement(UncompiledScriptElement genericElement) { Append($"\t{s}\n"); break; } + case UncompiledScriptElementDataDirective { Value: DataDirective dataDirective }: { + string s = FormatDataDirective(dataDirective); + Append($"\t{s}\n"); + break; + } case UncompiledScriptElementLabel { Index: int index }: { Append($"{index}"); Append(":"); @@ -111,6 +117,19 @@ string FormatInstruction(Instruction instruction) { return sb.ToString(); } + string FormatDataDirective(DataDirective dataDirective) { + StringBuilder sb = new(); + sb.Append(dataDirective.Name); + for (int i = 0; i < dataDirective.Operands.Length; i++) { + if (i != 0) { + sb.Append(","); + } + sb.Append(" "); + ExpressionSyntax.Format(sb, dataDirective.Operands[i]); + } + return sb.ToString(); + } + void Append(string s) { foreach (char c in s) { if (c == '\n') { @@ -140,11 +159,16 @@ public ImmutableArray Parse() { UncompiledScriptElement ParseElement() { char ch = _reader.Peek(0); - if (IsInstructionNameStart(ch)) { + if (IsIdentifierStart(ch)) { string name = ParseInstructionName(); switch (name) { case "hex": { return new UncompiledScriptElementRaw(ParseRaw()); + } case "StringID" or "Adr" or "dw" or "dd": { + ImmutableArray operands = ParseOperands(); + DataDirective dataDirective = new(name, operands); + return new UncompiledScriptElementDataDirective(dataDirective); + break; } default: { ImmutableArray operands = ParseOperands(); @@ -231,9 +255,9 @@ UncompiledScriptElementInstruction ParseEvalInstruction() { } string ParseInstructionName() { - Debug.Assert(IsInstructionNameStart(_reader.Peek(0))); + Debug.Assert(IsIdentifierStart(_reader.Peek(0))); string s = ""; - while (IsInstructionNamePart(_reader.Peek(0))) { + while (IsIdentifierPart(_reader.Peek(0))) { s += _reader.Next(); } return s; @@ -264,12 +288,12 @@ int ParseNibble() { }; } - static bool IsInstructionNameStart(char c) { + static bool IsIdentifierStart(char c) { return c is '_' or (>= 'A' and <= 'Z') or (>= 'a' and <= 'z'); } - static bool IsInstructionNamePart(char c) { - return IsInstructionNameStart(c) || IsDigit(c); + static bool IsIdentifierPart(char c) { + return IsIdentifierStart(c) || IsDigit(c); } static bool IsDigit(char c) { From 699f91ccac63a6a61b3c3aa765e8eb9664d2fd58 Mon Sep 17 00:00:00 2001 From: fl4tisjustice Date: Fri, 5 Sep 2025 16:06:09 +0100 Subject: [PATCH 2/3] Specialization of InstructionSpec into VmInstructionSpec and DataDirectiveSpec --- mgs-spec-bank/datadirectives/base.yaml | 6 +- src/MagesScriptTool/DataDirective.cs | 13 -- src/MagesScriptTool/DataDirectiveEncoding.cs | 112 ------------------ src/MagesScriptTool/DataDirectiveSpec.cs | 10 +- src/MagesScriptTool/DataDirectivesSpec.cs | 20 ---- src/MagesScriptTool/InstructionEncoding.cs | 68 +++++++---- src/MagesScriptTool/InstructionKind.cs | 5 + src/MagesScriptTool/InstructionSpec.cs | 6 +- src/MagesScriptTool/OperandKind.cs | 1 + src/MagesScriptTool/Program.cs | 10 +- src/MagesScriptTool/ScriptCompiler.cs | 8 +- src/MagesScriptTool/ScriptDecompiler.cs | 55 ++++----- src/MagesScriptTool/SpecBank.cs | 28 ++--- .../UncompiledScriptElementDataDirective.cs | 9 -- src/MagesScriptTool/UncompiledScriptSyntax.cs | 23 ---- src/MagesScriptTool/VmInstructionSpec.cs | 11 ++ 16 files changed, 110 insertions(+), 275 deletions(-) delete mode 100644 src/MagesScriptTool/DataDirective.cs delete mode 100644 src/MagesScriptTool/DataDirectiveEncoding.cs delete mode 100644 src/MagesScriptTool/DataDirectivesSpec.cs create mode 100644 src/MagesScriptTool/InstructionKind.cs delete mode 100644 src/MagesScriptTool/UncompiledScriptElementDataDirective.cs create mode 100644 src/MagesScriptTool/VmInstructionSpec.cs diff --git a/mgs-spec-bank/datadirectives/base.yaml b/mgs-spec-bank/datadirectives/base.yaml index 38b4418..ad9facc 100644 --- a/mgs-spec-bank/datadirectives/base.yaml +++ b/mgs-spec-bank/datadirectives/base.yaml @@ -3,15 +3,15 @@ flags: [~stringid_16] - name: "StringID" - operands: [int16] + operands: [uint16] flags: [stringid_16] - name: "dw" - operands: [int16] + operands: [uint16] flags: [] - name: "Adr" - operands: [int16] + operands: [uint16] flags: [] - name: "dd" diff --git a/src/MagesScriptTool/DataDirective.cs b/src/MagesScriptTool/DataDirective.cs deleted file mode 100644 index b32cdff..0000000 --- a/src/MagesScriptTool/DataDirective.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Immutable; - -namespace MagesScriptTool; - -sealed class DataDirective { - public readonly string Name; - public readonly ImmutableArray Operands; - - public DataDirective(string name, ImmutableArray operands) { - Name = name; - Operands = operands; - } -} diff --git a/src/MagesScriptTool/DataDirectiveEncoding.cs b/src/MagesScriptTool/DataDirectiveEncoding.cs deleted file mode 100644 index 56fa890..0000000 --- a/src/MagesScriptTool/DataDirectiveEncoding.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Collections.Immutable; -using System.Diagnostics; - -namespace MagesScriptTool; - -sealed class DataDirectiveEncoding { - readonly DataDirectivesSpec _spec; - - public DataDirectiveEncoding(DataDirectivesSpec spec) { - _spec = spec; - } - - public void Encode(Stream stream, DataDirective dataDirective) { - DataDirectiveSpec? spec = _spec.GetSpec(dataDirective.Name); - if (spec is null) { - throw new Exception($"Unrecognized data directive name: {dataDirective.Name}."); - } - ImmutableArray kinds = spec.Operands; - ImmutableArray operands = dataDirective.Operands; - if (operands.Length != kinds.Length) { - throw new Exception($"Expected {kinds.Length} arguments, got {operands.Length}."); - } - for (int i = 0; i < kinds.Length; i++) { - EncodeOperand(stream, kinds[i], operands[i]); - } - } - - public DataDirective Decode(Stream stream, string name) { - DataDirectiveSpec? spec = _spec.GetSpec(name); - Debug.Assert(spec is not null); - - ImmutableArray operandSpecs = spec.Operands; - List operands = []; - foreach (OperandKind operandSpec in operandSpecs) { - operands.Add(DecodeOperand(stream, operandSpec)); - } - return new(spec.Name, [..operands]); - } - - static void EncodeOperand(Stream stream, OperandKind kind, ExpressionNode operand) { - switch (kind) { - case OperandKind.Int16: { - EncodeOperandInt16(stream, operand); - break; - } - case OperandKind.Int32: { - EncodeOperandInt32(stream, operand); - break; - } - default: { - throw new NotImplementedException(kind.ToString()); - } - }; - } - - static void EncodeOperandInt16(Stream stream, ExpressionNode expression) { - int value = expression.GetInt(); - PutByte(stream, (byte)(value >> 0)); - PutByte(stream, (byte)(value >> 8)); - } - - static void EncodeOperandInt32(Stream stream, ExpressionNode expression) { - int value = expression.GetInt(); - PutByte(stream, (byte)(value >> 0)); - PutByte(stream, (byte)(value >> 8)); - PutByte(stream, (byte)(value >> 16)); - PutByte(stream, (byte)(value >> 24)); - } - - static ExpressionNode DecodeOperand(Stream stream, OperandKind kind) { - return kind switch { - OperandKind.Int16 => DecodeOperandInt16(stream), - OperandKind.Int32 => DecodeOperandInt32(stream), - _ => throw new NotImplementedException(kind.ToString()), - }; - } - - static ExpressionNodeNumber DecodeOperandInt16(Stream stream) { - int value = 0; - value |= GetByte(stream) << 0; - value |= GetByte(stream) << 8; - value = SignExtend(value, 16); - return new(value); - } - - static ExpressionNodeNumber DecodeOperandInt32(Stream stream) { - int value = 0; - value |= GetByte(stream) << 0; - value |= GetByte(stream) << 8; - value |= GetByte(stream) << 16; - value |= GetByte(stream) << 24; - value = SignExtend(value, 32); - return new(value); - } - - static byte GetByte(Stream stream) { - int value = stream.ReadByte(); - if (value < 0) { - throw new EndOfStreamException(); - } - return (byte)value; - } - - static int SignExtend(int value, int length) { - int mask = 1 << (length - 1); - int sign = value & mask; - return value | ~(sign - 1); - } - static void PutByte(Stream stream, byte value) { - stream.WriteByte(value); - } -} \ No newline at end of file diff --git a/src/MagesScriptTool/DataDirectiveSpec.cs b/src/MagesScriptTool/DataDirectiveSpec.cs index edad419..c06e766 100644 --- a/src/MagesScriptTool/DataDirectiveSpec.cs +++ b/src/MagesScriptTool/DataDirectiveSpec.cs @@ -2,12 +2,6 @@ namespace MagesScriptTool; -sealed class DataDirectiveSpec { - public readonly string Name; - public readonly ImmutableArray Operands; - - public DataDirectiveSpec(string name, ImmutableArray operands) { - Name = name; - Operands = operands; - } +sealed class DataDirectiveSpec : InstructionSpec { + public DataDirectiveSpec(string name, ImmutableArray operands) : base(name, operands) {} } diff --git a/src/MagesScriptTool/DataDirectivesSpec.cs b/src/MagesScriptTool/DataDirectivesSpec.cs deleted file mode 100644 index 5a13c4e..0000000 --- a/src/MagesScriptTool/DataDirectivesSpec.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Immutable; - -namespace MagesScriptTool; - -sealed class DataDirectivesSpec { - readonly Dictionary _byName = []; - - public DataDirectivesSpec(ImmutableArray specs) { - foreach (DataDirectiveSpec spec in specs) { - if (_byName.ContainsKey(spec.Name)) { - throw new Exception($"Duplicate string tag name: {spec.Name}."); - } - _byName[spec.Name] = spec; - } - } - - public DataDirectiveSpec? GetSpec(string name) { - return _byName.GetValueOrDefault(name); - } -} diff --git a/src/MagesScriptTool/InstructionEncoding.cs b/src/MagesScriptTool/InstructionEncoding.cs index c393c96..dc36444 100644 --- a/src/MagesScriptTool/InstructionEncoding.cs +++ b/src/MagesScriptTool/InstructionEncoding.cs @@ -1,20 +1,24 @@ using System.Collections.Immutable; +using System.Diagnostics; using System.Text; +using System.Xml; namespace MagesScriptTool; sealed class InstructionEncoding { - readonly ImmutableTree _tree; - readonly ImmutableDictionary _table; + readonly ImmutableTree _opcodeTree; + readonly ImmutableDictionary _specTable; - InstructionEncoding(ImmutableTree tree, ImmutableDictionary table) { - _tree = tree; - _table = table; + InstructionEncoding(ImmutableTree opcodeTree, ImmutableDictionary specTable) { + _opcodeTree = opcodeTree; + _specTable = specTable; } public void Encode(Stream stream, Instruction instruction) { - InstructionSpec spec = _table[instruction.Name]; - PutBytes(stream, spec.Opcode.AsSpan()); + InstructionSpec spec = _specTable[instruction.Name]; + if (spec is VmInstructionSpec vmInstructionSpec) { + PutBytes(stream, vmInstructionSpec.Opcode.AsSpan()); + } ImmutableArray kinds = spec.Operands; ImmutableArray operands = instruction.Operands; if (operands.Length != kinds.Length) { @@ -32,9 +36,22 @@ public Instruction Decode(Stream stream) { foreach (OperandKind operandSpec in operandSpecs) { operands.Add(DecodeOperand(stream, operandSpec)); } - return new(spec.Name, [..operands]); + return new (spec.Name, [..operands]); } + public Instruction Decode(Stream stream, string dataDirective) { + DataDirectiveSpec spec = _specTable.GetValueOrDefault(dataDirective) switch { + DataDirectiveSpec dataDirectiveSpec => dataDirectiveSpec, + _ => throw new Exception($"Unrecognized data directive name: {dataDirective}") + }; + + ImmutableArray operandSpecs = spec.Operands; + List operands = []; + foreach (OperandKind operandSpec in operandSpecs) { + operands.Add(DecodeOperand(stream, operandSpec)); + } + return new (spec.Name, [..operands]); + } static void EncodeOperand(Stream stream, OperandKind kind, ExpressionNode operand) { switch (kind) { case OperandKind.Expr: { @@ -45,6 +62,7 @@ static void EncodeOperand(Stream stream, OperandKind kind, ExpressionNode operan EncodeOperandInt8(stream, operand); break; } + case OperandKind.UInt16: case OperandKind.Int16: { EncodeOperandInt16(stream, operand); break; @@ -95,17 +113,17 @@ static void PutBytes(Stream stream, ReadOnlySpan data) { stream.Write(data); } - InstructionSpec DecodeOpcode(Stream stream) { + VmInstructionSpec DecodeOpcode(Stream stream) { long start = stream.Position; - ImmutableTree cursor = _tree; + ImmutableTree cursor = _opcodeTree; while (true) { byte b = GetByte(stream); - ImmutableTree? next = cursor[b]; + ImmutableTree? next = cursor[b]; if (next is null) { throw new Exception($"Unrecognized instruction at {start}."); } cursor = next; - if (cursor.Value is InstructionSpec opcodeSpec) { + if (cursor.Value is VmInstructionSpec opcodeSpec) { return opcodeSpec; } } @@ -115,6 +133,7 @@ static ExpressionNode DecodeOperand(Stream stream, OperandKind kind) { return kind switch { OperandKind.Expr => ExpressionEncoding.Decode(stream), OperandKind.Int8 => DecodeOperandInt8(stream), + OperandKind.UInt16 => DecodeOperandUInt16(stream), OperandKind.Int16 => DecodeOperandInt16(stream), OperandKind.Int32 => DecodeOperandInt32(stream), OperandKind.Str => DecodeOperandStr(stream), @@ -138,6 +157,13 @@ static ExpressionNodeNumber DecodeOperandInt8(Stream stream) { return new(value); } + static ExpressionNodeNumber DecodeOperandUInt16(Stream stream) { + int value = 0; + value |= GetByte(stream) << 0; + value |= GetByte(stream) << 8; + return new(value); + } + static ExpressionNodeNumber DecodeOperandInt16(Stream stream) { int value = 0; value |= GetByte(stream) << 0; @@ -169,10 +195,10 @@ static int SignExtend(int value, int length) { return value | ~(sign - 1); } - public static InstructionEncoding BuildFrom(ImmutableArray opcodeSpecs) { - ImmutableTree tree = BuildOpcodeTree(opcodeSpecs); + public static InstructionEncoding BuildFrom(ImmutableArray specs) { + ImmutableTree tree = BuildOpcodeTree(specs); Dictionary table = []; - foreach (InstructionSpec spec in opcodeSpecs) { + foreach (InstructionSpec spec in specs) { if (table.ContainsKey(spec.Name)) { throw new Exception($"Duplicate instruction name: {spec.Name}."); } @@ -181,14 +207,16 @@ public static InstructionEncoding BuildFrom(ImmutableArray opco return new(tree, table.ToImmutableDictionary()); } - static ImmutableTree BuildOpcodeTree(ImmutableArray specs) { - Tree tree = new(); + static ImmutableTree BuildOpcodeTree(ImmutableArray specs) { + Tree tree = new(); foreach (InstructionSpec spec in specs) { - ImmutableArray opcode = spec.Opcode; + if (spec is not VmInstructionSpec) continue; + VmInstructionSpec vmInstructionSpec = (VmInstructionSpec)spec; + ImmutableArray opcode = vmInstructionSpec.Opcode; if (opcode.Length == 0) { throw new Exception($"Empty opcode: {spec.Name}."); } - Tree cursor = tree; + Tree cursor = tree; for (int i = 0; i < opcode.Length; i++) { if (cursor.HasValue) { throw new Exception($"Duplicate opcode prefix: {Convert.ToHexString(opcode.AsSpan()[..i])}."); @@ -201,7 +229,7 @@ static ImmutableTree BuildOpcodeTree(ImmutableArray Opcode; public readonly ImmutableArray Operands; - public InstructionSpec(string name, ImmutableArray opcode, ImmutableArray operands) { + public InstructionSpec(string name, ImmutableArray operands) { Name = name; - Opcode = opcode; Operands = operands; } } diff --git a/src/MagesScriptTool/OperandKind.cs b/src/MagesScriptTool/OperandKind.cs index e6941f8..2e979d9 100644 --- a/src/MagesScriptTool/OperandKind.cs +++ b/src/MagesScriptTool/OperandKind.cs @@ -4,6 +4,7 @@ enum OperandKind { Expr, UInt8, Int8, + UInt16, Int16, Int32, Str, diff --git a/src/MagesScriptTool/Program.cs b/src/MagesScriptTool/Program.cs index 29ef8dc..1ec70a4 100644 --- a/src/MagesScriptTool/Program.cs +++ b/src/MagesScriptTool/Program.cs @@ -78,7 +78,6 @@ sealed class Tool { public readonly bool GenerateSdb; public readonly InstructionEncoding InstructionEncoding; - public readonly DataDirectiveEncoding DataDirectiveEncoding; public readonly UncompiledStringTableSyntax UncompiledStringTableSyntax; public readonly StringGlyphSyntax StringGlyphSyntax; public readonly CompiledScriptPackageEncoding CompiledScriptPackageEncoding; @@ -111,11 +110,6 @@ public Tool(ParseResult result) { ImmutableArray stringTagSpecs = bank.GetStringTagSpecs(flags); StringTagsSpec stringTagsSpec = new(stringTagSpecs); - ImmutableArray dataDirectiveSpecs = bank.GetDataDirectiveSpecs(flags); - DataDirectivesSpec dataDirectivesSpec = new(dataDirectiveSpecs); - - DataDirectiveEncoding = new(dataDirectivesSpec); - CompiledStringEncoding compiledStringEncoding = new(stringTagsSpec); CompiledScriptPackageEncoding = new(compiledStringEncoding); CompiledStringTableEncoding = new(compiledStringEncoding); @@ -234,7 +228,7 @@ static async Task CompileScriptPackage(Tool tool, string uncompiledScriptN ImmutableArray uncompiledScriptElements = await ParseUncompiledScript(tool, uncompiledScriptPath); ImmutableArray uncompiledStringTableEntries = await ParseUncompiledStringTable(tool, uncompiledStringTablePath); - ScriptCompiler compiler = new(tool.InstructionEncoding, tool.DataDirectiveEncoding); + ScriptCompiler compiler = new(tool.InstructionEncoding); CompiledScript compiledScript = compiler.Compile(uncompiledScriptElements); List> compiledStrings = []; @@ -298,7 +292,7 @@ static async Task DecompileScriptPackage(Tool tool, string compiledScriptP try { CompiledScriptPackage compiledScriptPackage = await DecodeCompiledScriptPackage(tool, compiledScriptPackagePath); - ScriptDecompiler decompiler = new(tool.InstructionEncoding, tool.DataDirectiveEncoding, compiledScriptPackage.Script); + ScriptDecompiler decompiler = new(tool.InstructionEncoding, compiledScriptPackage.Script); (ImmutableArray uncompiledScriptElements, ImmutableDictionary instructionPositions) = decompiler.Decompile(); ImmutableArray> compiledStrings = compiledScriptPackage.Strings; diff --git a/src/MagesScriptTool/ScriptCompiler.cs b/src/MagesScriptTool/ScriptCompiler.cs index 5f95030..1dcd67e 100644 --- a/src/MagesScriptTool/ScriptCompiler.cs +++ b/src/MagesScriptTool/ScriptCompiler.cs @@ -4,14 +4,12 @@ namespace MagesScriptTool; sealed class ScriptCompiler { readonly InstructionEncoding _instructionEncoding; - readonly DataDirectiveEncoding _dataDirectiveEncoding; readonly MemoryStream _stream = new(); readonly SortedDictionary _labelTable = []; readonly SortedDictionary _returnLabelTable = []; - public ScriptCompiler(InstructionEncoding instructionEncoding, DataDirectiveEncoding dataDirectiveEncoding) { + public ScriptCompiler(InstructionEncoding instructionEncoding) { _instructionEncoding = instructionEncoding; - _dataDirectiveEncoding = dataDirectiveEncoding; } public CompiledScript Compile(ImmutableArray elements) { @@ -44,10 +42,6 @@ void ProcessElement(UncompiledScriptElement element) { _instructionEncoding.Encode(_stream, instruction); break; } - case UncompiledScriptElementDataDirective { Value: DataDirective dataDirective }: { - _dataDirectiveEncoding.Encode(_stream, dataDirective); - break; - } case UncompiledScriptElementLabel { Index: int index }: { int offset = (int)_stream.Position; if (_labelTable.ContainsKey(index)) { diff --git a/src/MagesScriptTool/ScriptDecompiler.cs b/src/MagesScriptTool/ScriptDecompiler.cs index 9ae0a99..9c73a77 100644 --- a/src/MagesScriptTool/ScriptDecompiler.cs +++ b/src/MagesScriptTool/ScriptDecompiler.cs @@ -4,8 +4,6 @@ namespace MagesScriptTool; sealed class ScriptDecompiler { readonly InstructionEncoding _instructionEncoding; - readonly DataDirectiveEncoding _dataDirectiveEncoding; - readonly ImmutableArray _code; readonly ImmutableArray _labels; readonly ImmutableArray _returnLabels; @@ -15,9 +13,8 @@ sealed class ScriptDecompiler { readonly Dictionary _instructionPositions = []; - public ScriptDecompiler(InstructionEncoding instructionEncoding, DataDirectiveEncoding dataDirectiveEncoding, CompiledScript compiledScript) { + public ScriptDecompiler(InstructionEncoding instructionEncoding, CompiledScript compiledScript) { _instructionEncoding = instructionEncoding; - _dataDirectiveEncoding = dataDirectiveEncoding; _code = compiledScript.Code; _labels = compiledScript.Labels; @@ -144,7 +141,6 @@ void SetLabelKind(int label, ChunkKind kind) { sealed class Chunk : Stream { readonly ScriptDecompiler _parent; readonly InstructionEncoding _instructionEncoding; - readonly DataDirectiveEncoding _dataDirectiveEncoding; readonly ImmutableArray _code; readonly public int _index; @@ -174,7 +170,6 @@ public override long Position { public Chunk(ScriptDecompiler parent, int index, int start, int end) { _parent = parent; _instructionEncoding = parent._instructionEncoding; - _dataDirectiveEncoding = parent._dataDirectiveEncoding; _code = parent._code; _index = index; @@ -261,39 +256,39 @@ void Decode() { void DecodeInt16Table() { while (Position < Length) { - AddDataDirective(_dataDirectiveEncoding.Decode(this, "dw")); + AddInstruction(_instructionEncoding.Decode(this, "dw")); } } void DecodeInt32Table() { while (Position < Length) { - AddDataDirective(_dataDirectiveEncoding.Decode(this, "dd")); + AddInstruction(_instructionEncoding.Decode(this, "dd")); } } void DecodeAdrTable() { while (Position < Length) { - AddDataDirective(_dataDirectiveEncoding.Decode(this, "Adr")); + AddInstruction(_instructionEncoding.Decode(this, "Adr")); } } void DecodeTextTable() { while (Position < Length) { - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); } } void DecodeNameIdTable() { SetIncomplete(); while (true) { - DataDirective id = _dataDirectiveEncoding.Decode(this, "dw"); - AddDataDirective(id); + Instruction id = _instructionEncoding.Decode(this, "dw"); + AddInstruction(id); if ((id.Operands[0].GetInt() & 0xFFFF) == 0xFFFF) { break; } - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); } SetComplete(); } @@ -302,8 +297,8 @@ void DecodeEncycDataTable() { SetIncomplete(); int index = 0; while (true) { - DataDirective pageCount = _dataDirectiveEncoding.Decode(this, "dw"); - AddDataDirective(pageCount); + Instruction pageCount = _instructionEncoding.Decode(this, "dw"); + AddInstruction(pageCount); if ((pageCount.Operands[0].GetInt() & 0xFFFF) == 0xFF) { break; } @@ -312,20 +307,20 @@ void DecodeEncycDataTable() { index++; // Category - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); // Name - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); // Pronounciation - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); // Sorting key - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); // Content for (int i = 0; i < pageCount.Operands[0].GetInt(); i++) { - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); } } SetComplete(); @@ -334,13 +329,13 @@ void DecodeEncycDataTable() { void DecodeEncycSortTable() { SetIncomplete(); - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); - AddDataDirective(_dataDirectiveEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); + AddInstruction(_instructionEncoding.Decode(this, "StringID")); for (int i = 0; i < 2; i++) { while (true) { - DataDirective value0 = _dataDirectiveEncoding.Decode(this, "dw"); - AddDataDirective(value0); + Instruction value0 = _instructionEncoding.Decode(this, "dw"); + AddInstruction(value0); if ((value0.Operands[0].GetInt() & 0xFFFF) == 0xFFFF) { break; } @@ -353,7 +348,7 @@ void DecodeEncycSortTable() { void DecodeMesModeFormatTable() { int index = 0; while (Position < Length) { - DataDirective value0 = _dataDirectiveEncoding.Decode(this, "dw"); + Instruction value0 = _instructionEncoding.Decode(this, "dw"); string? comment = index switch { 0 => "display mode", 1 => "message window ID", @@ -380,7 +375,7 @@ void DecodeMesModeFormatTable() { if (comment is not null) { AddComment(comment); } - AddDataDirective(value0); + AddInstruction(value0); index++; } } @@ -405,12 +400,6 @@ public void AddInstruction(Instruction instruction) { UpdateLastPosition(); } - public void AddDataDirective(DataDirective dataDirective) { - UncompiledScriptElementDataDirective element = new(dataDirective); - Body.Add(element); - UpdateLastPosition(); - } - public void UpdateLastPosition() { LastPosition = _position; } diff --git a/src/MagesScriptTool/SpecBank.cs b/src/MagesScriptTool/SpecBank.cs index e56b1ed..05baf04 100644 --- a/src/MagesScriptTool/SpecBank.cs +++ b/src/MagesScriptTool/SpecBank.cs @@ -23,9 +23,20 @@ public ImmutableArray GetInstructionSpecs(ImmutableDictionary GetDataDirectiveSpecs(ImmutableDictionary flags) { - List result = []; - foreach (string path in _index.DataDirectives) { - SerializedDataDirectiveSpec[] specs = SerializedDataDirectiveSpec.LoadList(Path.Join(_path, path)); - foreach (SerializedDataDirectiveSpec spec in specs) { - if (!spec.CheckFlags(flags)) { - continue; - } - result.Add(new(spec.Name, spec.ParseOperands())); - } - } - return [..result]; - } - public ImmutableDictionary GetFlags(string key) { return _index.Flags[key].ToImmutableDictionary(); } @@ -291,6 +288,7 @@ public ImmutableArray ParseOperands() { "expr" => OperandKind.Expr, "uint8" => OperandKind.UInt8, "int8" => OperandKind.Int8, + "uint16" => OperandKind.UInt16, "int16" => OperandKind.Int16, "int32" => OperandKind.Int32, "str" => OperandKind.Str, diff --git a/src/MagesScriptTool/UncompiledScriptElementDataDirective.cs b/src/MagesScriptTool/UncompiledScriptElementDataDirective.cs deleted file mode 100644 index 6044cb4..0000000 --- a/src/MagesScriptTool/UncompiledScriptElementDataDirective.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MagesScriptTool; - -sealed class UncompiledScriptElementDataDirective : UncompiledScriptElement { - public readonly DataDirective Value; - - public UncompiledScriptElementDataDirective(DataDirective value) { - Value = value; - } -} diff --git a/src/MagesScriptTool/UncompiledScriptSyntax.cs b/src/MagesScriptTool/UncompiledScriptSyntax.cs index edcf3b3..2c6a509 100644 --- a/src/MagesScriptTool/UncompiledScriptSyntax.cs +++ b/src/MagesScriptTool/UncompiledScriptSyntax.cs @@ -46,11 +46,6 @@ void FormatElement(UncompiledScriptElement genericElement) { Append($"\t{s}\n"); break; } - case UncompiledScriptElementDataDirective { Value: DataDirective dataDirective }: { - string s = FormatDataDirective(dataDirective); - Append($"\t{s}\n"); - break; - } case UncompiledScriptElementLabel { Index: int index }: { Append($"{index}"); Append(":"); @@ -117,19 +112,6 @@ string FormatInstruction(Instruction instruction) { return sb.ToString(); } - string FormatDataDirective(DataDirective dataDirective) { - StringBuilder sb = new(); - sb.Append(dataDirective.Name); - for (int i = 0; i < dataDirective.Operands.Length; i++) { - if (i != 0) { - sb.Append(","); - } - sb.Append(" "); - ExpressionSyntax.Format(sb, dataDirective.Operands[i]); - } - return sb.ToString(); - } - void Append(string s) { foreach (char c in s) { if (c == '\n') { @@ -164,11 +146,6 @@ UncompiledScriptElement ParseElement() { switch (name) { case "hex": { return new UncompiledScriptElementRaw(ParseRaw()); - } case "StringID" or "Adr" or "dw" or "dd": { - ImmutableArray operands = ParseOperands(); - DataDirective dataDirective = new(name, operands); - return new UncompiledScriptElementDataDirective(dataDirective); - break; } default: { ImmutableArray operands = ParseOperands(); diff --git a/src/MagesScriptTool/VmInstructionSpec.cs b/src/MagesScriptTool/VmInstructionSpec.cs new file mode 100644 index 0000000..3ef50f5 --- /dev/null +++ b/src/MagesScriptTool/VmInstructionSpec.cs @@ -0,0 +1,11 @@ +using System.Collections.Immutable; + +namespace MagesScriptTool; + +class VmInstructionSpec : InstructionSpec { + public readonly ImmutableArray Opcode; + + public VmInstructionSpec(string name, ImmutableArray opcode, ImmutableArray operands) : base(name, operands) { + Opcode = opcode; + } +} \ No newline at end of file From 405f2793cfd4bb0b9369601d4c001320d9148e35 Mon Sep 17 00:00:00 2001 From: fl4tisjustice Date: Wed, 10 Sep 2025 09:17:22 +0100 Subject: [PATCH 3/3] C;H LCC italics --- .../charset/chaos_head_lcc-extended.json | 348 ++++++++++++++++++ 1 file changed, 348 insertions(+) diff --git a/mgs-spec-bank/charset/chaos_head_lcc-extended.json b/mgs-spec-bank/charset/chaos_head_lcc-extended.json index 59fac1e..27de43d 100644 --- a/mgs-spec-bank/charset/chaos_head_lcc-extended.json +++ b/mgs-spec-bank/charset/chaos_head_lcc-extended.json @@ -8978,5 +8978,353 @@ "text": "=" } ] + }, + { + "parameters": { + "encoding": { + "style": "italic" + }, + "rasterization": { + "font": "italic" + } + }, + "glyphs": [ + { + "units": [2368], + "text": "!" + }, + { + "units": [2369], + "text": "'" + }, + { + "units": [2370], + "text": "," + }, + { + "units": [2371], + "text": "-" + }, + { + "units": [2372], + "text": "." + }, + { + "units": [2373], + "text": "0" + }, + { + "units": [2374], + "text": "1" + }, + { + "units": [2375], + "text": "2" + }, + { + "units": [2376], + "text": "3" + }, + { + "units": [2377], + "text": "4" + }, + { + "units": [2378], + "text": "5" + }, + { + "units": [2379], + "text": "6" + }, + { + "units": [2380], + "text": "7" + }, + { + "units": [2381], + "text": "8" + }, + { + "units": [2382], + "text": "9" + }, + { + "units": [2383], + "text": ":" + }, + { + "units": [2384], + "text": "?" + }, + { + "units": [2385], + "text": "A" + }, + { + "units": [2386], + "text": "B" + }, + { + "units": [2387], + "text": "C" + }, + { + "units": [2388], + "text": "D" + }, + { + "units": [2389], + "text": "E" + }, + { + "units": [2390], + "text": "F" + }, + { + "units": [2391], + "text": "G" + }, + { + "units": [2392], + "text": "H" + }, + { + "units": [2393], + "text": "I" + }, + { + "units": [2394], + "text": "J" + }, + { + "units": [2395], + "text": "K" + }, + { + "units": [2396], + "text": "L" + }, + { + "units": [2397], + "text": "M" + }, + { + "units": [2398], + "text": "N" + }, + { + "units": [2399], + "text": "O" + }, + { + "units": [2400], + "text": "P" + }, + { + "units": [2401], + "text": "Q" + }, + { + "units": [2402], + "text": "R" + }, + { + "units": [2403], + "text": "S" + }, + { + "units": [2405], + "text": "T" + }, + { + "units": [2406], + "text": "U" + }, + { + "units": [2407], + "text": "V" + }, + { + "units": [2408], + "text": "W" + }, + { + "units": [2409], + "text": "X" + }, + { + "units": [2410], + "text": "Y" + }, + { + "units": [2411], + "text": "Z" + }, + { + "units": [2412], + "text": "a" + }, + { + "units": [2413], + "text": "b" + }, + { + "units": [2414], + "text": "c" + }, + { + "units": [2415], + "text": "d" + }, + { + "units": [2416], + "text": "e" + }, + { + "units": [2417], + "text": "f" + }, + { + "units": [2418], + "text": "g" + }, + { + "units": [2419], + "text": "h" + }, + { + "units": [2420], + "text": "i" + }, + { + "units": [2421], + "text": "j" + }, + { + "units": [2422], + "text": "k" + }, + { + "units": [2423], + "text": "l" + }, + { + "units": [2424], + "text": "m" + }, + { + "units": [2425], + "text": "n" + }, + { + "units": [2426], + "text": "o" + }, + { + "units": [2427], + "text": "p" + }, + { + "units": [2428], + "text": "q" + }, + { + "units": [2429], + "text": "r" + }, + { + "units": [2430], + "text": "s" + }, + { + "units": [2431], + "text": "t" + }, + { + "units": [2432], + "text": "u" + }, + { + "units": [2433], + "text": "v" + }, + { + "units": [2434], + "text": "w" + }, + { + "units": [2435], + "text": "x" + }, + { + "units": [2436], + "text": "y" + }, + { + "units": [2437], + "text": "z" + }, + { + "units": [2438], + "text": "—" + }, + { + "units": [2439], + "text": "‘" + }, + { + "units": [2440], + "text": "’" + }, + { + "units": [2441], + "text": "“" + }, + { + "units": [2442], + "text": "”" + }, + { + "units": [2443], + "text": "Ⅱ" + }, + { + "units": [2444], + "text": "(" + }, + { + "units": [2445], + "text": ")" + }, + { + "units": [2446], + "text": "♪" + }, + { + "units": [2447], + "text": "/" + }, + { + "units": [2448], + "text": "+" + }, + { + "units": [2449], + "text": "@" + }, + { + "units": [2450], + "text": "*" + }, + { + "units": [2451], + "text": ";" + }, + { + "units": [2452], + "text": "☆" + } + ] } ]