Exemple #1
0
        def prepare_act2_output(self):
            """
            Prepare this node for outputting.
            This sets the feature and makes sure it is correct.

            @return: True iff parsing of this node is needed
            @rtype: C{bool}
            """
            if not cls_is_referenced:
                return True
            if not self._prepared:
                self._prepared = True
                # copy, since we're going to modify
                ref_nodes = self._referencing_nodes.copy()
                for node in ref_nodes:
                    used = node.prepare_act2_output()
                    if not used:
                        node._remove_reference(self)

                # now determine the feature
                if self._has_explicit_feature():
                    # by this time, feature should be set
                    assert len(self.feature_set) == 1
                    for n in self._referencing_nodes:
                        if n.feature_set != self.feature_set:
                            msg = "Cannot refer to block '{}' with feature '{}', expected feature is '{}'"
                            msg = msg.format(
                                self.name.value,
                                general.feature_name(
                                    next(iter(self.feature_set))),
                                general.feature_name(
                                    n.feature_set.difference(
                                        self.feature_set).pop()),
                            )
                            raise generic.ScriptError(msg, n.pos)

                elif len(self._referencing_nodes) != 0:
                    for n in self._referencing_nodes:
                        # Add the features from all calling blocks to the set
                        self.feature_set.update(n.feature_set)

                if len(self._referencing_nodes) == 0 and (
                        not self.optimised or self.optimised is self):
                    # if we can be 'not used', there ought to be a way to refer to this block
                    assert self.name is not None
                    generic.print_warning(
                        generic.Warning.OPTIMISATION,
                        "Block '{}' is not referenced, ignoring.".format(
                            self.name.value),
                        self.pos,
                    )

            return len(self._referencing_nodes) != 0
Exemple #2
0
    def preprocess_storageop(self, expr):
        assert isinstance(expr, expression.StorageOp)
        if expr.info["perm"] and self.feature not in (0x08, 0x0A, 0x0D):
            raise generic.ScriptError(
                "Persistent storage is not supported for feature '{}'".format(
                    general.feature_name(self.feature)),
                expr.pos,
            )

        if expr.info["store"]:
            op = nmlop.STO_PERM if expr.info["perm"] else nmlop.STO_TMP
            ret = expression.BinOp(op, expr.value, expr.register, expr.pos)
        else:
            var_num = 0x7C if expr.info["perm"] else 0x7D
            ret = expression.Variable(expression.ConstantNumeric(var_num),
                                      param=expr.register,
                                      pos=expr.pos)

        if expr.info["perm"] and self.feature == 0x08:
            # store grfid in register 0x100 for town persistent storage
            grfid = expression.ConstantNumeric(
                0xFFFFFFFF if expr.grfid is None else expression.
                parse_string_to_dword(expr.grfid))
            store_op = nmlop.STO_TMP(grfid, 0x100, expr.pos)
            ret = nmlop.VAL2(store_op, ret)
        elif expr.grfid is not None:
            raise generic.ScriptError(
                "Specifying a grfid is only possible for town persistent storage.",
                expr.pos)
        return ret
Exemple #3
0
def get_property_info_list(feature, name):
    """
    Find information on a single property, based on feature and name/number

    @param feature: Feature of the associated item
    @type feature: C{int}

    @param name: Name (or number) of the property
    @type name: L{Identifier} or L{ConstantNumeric}

    @return: A list of dictionaries with property information
    @rtype: C{list} of C{dict}
    """
    # Validate feature
    assert feature in range(0, len(properties))  # guaranteed by item
    if properties[feature] is None:
        raise generic.ScriptError(
            "Setting properties for feature '{}' is not possible, no properties are defined."
            .format(general.feature_name(feature)),
            name.pos,
        )

    if isinstance(name, expression.Identifier):
        prop_name = name.value
        if prop_name not in properties[feature]:
            raise generic.ScriptError("Unknown property name: " + prop_name,
                                      name.pos)
        prop_info_list = properties[feature][prop_name]
        if not isinstance(prop_info_list, list):
            prop_info_list = [prop_info_list]
    elif isinstance(name, expression.ConstantNumeric):
        for prop_info_list in properties[feature].values():
            if not isinstance(prop_info_list, list):
                prop_info_list = [prop_info_list]
            # Only non-compound properties may be set by number
            if len(prop_info_list) == 1 and "num" in prop_info_list[
                    0] and prop_info_list[0]["num"] == name.value:
                break
        else:
            raise generic.ScriptError("Unknown property number: " + str(name),
                                      name.pos)
    else:
        raise AssertionError()

    for prop_info in prop_info_list:
        if "replaced_by" in prop_info:
            generic.print_warning(
                generic.Warning.DEPRECATION,
                "'{}' is deprecated, consider using '{}' instead".format(
                    prop_name, prop_info["replaced_by"]),
                name.pos,
            )
        if "warning" in prop_info:
            generic.print_warning(generic.Warning.GENERIC,
                                  prop_info["warning"], name.pos)
    return prop_info_list
