From d55766a514f14f64e2f4dc0375db3a2f28543576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20K=C3=B6pcke?= Date: Thu, 30 Jan 2025 10:58:23 +0100 Subject: [PATCH] Fix offset consistency after Ssb compilation (#572) --- skytemple_files/script/ssb/script_compiler.py | 7 +- .../script/ssb/jump_test.py | 103 ++++++++++++++++++ 2 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 test/skytemple_files_test/script/ssb/jump_test.py diff --git a/skytemple_files/script/ssb/script_compiler.py b/skytemple_files/script/ssb/script_compiler.py index a151c018..e85cc1d3 100644 --- a/skytemple_files/script/ssb/script_compiler.py +++ b/skytemple_files/script/ssb/script_compiler.py @@ -292,10 +292,13 @@ def compile_structured( # Handle the rest new_params.append(self._parse_param(param, built_strings, built_constants)) op_len += 2 - built_ops.append(SkyTempleSsbOperation(opcode_cursor, op_code, new_params)) + + actual_stored_offset = int(opcode_cursor / 2) + + built_ops.append(SkyTempleSsbOperation(actual_stored_offset, op_code, new_params)) # Create actual offset mapping for this opcode and update source map - opcode_index_mem_offset_mapping[in_op.offset] = int(opcode_cursor / 2) + opcode_index_mem_offset_mapping[in_op.offset] = actual_stored_offset bytes_written_last_rtn += op_len opcode_cursor += op_len diff --git a/test/skytemple_files_test/script/ssb/jump_test.py b/test/skytemple_files_test/script/ssb/jump_test.py new file mode 100644 index 00000000..5c69363e --- /dev/null +++ b/test/skytemple_files_test/script/ssb/jump_test.py @@ -0,0 +1,103 @@ +# Copyright 2020-2025 Capypara and the SkyTemple Contributors +# +# This file is part of SkyTemple. +# +# SkyTemple is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# SkyTemple is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with SkyTemple. If not, see . +import unittest + +from explorerscript.ssb_converting.ssb_data_types import SsbOperation + +from skytemple_files.common.ppmdu_config.xml_reader import Pmd2XmlReader +from skytemple_files.script.ssb.script_compiler import ScriptCompiler + +# This is the jump test from ExplorerScript ported to skytemple-files to also test offset rewriting. + +LOOPING_SCRIPT = """ +def 0 { + @foo; + me_Stop(); + jump @foo; + end; +} +""" + +SELF_LOOPING_SCRIPT = """ +def 0 { + @foo; + jump @foo; + end; +} +""" + + +class StringTestCase(unittest.TestCase): + """ + Tests compilation of jumps. + """ + + def test_looping_exps(self) -> None: + routine_ops = self.compile_exps(LOOPING_SCRIPT) + self.assertEqual(1, len(routine_ops)) + routine = routine_ops[0] + self.assertEqual(3, len(routine)) + + for op in routine_ops[0]: + print(op) + + # FIRST OP + op_to_check = routine[0] + self.assertEqual("me_Stop", op_to_check.op_code.name) + self.assertEqual(5, op_to_check.offset) + self.assertEqual(0, len(op_to_check.params)) + + # SECOND OP + op_to_check = routine[1] + self.assertEqual("Jump", op_to_check.op_code.name) + self.assertEqual(6, op_to_check.offset) + self.assertEqual(1, len(op_to_check.params)) + self.assertIs(5, op_to_check.params[0]) + + # THIRD OP + op_to_check = routine[2] + self.assertEqual("End", op_to_check.op_code.name) + self.assertEqual(8, op_to_check.offset) + self.assertEqual(0, len(op_to_check.params)) + + def test_self_looping_exps(self) -> None: + routine_ops = self.compile_exps(SELF_LOOPING_SCRIPT) + + self.assertEqual(1, len(routine_ops)) + routine = routine_ops[0] + self.assertEqual(2, len(routine)) + + # FIRST OP + op_to_check = routine[0] + self.assertEqual("Jump", op_to_check.op_code.name) + self.assertEqual(5, op_to_check.offset) + self.assertEqual(1, len(op_to_check.params)) + self.assertIs(5, op_to_check.params[0]) + + # SECOND OP + op_to_check = routine[1] + self.assertEqual("End", op_to_check.op_code.name) + self.assertEqual(7, op_to_check.offset) + self.assertEqual(0, len(op_to_check.params)) + + def compile_exps(self, src: str) -> list[list[SsbOperation]]: + static_data = Pmd2XmlReader.load_default() + compiler = ScriptCompiler(static_data) + + ssb_after = compiler.compile_explorerscript(src, "")[0] + + return ssb_after.routine_ops