Example #1
0
 def add_linked_to_names_to_routine_ops(self):
     for _, r in self.routine_info:
         try:
             if r.type == SsbRoutineType.ACTOR:
                 r.linked_to_name = SsbConstant.create_for(self._scriptdata.level_entities__by_id[r.linked_to]).name
             elif r.type == SsbRoutineType.OBJECT:
                 r.linked_to_name = SsbConstant.create_for(self._scriptdata.objects__by_id[r.linked_to]).name
         except KeyError:
             pass
Example #2
0
    def _assert_same_vertex(self, i, self_v: 'Vertex', other_v: 'Vertex'):
        if self_v is None or other_v is None:
            assert self_v == other_v, f"Both must be None {self._r_info(i)}"
            return

        self_op: SsbOperation = self_v['op']
        other_op: SsbOperation = other_v['op']
        # We can't really check foreign jumps
        if isinstance(self_op, SsbForeignLabel) or isinstance(
                other_op, SsbForeignLabel):
            assert isinstance(self_op, SsbForeignLabel) and isinstance(other_op, SsbForeignLabel), \
                f"If one is foreign label, both must be ({self._r_info(i)})."
            return
        # If this is a label jump, take root.
        if hasattr(self_op, 'root'):
            self_op = self_op.root
        if hasattr(other_op, 'root'):
            other_op = other_op.root

        # OPCODES EXCEPTIONS
        # We replace flag_CalcValue with the ASSIGN operator with flag_Set
        if self_op.op_code.name == OPS_FLAG__CALC_VALUE and self_op.params[
                1] == SsbCalcOperator.ASSIGN.value:
            self_op.op_code = SsbOpCode(-1, OPS_FLAG__SET)
            self_op.params = [self_op.params[0], self_op.params[2]]
        if other_op.op_code.name == OPS_FLAG__CALC_VALUE and other_op.params[
                1] == SsbCalcOperator.ASSIGN.value:
            other_op.op_code = SsbOpCode(-1, OPS_FLAG__SET)
            other_op.params = [other_op.params[0], other_op.params[2]]
        # We replace BranchBit + PERFORMANCE_PROGRESS_LIST with BranchPerformance
        if self_op.op_code.name == OP_BRANCH_BIT and self_op.params[
                0].name == SsbConstant.create_for(
                    self._variables_by_name['PERFORMANCE_PROGRESS_LIST']).name:
            self_op.op_code.name = SsbOpCode(-1, OP_BRANCH_PERFORMANCE)
            self_op.params = [self_op.params[1], 1]
        if other_op.op_code.name == OP_BRANCH_BIT and other_op.params[
                0].name == SsbConstant.create_for(
                    self._variables_by_name['PERFORMANCE_PROGRESS_LIST']).name:
            other_op.op_code.name = SsbOpCode(-1, OP_BRANCH_PERFORMANCE)
            other_op.params = [other_op.params[1], 1]

        assert self_op.op_code.name == other_op.op_code.name, f"Opcodes were not the same: {self_op.op_code.name} " \
                                                              f"vs. {other_op.op_code.name} [{self_v.index}," \
                                                              f"{other_v.index}] ({self._r_info(i)})."

        self_op_params = self_op.params
        other_op_params = other_op.params
        # PARAMETER EXCEPTIONS:
        # We force BranchVariation to be a boolean, because the game seems to treat it as such too.
        if self_op.op_code.name == OP_BRANCH_VARIATION:
            if self_op_params[0] > 1:
                self_op_params[0] = 1
            if other_op_params[0] > 1:
                other_op_params[0] = 1

        assert self_op_params == other_op_params, f"Parameters of opcode ({self_op.op_code.name}) [{self_v.index}," \
                                                  f"{other_v.index}] are not the same ({self._r_info(i)})."
    def compile_explorerscript(self, es_src: str, exps_absolue_path: str,
                               callback_after_parsing: Callable = None,
                               lookup_paths: List[str] = None) -> Tuple[Ssb, SourceMap]:
        """
        Compile ExplorerScript into a SSB model. Returns the Ssb model, the source map, and a list of macros
        that were used in the ExplorerScript file.

        lookup_paths is the list of include lookup paths.

        :raises: ParseError: On parsing errors
        :raises: SsbCompilerError: On logical compiling errors (eg. unknown opcodes)
        :raises: ValueError: On misc. logical compiling errors (eg. unknown constants)
        """
        logger.debug("Compiling ExplorerScript (size: %d, path: %s)...", len(es_src), exps_absolue_path)

        base_compiler = ExplorerScriptSsbCompiler(
            SsbConstant.create_for(self.rom_data.script_data.game_variables__by_name['PERFORMANCE_PROGRESS_LIST']).name,
            lookup_paths
        )
        base_compiler.compile(es_src, exps_absolue_path)

        # Profiling callback
        if callback_after_parsing:
            callback_after_parsing()

        return self.compile_structured(
            base_compiler.routine_infos, base_compiler.routine_ops, base_compiler.named_coroutines,
            base_compiler.source_map
        )