Exemple #4
0
def get_property_info_list(feature, name):
    """
    Find information on a single property, based on feature and name/number

    @param feature: Feature of the associated item
    @type feature: C{int}

    @param name: Name (or number) of the property
    @type name: L{Identifier} or L{ConstantNumeric}

    @return: A list of dictionaries with property information
    @rtype: C{list} of C{dict}
    """
    global properties

    #Validate feature
    assert feature in range(0, len(properties))  #guaranteed by item
    if properties[feature] is None:
        raise generic.ScriptError(
            "Setting properties for feature '{}' is not possible, no properties are defined."
            .format(general.feature_name(feature)), name.pos)

    if isinstance(name, expression.Identifier):
        if not name.value in properties[feature]:
            raise generic.ScriptError("Unknown property name: " + name.value,
                                      name.pos)
        prop_info_list = properties[feature][name.value]
        if not isinstance(prop_info_list, list):
            prop_info_list = [prop_info_list]
    elif isinstance(name, expression.ConstantNumeric):
        for p in properties[feature]:
            prop_info_list = properties[feature][p]
            if not isinstance(prop_info_list, list):
                prop_info_list = [prop_info_list]
            # Only non-compound properties may be set by number
            if len(prop_info_list) == 1 and 'num' in prop_info_list[
                    0] and prop_info_list[0]['num'] == name.value:
                break
        else:
            raise generic.ScriptError("Unknown property number: " + str(name),
                                      name.pos)
    else:
        assert False

    for prop_info in prop_info_list:
        if 'warning' in prop_info:
            generic.print_warning(prop_info['warning'], name.pos)
    return prop_info_list
Exemple #5
0
    def preprocess_storageop(self, expr):
        assert isinstance(expr, expression.StorageOp)
        max = 0xF if expr.info['perm'] else 0x10F
        if isinstance(
                expr.register,
                expression.ConstantNumeric) and expr.register.value > max:
            raise generic.ScriptError(
                "Register number must be in range 0..{:d}, encountered {:d}.".
                format(max, expr.register.value), expr.pos)
        if expr.info['perm'] and self.feature not in (0x08, 0x0A, 0x0D):
            raise generic.ScriptError(
                "Persistent storage is not supported for feature '{}'".format(
                    general.feature_name(self.feature)), expr.pos)

        if expr.info['store']:
            op = nmlop.STO_PERM if expr.info['perm'] else nmlop.STO_TMP
            ret = expression.BinOp(op, expr.value, expr.register, expr.pos)
        else:
            var_num = 0x7C if expr.info['perm'] else 0x7D
            ret = expression.Variable(expression.ConstantNumeric(var_num),
                                      param=expr.register,
                                      pos=expr.pos)

        if expr.info['perm'] and self.feature == 0x08:
            # store grfid in register 0x100 for town persistent storage
            grfid = expression.ConstantNumeric(
                0xFFFFFFFF if expr.grfid is None else expression.
                parse_string_to_dword(expr.grfid))
            store_op = expression.BinOp(nmlop.STO_TMP, grfid,
                                        expression.ConstantNumeric(0x100),
                                        expr.pos)
            ret = expression.BinOp(nmlop.VAL2, store_op, ret, expr.pos)
        elif expr.grfid is not None:
            raise generic.ScriptError(
                "Specifying a grfid is only possible for town persistent storage.",
                expr.pos)
        return ret
