From 1ba7d3b6a7e33af2bcdb56928532914f7c72cafc Mon Sep 17 00:00:00 2001 From: Daan Vanden Bosch Date: Mon, 5 Aug 2019 00:14:39 +0200 Subject: [PATCH] All bugs resulting from opcode typing changes have been fixed. --- .../resources/scripting/opcodes.yml | 25 +++++++--- src/data_formats/parsing/quest/bin.ts | 8 ++- src/scripting/AssemblyLexer.ts | 2 +- src/scripting/assembler_messages.ts | 2 +- src/scripting/assembly.ts | 50 +++++++++++-------- src/scripting/assembly_worker.ts | 5 ++ src/scripting/opcodes.ts | 26 +++++++--- .../quest_editor/AssemblyEditorComponent.tsx | 22 ++++---- 8 files changed, 89 insertions(+), 51 deletions(-) diff --git a/assets_generation/resources/scripting/opcodes.yml b/assets_generation/resources/scripting/opcodes.yml index 4441aa25..8ff98196 100644 --- a/assets_generation/resources/scripting/opcodes.yml +++ b/assets_generation/resources/scripting/opcodes.yml @@ -854,9 +854,11 @@ opcodes: params: - type: reg_tup_ref reg_tup: - - type: byte + - type: dword access: write - type: dword + doc: Player slot. + stack: pop - code: 0x6b mnemonic: p_disablewarp @@ -2829,8 +2831,14 @@ opcodes: - code: 0xf8dc mnemonic: npc_action_string params: - - type: dword - - type: dword + - type: reg_tup_ref + reg_tup: + - type: dword + access: read + - type: reg_tup_ref + reg_tup: + - type: dword + access: read - type: string_label - code: 0xf8dd @@ -3346,12 +3354,15 @@ opcodes: - code: 0xf924 mnemonic: get_coord_player_detect params: - - type: dword - doc: Player slot. - type: reg_tup_ref reg_tup: # TODO: determine type and access - - type: dword - access: write + - type: any + doc: Player slot. + access: read + - type: reg_tup_ref + reg_tup: # TODO: determine type and access + - type: any + access: read - code: 0xf925 mnemonic: read_global_flag diff --git a/src/data_formats/parsing/quest/bin.ts b/src/data_formats/parsing/quest/bin.ts index 984c49a9..e3a251bb 100644 --- a/src/data_formats/parsing/quest/bin.ts +++ b/src/data_formats/parsing/quest/bin.ts @@ -303,7 +303,8 @@ function parse_object_code( offset += segment.data.byteLength; break; case SegmentType.String: - offset += 2 * segment.value.length + 2; + // String segments should be multiples of 4 bytes. + offset += 4 * Math.ceil((segment.value.length + 1) / 2); break; default: throw new Error(`${SegmentType[segment!.type]} not implemented.`); @@ -501,6 +502,7 @@ function parse_instructions_segment( break; case TYPE_D_LABEL: segment_type = SegmentType.Data; + break; case TYPE_S_LABEL: segment_type = SegmentType.String; break; @@ -722,7 +724,9 @@ function write_object_code( } } } else if (segment.type === SegmentType.String) { - cursor.write_string_utf16(segment.value, 2 * segment.value.length + 2); + // String segments should be multiples of 4 bytes. + const byte_length = 4 * Math.ceil((segment.value.length + 1) / 2); + cursor.write_string_utf16(segment.value, byte_length); } else { cursor.write_cursor(new ArrayBufferCursor(segment.data, cursor.endianness)); } diff --git a/src/scripting/AssemblyLexer.ts b/src/scripting/AssemblyLexer.ts index 73bd0902..efac1ab5 100644 --- a/src/scripting/AssemblyLexer.ts +++ b/src/scripting/AssemblyLexer.ts @@ -323,7 +323,7 @@ export class AssemblyLexer { case ".data": return { type: TokenType.DataSection, col, len: 5 }; case ".string": - return { type: TokenType.DataSection, col, len: 7 }; + return { type: TokenType.StringSection, col, len: 7 }; default: return { type: TokenType.InvalidSection, col, len: this.marked_len() }; } diff --git a/src/scripting/assembler_messages.ts b/src/scripting/assembler_messages.ts index 61f3b6c9..8367fa62 100644 --- a/src/scripting/assembler_messages.ts +++ b/src/scripting/assembler_messages.ts @@ -1,6 +1,6 @@ import { editor } from "monaco-editor"; -import { Segment } from "../data_formats/parsing/quest/bin"; import { AssemblyError } from "./assembly"; +import { Segment } from "./instructions"; export type ScriptWorkerInput = NewAssemblyInput | AssemblyChangeInput; diff --git a/src/scripting/assembly.ts b/src/scripting/assembly.ts index b2cc8849..419123b3 100644 --- a/src/scripting/assembly.ts +++ b/src/scripting/assembly.ts @@ -74,7 +74,7 @@ class Assembler { private errors!: AssemblyError[]; // Encountered labels. private labels!: Set; - private section_type: SegmentType = SegmentType.Instructions; + private section: SegmentType = SegmentType.Instructions; constructor(private assembly: string[], private manual_stack: boolean) {} @@ -89,7 +89,7 @@ class Assembler { this.errors = []; this.labels = new Set(); // Need to cast SegmentType.Instructions because of TypeScript bug. - this.section_type = SegmentType.Instructions as SegmentType; + this.section = SegmentType.Instructions as SegmentType; for (const line of this.assembly) { this.tokens = this.lexer.tokenize_line(line); @@ -107,7 +107,7 @@ class Assembler { this.parse_section(token); break; case TokenType.Int: - if (this.section_type === SegmentType.Data) { + if (this.section === SegmentType.Data) { this.parse_bytes(token); } else { this.add_error({ @@ -118,7 +118,7 @@ class Assembler { } break; case TokenType.String: - if (this.section_type === SegmentType.String) { + if (this.section === SegmentType.String) { this.parse_string(token); } else { this.add_error({ @@ -129,7 +129,7 @@ class Assembler { } break; case TokenType.Ident: - if (this.section_type === SegmentType.Instructions) { + if (this.section === SegmentType.Instructions) { this.parse_instruction(token); } else { this.add_error({ @@ -184,9 +184,11 @@ class Assembler { this.segment = instruction_segment; this.object_code.push(instruction_segment); + } else if (this.segment.type === SegmentType.Instructions) { + this.segment.instructions.push(new Instruction(opcode, args)); + } else { + logger.error(`Line ${this.line_no}: Expected instructions segment.`); } - - (this.segment as InstructionSegment).instructions.push(new Instruction(opcode, args)); } private add_bytes(bytes: number[]): void { @@ -200,15 +202,16 @@ class Assembler { this.segment = data_segment; this.object_code.push(data_segment); - } else { - const d_seg = this.segment as DataSegment; - const buf = new ArrayBuffer(d_seg.data.byteLength + bytes.length); + } else if (this.segment.type === SegmentType.Data) { + const buf = new ArrayBuffer(this.segment.data.byteLength + bytes.length); const arr = new Uint8Array(buf); - arr.set(new Uint8Array(d_seg.data)); + arr.set(new Uint8Array(this.segment.data)); arr.set(new Uint8Array(bytes)); - d_seg.data = buf; + this.segment.data = buf; + } else { + logger.error(`Line ${this.line_no}: Expected data segment.`); } } @@ -223,9 +226,10 @@ class Assembler { this.segment = string_segment; this.object_code.push(string_segment); + } else if (this.segment.type === SegmentType.String) { + this.segment.value += str; } else { - const s_seg = this.segment as StringSegment; - s_seg.value += str; + logger.error(`Line ${this.line_no}: Expected string segment.`); } } @@ -276,7 +280,7 @@ class Assembler { const next_token = this.tokens.shift(); - switch (this.section_type) { + switch (this.section) { case SegmentType.Instructions: this.segment = { type: SegmentType.Instructions, @@ -348,21 +352,21 @@ class Assembler { col, len, }: CodeSectionToken | DataSectionToken | StringSectionToken): void { - let section_type!: SegmentType; + let section!: SegmentType; switch (type) { case TokenType.CodeSection: - section_type = SegmentType.Instructions; + section = SegmentType.Instructions; break; case TokenType.DataSection: - section_type = SegmentType.Data; + section = SegmentType.Data; break; case TokenType.StringSection: - section_type = SegmentType.String; + section = SegmentType.String; break; } - if (this.section_type === section_type) { + if (this.section === section) { this.add_warning({ col, length: len, @@ -370,7 +374,7 @@ class Assembler { }); } - this.section_type = section_type; + this.section = section; const next_token = this.tokens.shift(); @@ -479,7 +483,9 @@ class Assembler { if (param.type instanceof RegTupRefType) { this.add_instruction(Opcode.ARG_PUSHB, [arg]); } else { - logger.error(`Type ${param.type} not implemented.`); + logger.error( + `Line ${this.line_no}: Type ${param.type} not implemented.` + ); } break; diff --git a/src/scripting/assembly_worker.ts b/src/scripting/assembly_worker.ts index 9545c2ec..5afef60c 100644 --- a/src/scripting/assembly_worker.ts +++ b/src/scripting/assembly_worker.ts @@ -1,5 +1,10 @@ import { NewObjectCodeOutput, ScriptWorkerInput } from "./assembler_messages"; import { assemble } from "./assembly"; +import Logger from "js-logger"; + +Logger.useDefaults({ + defaultLevel: (Logger as any)[process.env["LOG_LEVEL"] || "OFF"], +}); const ctx: Worker = self as any; diff --git a/src/scripting/opcodes.ts b/src/scripting/opcodes.ts index 36b5465f..7beaf3bb 100644 --- a/src/scripting/opcodes.ts +++ b/src/scripting/opcodes.ts @@ -1437,13 +1437,13 @@ export class Opcode { undefined, [ new Param( - new RegTupRefType(new Param(TYPE_BYTE, undefined, ParamAccess.Write)), + new RegTupRefType(new Param(TYPE_DWORD, undefined, ParamAccess.Write)), undefined, undefined ), - new Param(TYPE_DWORD, undefined, undefined), + new Param(TYPE_DWORD, "Player slot.", undefined), ], - undefined + StackInteraction.Pop )); static readonly P_DISABLEWARP = (OPCODES[0x6b] = new Opcode( 0x6b, @@ -5049,8 +5049,16 @@ export class Opcode { "npc_action_string", undefined, [ - new Param(TYPE_DWORD, undefined, undefined), - new Param(TYPE_DWORD, undefined, undefined), + new Param( + new RegTupRefType(new Param(TYPE_DWORD, undefined, ParamAccess.Read)), + undefined, + undefined + ), + new Param( + new RegTupRefType(new Param(TYPE_DWORD, undefined, ParamAccess.Read)), + undefined, + undefined + ), new Param(TYPE_S_LABEL, undefined, undefined), ], undefined @@ -5907,9 +5915,13 @@ export class Opcode { "get_coord_player_detect", undefined, [ - new Param(TYPE_DWORD, "Player slot.", undefined), new Param( - new RegTupRefType(new Param(TYPE_DWORD, undefined, ParamAccess.Write)), + new RegTupRefType(new Param(TYPE_ANY, "Player slot.", ParamAccess.Read)), + undefined, + undefined + ), + new Param( + new RegTupRefType(new Param(TYPE_ANY, undefined, ParamAccess.Read)), undefined, undefined ), diff --git a/src/ui/quest_editor/AssemblyEditorComponent.tsx b/src/ui/quest_editor/AssemblyEditorComponent.tsx index 00773680..1839ced7 100644 --- a/src/ui/quest_editor/AssemblyEditorComponent.tsx +++ b/src/ui/quest_editor/AssemblyEditorComponent.tsx @@ -154,7 +154,7 @@ type MonacoProps = { class MonacoComponent extends Component { private div_ref = createRef(); private editor?: editor.IStandaloneCodeEditor; - private assembler?: AssemblyAnalyser; + private assembly_analyser?: AssemblyAnalyser; private disposers: (() => void)[] = []; render(): ReactNode { @@ -173,7 +173,7 @@ class MonacoComponent extends Component { wrappingIndent: "indent", }); - this.assembler = new AssemblyAnalyser(); + this.assembly_analyser = new AssemblyAnalyser(); this.disposers.push( this.dispose, @@ -205,8 +205,8 @@ class MonacoComponent extends Component { private update_model = () => { const quest = quest_editor_store.current_quest; - if (quest && this.editor && this.assembler) { - const assembly = this.assembler.disassemble(quest.object_code); + if (quest && this.editor && this.assembly_analyser) { + const assembly = this.assembly_analyser.disassemble(quest.object_code); const model = editor.createModel(assembly.join("\n"), "psoasm"); quest_editor_store.script_undo.action = new Action( @@ -256,8 +256,8 @@ class MonacoComponent extends Component { current_version = version; - if (!this.assembler) return; - this.assembler.update_assembly(e.changes); + if (!this.assembly_analyser) return; + this.assembly_analyser.update_assembly(e.changes); }); this.disposers.push(() => disposable.dispose()); @@ -269,10 +269,10 @@ class MonacoComponent extends Component { }; private update_model_markers = () => { - if (!this.editor || !this.assembler) return; + if (!this.editor || !this.assembly_analyser) return; // Reference errors here to make sure we get mobx updates. - this.assembler.errors.length; + this.assembly_analyser.errors.length; const model = this.editor.getModel(); if (!model) return; @@ -280,7 +280,7 @@ class MonacoComponent extends Component { editor.setModelMarkers( model, "psoasm", - this.assembler.errors.map(error => ({ + this.assembly_analyser.errors.map(error => ({ severity: MarkerSeverity.Error, message: error.message, startLineNumber: error.line_no, @@ -299,8 +299,8 @@ class MonacoComponent extends Component { this.editor = undefined; } - if (this.assembler) { - this.assembler.dispose(); + if (this.assembly_analyser) { + this.assembly_analyser.dispose(); } }; }