Example #4
0
 def to_explorerscript(self) -> Tuple[str, SourceMap]:
     self.add_linked_to_names_to_routine_ops()
     return ExplorerScriptSsbDecompiler(
         [x[1] for x in self.routine_info],
         self.get_filled_routine_ops(),
         self._scriptdata.common_routine_info__by_id,
         SsbConstant.create_for(self._scriptdata.game_variables__by_name['PERFORMANCE_PROGRESS_LIST']).name,
         SsbConstant.get_dungeon_mode_constants()
     ).convert()
Example #5
0
 def get_filled_routine_ops(self):
     """Returns self.routine_ops, but with constant strings, strings and constants from scriptdata filled out"""
     logger.debug("Disassembling SSB model data...")
     rtns: List[List[SkyTempleSsbOperation]] = []
     pos_marker_increment = 0
     for rtn in self.routine_ops:
         rtn_ops = []
         for op in rtn:
             new_params = []
             skip_arguments = 0
             for i, param in enumerate(op.params):
                 if skip_arguments > 0:
                     skip_arguments -= 1
                     continue
                 argument_spec = self._get_argument_spec(op.op_code, i)
                 if argument_spec is not None:
                     if argument_spec.type == 'uint':
                         # TODO: Do unsigned parameters actually exist? If so are they also 14bit?
                         new_params.append(param)
                     elif argument_spec.type == 'sint':
                         # 14 bit signed int.
                         if param & 0x4000:
                             param = -0x8000 + param
                         new_params.append(param)
                     elif argument_spec.type == 'Entity':
                         new_params.append(SsbConstant.create_for(self._scriptdata.level_entities__by_id[param]))
                     elif argument_spec.type == 'Object':
                         new_params.append(SsbConstant.create_for(self._scriptdata.objects__by_id[param]))
                     elif argument_spec.type == 'Routine':
                         new_params.append(SsbConstant.create_for(self._scriptdata.common_routine_info__by_id[param]))
                     elif argument_spec.type == 'Face':
                         if param in self._scriptdata.face_names__by_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.face_names__by_id[param]))
                         else:
                             logger.warning(f"Unknown face id: {param}")
                             new_params.append(param)
                     elif argument_spec.type == 'FaceMode':
                         new_params.append(SsbConstant.create_for(self._scriptdata.face_position_modes__by_id[param]))
                     elif argument_spec.type == 'GameVar':
                         new_params.append(SsbConstant.create_for(self._scriptdata.game_variables__by_id[param]))
                     elif argument_spec.type == 'Level':
                         if param in self._scriptdata.level_list__by_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.level_list__by_id[param]))
                         else:
                             logger.warning(f"Unknown level id: {param}")
                             new_params.append(param)
                     elif argument_spec.type == 'Menu':
                         if param in self._scriptdata.menus__by_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.menus__by_id[param]))
                         else:
                             logger.warning(f"Unknown menu id: {param}")
                             new_params.append(param)
                     elif argument_spec.type == 'ProcessSpecial':
                         if param in self._scriptdata.process_specials__by_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.process_specials__by_id[param]))
                         else:
                             new_params.append(param)
                             logger.warning(f"Unknown special process id: {param}")
                     elif argument_spec.type == 'Direction':
                         if param in self._scriptdata.directions__by_ssb_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.directions__by_ssb_id[param]))
                         else:
                             new_params.append(param)
                             logger.warning(f"Unknown direction id: {param}")
                     elif argument_spec.type == 'Bgm':
                         if param in self._scriptdata.bgms__by_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.bgms__by_id[param]))
                         else:
                             logger.warning(f"Unknown BGM id: {param}")
                             new_params.append(param)
                     elif argument_spec.type == 'Effect':
                         if param in self._scriptdata.sprite_effects__by_id:
                             new_params.append(SsbConstant.create_for(self._scriptdata.sprite_effects__by_id[param]))
                         else:
                             logger.warning(f"Unknown effect id: {param}")
                             new_params.append(param)
                     elif argument_spec.type == 'String':
                         try:
                             new_params.append(SsbOpParamLanguageString(self.get_single_string(param - len(self.constants))))
                         except IndexError:
                             # Fall back to const table
                             new_params.append(SsbOpParamConstString(self.constants[param]))
                     elif argument_spec.type == 'ConstString':
                         try:
                             new_params.append(SsbOpParamConstString(self.constants[param]))
                         except IndexError:
                             # Fall back to lang string
                             new_params.append(SsbOpParamLanguageString(self.get_single_string(param - len(self.constants))))
                     elif argument_spec.type == 'PositionMark':
                         x_offset = y_offset = x_relative = y_relative = 0
                         try:
                             x_offset = param
                             y_offset = op.params[i + 1]
                             x_relative = op.params[i + 2]
                             y_relative = op.params[i + 3]
                         except IndexError:
                             logger.warning("SSB had wrong number of arguments for building a position marker.")
                         new_params.append(SsbOpParamPositionMarker(
                             f'm{pos_marker_increment}', x_offset, y_offset, x_relative, y_relative
                         ))
                         pos_marker_increment += 1
                         skip_arguments = 3
                     else:
                         raise RuntimeError(f"Unknown argument type '{argument_spec.type}'")
                 else:
                     raise RuntimeError(f"Missing argument spec for argument #{i} for OpCode {op.op_code.name}")
             new_op = SkyTempleSsbOperation(op.offset, op.op_code, new_params)
             rtn_ops.append(new_op)
         rtns.append(rtn_ops)
     return rtns