Exemple #6
0
def get_layout_action2s(spritelayout, feature, spr_pos):
    """
    @param spr_pos: Position information of the sprite view.
    @type  spr_pos: L{Position}
    """
    ground_sprite = None
    building_sprites = []
    actions = []

    if feature not in action2.features_sprite_layout:
        raise generic.ScriptError(
            "Sprite layouts are not supported for feature '{}'.".format(
                general.feature_name(feature)))

    # Allocate registers
    param_map = {}
    param_registers = []
    for param in spritelayout.param_list:
        reg = action2var.VarAction2CallParam(param.value)
        param_registers.append(reg)
        param_map[param.value] = reg
    param_map = (param_map, lambda name, value, pos: action2var.
                 VarAction2LoadCallParam(value, name))
    spritelayout.register_map[feature] = param_registers

    # Reduce all expressions, can't do that earlier as feature is not known
    all_sprite_sets = []
    layout_sprite_list = []  # Create a new structure
    for layout_sprite in spritelayout.layout_sprite_list:
        param_list = []
        layout_sprite_list.append(
            (layout_sprite.type, layout_sprite.pos, param_list))
        for param in layout_sprite.param_list:
            param_val = action2var.reduce_varaction2_expr(
                param.value, action2var.get_scope(feature), [param_map])
            param_list.append((param.name, param_val))
            if isinstance(param_val, expression.SpriteGroupRef):
                spriteset = action2.resolve_spritegroup(param_val.name)
                if not spriteset.is_spriteset():
                    raise generic.ScriptError(
                        "Expected a reference to a spriteset.", param_val.pos)
                all_sprite_sets.append(spriteset)
    actions.extend(
        action1.add_to_action1(all_sprite_sets, feature, spritelayout.pos))

    temp_registers = []
    for type, pos, param_list in layout_sprite_list:
        if type.value not in layout_sprite_types:
            raise generic.ScriptError(
                "Invalid sprite type '{}' encountered. Expected 'ground', 'building', or 'childsprite'."
                .format(type.value),
                type.pos,
            )
        sprite = Action2LayoutSprite(feature, layout_sprite_types[type.value],
                                     pos, [param_map])
        for name, value in param_list:
            sprite.set_param(name, value)
        temp_registers.extend(sprite.get_all_registers())
        if sprite.type == Action2LayoutSpriteType.GROUND:
            if ground_sprite is not None:
                raise generic.ScriptError(
                    "Sprite layout can have no more than one ground sprite",
                    spritelayout.pos)
            ground_sprite = sprite
        else:
            building_sprites.append(sprite)

    if ground_sprite is None:
        if len(building_sprites) == 0:
            # no sprites defined at all, that's not very much.
            raise generic.ScriptError(
                "Sprite layout requires at least one sprite", spr_pos)
        # set to 0 for no ground sprite
        ground_sprite = Action2LayoutSprite(feature,
                                            Action2LayoutSpriteType.GROUND)
        ground_sprite.set_param(expression.Identifier("sprite"),
                                expression.ConstantNumeric(0))

    action6.free_parameters.save()
    act6 = action6.Action6()

    advanced = any(x.is_advanced_sprite()
                   for x in building_sprites + [ground_sprite])

    offset = 4
    sprite_num = ground_sprite.get_sprite_number()
    sprite_num, offset = actionD.write_action_value(sprite_num, actions, act6,
                                                    offset, 4)
    if advanced:
        offset += ground_sprite.get_registers_size()

    for sprite in building_sprites:
        sprite_num = sprite.get_sprite_number()
        sprite_num, offset = actionD.write_action_value(
            sprite_num, actions, act6, offset, 4)
        if advanced:
            offset += sprite.get_registers_size()
        offset += 3 if sprite.type == Action2LayoutSpriteType.CHILD else 6

    if len(act6.modifications) > 0:
        actions.append(act6)

    layout_action = Action2Layout(
        feature,
        spritelayout.name.value + " - feature {:02X}".format(feature),
        spritelayout.pos,
        ground_sprite,
        building_sprites,
        param_registers,
    )
    actions.append(layout_action)

    if temp_registers:
        varact2parser = action2var.Varaction2Parser(
            feature, action2var.get_scope(feature))
        for register_info in temp_registers:
            reg, expr = register_info[1], register_info[2]
            if reg is None:
                continue
            varact2parser.parse_expr(expr)
            varact2parser.var_list.append(nmlop.STO_TMP)
            varact2parser.var_list.append(reg)
            varact2parser.var_list.append(nmlop.VAL2)
            varact2parser.var_list_size += reg.get_size() + 2

    # Only continue if we actually needed any new registers
    if temp_registers and varact2parser.var_list:  # lgtm[py/uninitialized-local-variable]
        # Remove the last VAL2 operator
        varact2parser.var_list.pop()
        varact2parser.var_list_size -= 1

        actions.extend(varact2parser.extra_actions)
        extra_act6 = action6.Action6()
        for mod in varact2parser.mods:
            extra_act6.modify_bytes(mod.param, mod.size, mod.offset + 4)
        if len(extra_act6.modifications) > 0:
            actions.append(extra_act6)

        varaction2 = action2var.Action2Var(
            feature,
            "{}@registers - feature {:02X}".format(spritelayout.name.value,
                                                   feature), spritelayout.pos,
            0x89)
        varaction2.var_list = varact2parser.var_list
        ref = expression.SpriteGroupRef(spritelayout.name, [], None,
                                        layout_action)
        varaction2.ranges.append(
            action2var.VarAction2Range(expression.ConstantNumeric(0),
                                       expression.ConstantNumeric(0), ref, ""))
        varaction2.default_result = ref
        varaction2.default_comment = ""

        # Add two references (default + range)
        # Make sure that registers allocated here are not used by the spritelayout
        action2.add_ref(ref, varaction2, True)
        action2.add_ref(ref, varaction2, True)
        spritelayout.set_action2(varaction2, feature)
        actions.append(varaction2)
    else:
        spritelayout.set_action2(layout_action, feature)

    action6.free_parameters.restore()
    return actions
Exemple #7
0
def get_real_action2s(spritegroup, feature):
    loaded_list = []
    loading_list = []
    actions = []

    if feature not in action2.features_sprite_group:
        raise generic.ScriptError(
            "Sprite groups that combine sprite sets are not supported for feature '{}'."
            .format(general.feature_name(feature)),
            spritegroup.pos,
        )

    # First make sure that all referenced real sprites are put in a single action1
    spriteset_list = []
    for view in spritegroup.spriteview_list:
        spriteset_list.extend([
            action2.resolve_spritegroup(sg_ref.name)
            for sg_ref in view.spriteset_list
        ])
    actions.extend(
        action1.add_to_action1(spriteset_list, feature, spritegroup.pos))

    view_names = sorted(view.name.value
                        for view in spritegroup.spriteview_list)
    if feature in (0x00, 0x01, 0x02, 0x03):
        if view_names != sorted(["loading", "loaded"]):
            raise generic.ScriptError(
                "Expected a 'loading' and a 'loaded' (list of) sprite set(s).",
                spritegroup.pos)
    elif feature in (0x05, 0x0B, 0x0D, 0x10):
        msg = (
            "Sprite groups for feature {:02X} will not be supported in the future, as they are no longer needed."
            " Directly refer to sprite sets instead.").format(feature)
        generic.print_warning(msg, spritegroup.pos)
        if view_names != ["default"]:
            raise generic.ScriptError(
                "Expected only a 'default' (list of) sprite set(s).",
                spritegroup.pos)

    for view in spritegroup.spriteview_list:
        if len(view.spriteset_list) == 0:
            raise generic.ScriptError(
                "Expected at least one sprite set, encountered 0.", view.pos)
        for set_ref in view.spriteset_list:
            spriteset = action2.resolve_spritegroup(set_ref.name)
            action1_index = action1.get_action1_index(spriteset)
            if view.name.value == "loading":
                loading_list.append(action1_index)
            else:
                loaded_list.append(action1_index)

    actions.append(
        Action2Real(
            feature,
            spritegroup.name.value + " - feature {:02X}".format(feature),
            spritegroup.pos,
            loaded_list,
            loading_list,
        ))
    spritegroup.set_action2(actions[-1], feature)
    return actions