Beispiel #1
0
def create_proc_call_varaction2(feature, proc, ret_value_function, pos):
    """
    Create a varaction2 that executes a procedure call and applies a function on the result

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

    @param proc: Procedure to execute
    @type proc: L{SpriteGroupRef}

    @param ret_value_function: Function to apply on the result (L{Expression} -> L{Expression})
    @type ret_value_function: C{function}

    @param pos: Positional context.
    @type  pos: L{Position}

    @return: A list of extra actions, reference to the created action2 and a comment to add
    @rtype: C{tuple} of (C{list} of L{BaseAction}, L{SpriteGroupRef}, C{str})
    """
    varact2parser = action2var.Varaction2Parser(feature)
    varact2parser.parse_proc_call(proc)

    mapping = {0xFFFF: (expression.SpriteGroupRef(expression.Identifier('CB_FAILED'), [], None), None)}
    default = ret_value_function(expression.Variable(expression.ConstantNumeric(0x1C)))
    action_list, result = create_intermediate_varaction2(feature, varact2parser, mapping, default, pos)
    comment = result.name.value + ';'
    return (action_list, result, comment)
Beispiel #2
0
 def t_ID(self, t):
     r"[a-zA-Z_][a-zA-Z0-9_]*"
     if t.value in reserved:  # Check for reserved words
         t.type = reserved[t.value]
     else:
         t.type = "ID"
         t.value = expression.Identifier(t.value, t.lineno)
     return t
Beispiel #3
0
    def pre_process(self):
        if len(self.args) not in (1, 2):
            raise generic.ScriptError("engine_override expects 1 or 2 parameters", self.pos)

        if len(self.args) == 1:
            try:
                self.source_grfid = expression.Identifier('GRFID').reduce(global_constants.const_list).value
                assert isinstance(self.source_grfid, int)
            except generic.ScriptError:
                raise generic.ScriptError("GRFID of this grf is required, but no grf-block is defined.", self.pos)
        else:
            self.source_grfid = expression.parse_string_to_dword(self.args[0].reduce(global_constants.const_list))

        self.grfid = expression.parse_string_to_dword(self.args[-1].reduce(global_constants.const_list))
Beispiel #4
0
def reduce_varaction2_expr(expr, feature, extra_dicts=[]):
    # 'normal' and 60+x variables to use
    vars_normal = action2var_variables.varact2vars[feature]
    vars_60x = action2var_variables.varact2vars60x[feature]

    # lambda function to convert (value, pos) to a function pointer
    # since we need the variable name later on, a reverse lookup is needed
    # TODO pass the function name along to avoid this
    func60x = lambda value, pos: expression.FunctionPtr(
        expression.Identifier(generic.reverse_lookup(vars_60x, value), pos),
        parse_60x_var, value)

    return expr.reduce(extra_dicts + [(action2var_variables.varact2_globalvars, parse_var), \
        (vars_normal, parse_var), \
        (vars_60x, func60x)] + \
        global_constants.const_list)
Beispiel #5
0
def make_empty_layout_action2(feature, pos):
    """
    Make an empty layout action2
    For use with failed callbacks

    @param feature: Feature of the sprite layout to create
    @type feature: C{int}

    @param pos: Positional context.
    @type  pos: L{Position}

    @return: The created sprite layout action2
    @rtype: L{Action2Layout}
    """
    ground_sprite = Action2LayoutSprite(feature, Action2LayoutSpriteType.GROUND)
    ground_sprite.set_param(expression.Identifier('sprite'), expression.ConstantNumeric(0))
    return Action2Layout(feature, "@CB_FAILED_LAYOUT{:02X}".format(feature), pos, ground_sprite, [], [])
Beispiel #6
0
def create_return_action(expr, feature, name, var_range):
    """
    Create a varaction2 to return the computed value

    @param expr: Expression to return
    @type expr: L{Expression}

    @param feature: Feature of the switch-block
    @type feature: C{int}

    @param name: Name of the new varaction2
    @type name: C{str}

    @return: A tuple of two values:
                - Action list to prepend
                - Reference to the created varaction2
    @rtype: C{tuple} of (C{list} of L{BaseAction}, L{SpriteGroupRef})
    """
    varact2parser = Varaction2Parser(
        feature if var_range ==
        0x89 else action2var_variables.varact2parent_scope[feature])
    varact2parser.parse_expr(expr)

    action_list = 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:
        action_list.append(extra_act6)

    varaction2 = Action2Var(feature, name, expr.pos, var_range)
    varaction2.var_list = varact2parser.var_list
    varaction2.default_result = expression.ConstantNumeric(
        0)  # Bogus result, it's the nvar == 0 that matters
    varaction2.default_comment = "Return computed value"

    for proc in varact2parser.proc_call_list:
        action2.add_ref(proc, varaction2, True)

    ref = expression.SpriteGroupRef(expression.Identifier(name), [], None,
                                    varaction2)
    action_list.append(varaction2)
    return (action_list, ref)
Beispiel #7
0
    def move_pieces(self):
        """
        Move pieces to new action F instances to make it fit, if needed.

        @return: Created action F instances.
        @rtype:  C{list} of L{ActionF}

        @note: Function may change L{pieces}.
        """
        global townname_serial

        if len(self.pieces) <= 255:
            return []  # Trivially correct.

        # There are too many pieces.
        number_action_f = (len(self.pieces) + 254) // 255
        pow2 = 1
        while pow2 < number_action_f:
            pow2 = pow2 * 2
        if pow2 < 255: number_action_f = pow2

        heap = []  # Heap of (summed probability, subset-of-pieces)
        i = 0
        while i < number_action_f:
            # Index 'i' is added to have a unique sorting when lists have equal total probabilities.
            heapq.heappush(heap, (0, i, []))
            i = i + 1

        finished_actions = []
        # Index 'idx' is added to have a unique sorting when pieces have equal probabilities.
        rev_pieces = sorted(((p.probability.value, idx, p)
                             for idx, p in enumerate(self.pieces)),
                            reverse=True)
        for prob, _idx, piece in rev_pieces:
            while True:
                sub = heapq.heappop(heap)
                if len(sub[2]) < 255:
                    break
                # If a subset already has the max number of parts, consider it finished.
                finished_actions.append(sub)

            sub[2].append(piece)
            sub = (sub[0] + prob, sub[1], sub[2])
            heapq.heappush(heap, sub)

        finished_actions.extend(heap)

        # To ensure the chances do not get messed up due to one part needing less bits for its
        # selection, all parts are forced to use the same number of bits.
        max_prob = max(sub[0] for sub in finished_actions)
        num_bits = 1
        while (1 << num_bits) < max_prob:
            num_bits = num_bits + 1

        # Assign to action F
        actFs = []
        for _prob, _idx, sub in finished_actions:
            actF_name = expression.Identifier(
                "**townname #{:d}**".format(townname_serial), None)
            townname_serial = townname_serial + 1
            town_part = TownNamesPart(sub, self.pos)
            town_part.set_numbits(num_bits)
            actF = actionF.ActionF(actF_name, None, None, [town_part],
                                   self.pos)
            actFs.append(actF)

            # Remove pieces of 'sub' from self.pieces
            counts = len(self.pieces), len(sub)
            sub_set = set(sub)
            self.pieces = [
                piece for piece in self.pieces if piece not in sub_set
            ]
            assert len(self.pieces) == counts[0] - counts[1]

            self.pieces.append(
                TownNamesEntryDefinition(actF_name,
                                         expression.ConstantNumeric(1),
                                         self.pos))

        # update self.parts
        return actFs
Beispiel #8
0
def create_intermediate_varaction2(feature, varact2parser, mapping, default, pos):
    """
    Create a varaction2 based on a parsed expression and a value mapping

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

    @param varact2parser: Parser containing a parsed expression
    @type varact2parser: L{Varaction2Parser}

    @param mapping: Mapping of various values to sprite groups / return values, with a possible extra function to apply to the return value
    @type mapping: C{dict} that maps C{int} to C{tuple} of (L{SpriteGroupRef}, C{function}, or C{None})

    @param default: Default sprite group if no value matches
    @type default: L{SpriteGroupRef}

    @param pos: Positional context.
    @type  pos: L{Position}

    @return: A tuple containing the action list and a reference to the created action2
    @rtype: C{tuple} of (C{list} of L{BaseAction}, L{SpriteGroupRef})
    """
    global action2_id

    action_list = varact2parser.extra_actions
    act6 = action6.Action6()
    for mod in varact2parser.mods:
        act6.modify_bytes(mod.param, mod.size, mod.offset + 4)

    name = expression.Identifier("@action3_{:d}".format(action2_id))
    action2_id += 1
    varaction2 = action2var.Action2Var(feature, name.value, pos, 0x89)
    varaction2.var_list = varact2parser.var_list
    offset = 5 + varact2parser.var_list_size
    for proc in varact2parser.proc_call_list:
        action2.add_ref(proc, varaction2, True)

    for switch_value in sorted(mapping):
        return_value, ret_value_function = mapping[switch_value]
        if ret_value_function is None:
            result, comment = action2var.parse_result(return_value, action_list, act6, offset, varaction2, None, 0x89)
        else:
            if isinstance(return_value, expression.SpriteGroupRef):
                # We need to execute the callback via a procedure call
                # then return CB_FAILED if the CB failed,
                # or the CB result (with ret_value_function applied) if successful
                if return_value.name.value == 'CB_FAILED':
                    result, comment = action2var.parse_result(return_value, action_list, act6, offset, varaction2, None, 0x89)
                else:
                    extra_actions, result, comment = create_proc_call_varaction2(feature, return_value, ret_value_function, pos)
                    action_list.extend(extra_actions)
            else:
                return_value = ret_value_function(return_value).reduce()
                result, comment = action2var.parse_result(return_value, action_list, act6, offset, varaction2, None, 0x89)

        varaction2.ranges.append(action2var.VarAction2Range(expression.ConstantNumeric(switch_value), expression.ConstantNumeric(switch_value), result, comment))
        offset += 10
    result, comment = action2var.parse_result(default, action_list, act6, offset, varaction2, None, 0x89)
    varaction2.default_result = result
    varaction2.default_comment = comment

    return_ref = expression.SpriteGroupRef(name, [], None, varaction2)
    if len(act6.modifications) > 0: action_list.append(act6)
    action_list.append(varaction2)
    return (action_list, return_ref)
Beispiel #9
0
def parse_graphics_block_single_id(graphics_block, feature, id, is_livery_override, house_tile = None, house_north_tile_id = None):
    action6.free_parameters.save()
    prepend_action_list = []
    action_list = []
    act6 = action6.Action6()
    act3 = create_action3(feature, id, action_list, act6, is_livery_override)

    cargo_gfx = {}
    seen_callbacks = set()
    callbacks = []
    livery_override = None # Used for rotor graphics

    for graphics in graphics_block.graphics_list:
        cargo_id = graphics.cargo_id
        if isinstance(cargo_id, expression.Identifier):
            cb_name = cargo_id.value
            cb_table = action3_callbacks.callbacks[feature]
            if cb_name in cb_table:
                if cb_name in seen_callbacks:
                    raise generic.ScriptError("Callback '{}' is defined multiple times.".format(cb_name), cargo_id.pos)
                seen_callbacks.add(cb_name)

                info_list = cb_table[cb_name]
                if not isinstance(info_list, list):
                    info_list = [info_list]

                for info in info_list:
                    if 'deprecate_message' in info:
                        generic.print_warning(info['deprecate_message'], cargo_id.pos)
                    if house_tile is not None and 'tiles' in info and house_tile not in info['tiles']:
                        continue

                    if info['type'] == 'cargo':
                        # Not a callback, but an alias for a certain cargo type
                        if info['num'] in cargo_gfx:
                            raise generic.ScriptError("Graphics for '{}' are defined multiple times.".format(cb_name), cargo_id.pos)
                        cargo_gfx[info['num']] = graphics.result.value
                    elif info['type'] == 'cb':
                        callbacks.append( (info, graphics.result.value) )
                    elif info['type'] == 'override':
                        assert livery_override is None
                        livery_override = graphics.result.value
                    else:
                        assert False
                continue

        # Not a callback, so it must be a 'normal' cargo (vehicles/stations only)
        cargo_id = cargo_id.reduce_constant(global_constants.const_list)
        # Raise the error only now, to let the 'unknown identifier' take precedence
        if feature >= 5: raise generic.ScriptError("Associating graphics with a specific cargo is possible only for vehicles and stations.", cargo_id.pos)
        if cargo_id.value in cargo_gfx:
            raise generic.ScriptError("Graphics for cargo {:d} are defined multiple times.".format(cargo_id.value), cargo_id.pos)
        cargo_gfx[cargo_id.value] = graphics.result.value

    if graphics_block.default_graphics is not None:
        if 'default' not in action3_callbacks.callbacks[feature]:
            raise generic.ScriptError("Default graphics may not be defined for this feature (0x{:02X}).".format(feature), graphics_block.default_graphics.pos)
        if None in cargo_gfx:
            raise generic.ScriptError("Default graphics are defined twice.", graphics_block.default_graphics.pos)
        cargo_gfx[None] = graphics_block.default_graphics.value

    # An in-between varaction2 is always needed for houses
    if len(callbacks) != 0 or feature == 0x07:
        cb_flags = 0
        # Determine the default value
        if None not in cargo_gfx:
            cargo_gfx[None] = expression.SpriteGroupRef(expression.Identifier('CB_FAILED', None), [], None)
        default_val = cargo_gfx[None]

        cb_mapping = {}
        cb_buy_mapping = {}
        # Special case for vehicle cb 36, maps var10 values to spritegroups
        cb36_mapping = {}
        cb36_buy_mapping = {}
        # Sspecial case for industry production CB, maps var18 values to spritegroups
        prod_cb_mapping = {}

        for cb_info, gfx in callbacks:
            if 'flag_bit' in cb_info:
                # Set a bit in the CB flags property
                cb_flags |= 1 << cb_info['flag_bit']

            value_function = cb_info.get('value_function', None)
            mapping_val = (gfx, value_function)

            # See action3_callbacks for info on possible values
            purchase = cb_info.get('purchase', 0)
            if isinstance(purchase, str):
                # Not in purchase list, if separate purchase CB is set
                purchase = 0 if purchase in seen_callbacks else 1
            # Explicit purchase CBs will need a purchase cargo, even if not needed for graphics
            if purchase == 2 and 0xFF not in cargo_gfx:
                cargo_gfx[0xFF] = default_val

            num = cb_info['num']
            if num == 0x36:
                if purchase != 2: cb36_mapping[cb_info['var10']] = mapping_val
                if purchase != 0: cb36_buy_mapping[cb_info['var10']] = mapping_val
            elif feature == 0x0A and num == 0x00:
                # Industry production CB
                assert purchase == 0
                prod_cb_mapping[cb_info['var18']] = mapping_val
            else:
                if purchase != 2: cb_mapping[num] = mapping_val
                if purchase != 0: cb_buy_mapping[num] = mapping_val

        if cb_flags != 0:
            prepend_action_list.extend(action0.get_callback_flags_actions(feature, id, cb_flags))

        # Handle CB 36
        if len(cb36_mapping) != 0:
            expr = expression.Variable(expression.ConstantNumeric(0x10), mask = expression.ConstantNumeric(0xFF))
            actions, cb36_ref = create_cb_choice_varaction2(feature, expr, cb36_mapping, default_val, graphics_block.pos)
            prepend_action_list.extend(actions)
            cb_mapping[0x36] = (cb36_ref, None)
        if len(cb36_buy_mapping) != 0:
            expr = expression.Variable(expression.ConstantNumeric(0x10), mask = expression.ConstantNumeric(0xFF))
            actions, cb36_ref = create_cb_choice_varaction2(feature, expr, cb36_buy_mapping, default_val, graphics_block.pos)
            prepend_action_list.extend(actions)
            cb_buy_mapping[0x36] = (cb36_ref, None)
        if len(prod_cb_mapping) != 0:
            expr = expression.Variable(expression.ConstantNumeric(0x18), mask = expression.ConstantNumeric(0xFF))
            actions, cb_ref = create_cb_choice_varaction2(feature, expr, prod_cb_mapping, default_val, graphics_block.pos)
            prepend_action_list.extend(actions)
            cb_mapping[0x00] = (cb_ref, None)

        for cargo in sorted(cargo_gfx, key=lambda x: -1 if x is None else x):
            mapping = cb_buy_mapping if cargo == 0xFF else cb_mapping
            if len(mapping) == 0 and feature != 0x07:
                # No callbacks here, so move along
                # Except for houses, where we need to store some stuff in a register
                continue
            if cargo_gfx[cargo] != default_val:
                # There are cargo-specific graphics, be sure to handle those
                # Unhandled callbacks should chain to the default, though
                mapping = mapping.copy()
                mapping[0x00] = (cargo_gfx[cargo], None)

            expr = expression.Variable(expression.ConstantNumeric(0x0C), mask = expression.ConstantNumeric(0xFFFF))
            if feature == 0x07:
                # Store relative x/y, item id (of the north tile) and house tile (HOUSE_TILE_XX constant) in register FF
                # Format: 0xIIHHYYXX: II: item ID, HH: house tile, YY: relative y, XX: relative x
                lowbytes_dict = {
                    'n' : 0x000000,
                    'e' : 0x010100,
                    'w' : 0x020001,
                    's' : 0x030101,
                }
                lowbytes = expression.ConstantNumeric(lowbytes_dict[house_tile])
                highbyte = expression.BinOp(nmlop.SHIFT_LEFT, house_north_tile_id, expression.ConstantNumeric(24))
                register_FF = expression.BinOp(nmlop.OR, lowbytes, highbyte, lowbytes.pos).reduce()
                register_FF = expression.BinOp(nmlop.STO_TMP, register_FF, expression.ConstantNumeric(0xFF))
                expr = expression.BinOp(nmlop.VAL2, register_FF, expr, register_FF.pos)

                if len(mapping) == 0:
                    # mapping must not be empty
                    mapping[0x00] = (default_val, None)

            actions, cb_ref = create_cb_choice_varaction2(feature, expr, mapping, default_val, graphics_block.pos)
            prepend_action_list.extend(actions)
            cargo_gfx[cargo] = cb_ref

    # Make sure to sort to make the order well-defined
    offset = 7 if feature <= 3 else 5
    for cargo_id in sorted(cg for cg in cargo_gfx if cg is not None):
        result, comment = action2var.parse_result(cargo_gfx[cargo_id], action_list, act6, offset + 1, act3, None, 0x89)
        act3.cid_mappings.append( (cargo_id, result, comment) )
        offset += 3

    if None in cargo_gfx:
        result, comment = action2var.parse_result(cargo_gfx[None], action_list, act6, offset, act3, None, 0x89)
        act3.def_cid = result
        act3.default_comment = comment
    else:
        act3.def_cid = None
        act3.default_comment = ''

    if livery_override is not None:
        act6livery = action6.Action6()
        # Add any extra actions before the main action3 (TTDP requirement)
        act3livery = create_action3(feature, id, action_list, act6livery, True)
        offset = 7 if feature <= 3 else 5
        result, comment = action2var.parse_result(livery_override, action_list, act6livery, offset, act3livery, None, 0x89)
        act3livery.def_cid = result
        act3livery.default_comment = comment

    if len(act6.modifications) > 0: action_list.append(act6)
    action_list.append(act3)
    if livery_override is not None:
        if len(act6livery.modifications) > 0: action_list.append(act6livery)
        action_list.append(act3livery)
    action6.free_parameters.restore()

    return prepend_action_list + action_list
Beispiel #10
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
Beispiel #11
0
def parse_sg_ref_result(result, action_list, parent_action, var_range):
    """
    Parse a result that is a sprite group reference.

    @param result: Result to parse
    @type result: L{SpriteGroupRef}

    @param action_list: List to append any extra actions to
    @type action_list: C{list} of L{BaseAction}

    @param parent_action: Reference to the action of which this is a result
    @type parent_action: L{BaseAction}

    @param var_range: Variable range to use for variables in the expression
    @type var_range: C{int}

    @return: Result to use in the calling varaction2
    @rtype: L{SpriteGroupRef}
    """
    if result.name.value == "CB_FAILED":
        return get_failed_cb_result(parent_action.feature, action_list,
                                    parent_action, result.pos)

    if len(result.param_list) == 0:
        action2.add_ref(result, parent_action)
        return result

    # Result is parametrized
    # Insert an intermediate varaction2 to store expressions in registers
    var_scope = get_scope(parent_action.feature, var_range)
    varact2parser = Varaction2Parser(parent_action.feature, var_scope)
    layout = action2.resolve_spritegroup(result.name)
    for i, param in enumerate(result.param_list):
        if i > 0:
            varact2parser.var_list.append(nmlop.VAL2)
            varact2parser.var_list_size += 1
        varact2parser.parse_expr(reduce_varaction2_expr(param, var_scope))
        varact2parser.var_list.append(nmlop.STO_TMP)
        store_tmp = VarAction2StoreCallParam(
            layout.register_map[parent_action.feature][i])
        varact2parser.var_list.append(store_tmp)
        varact2parser.var_list_size += store_tmp.get_size(
        ) + 1  # Add 1 for operator

    action_list.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:
        action_list.append(extra_act6)

    global return_action_id
    name = "@return_action_{:d}".format(return_action_id)
    varaction2 = Action2Var(parent_action.feature, name, result.pos, var_range)
    return_action_id += 1
    varaction2.var_list = varact2parser.var_list
    ref = expression.SpriteGroupRef(result.name, [], result.pos)
    varaction2.ranges.append(
        VarAction2Range(expression.ConstantNumeric(0),
                        expression.ConstantNumeric(0), ref, result.name.value))
    varaction2.default_result = ref
    varaction2.default_comment = result.name.value
    # Add the references as procs, to make sure, that any intermediate registers
    # are freed at the spritelayout and thus not selected to pass parameters
    # Reference is used twice (range + default) so call add_ref twice
    action2.add_ref(ref, varaction2, True)
    action2.add_ref(ref, varaction2, True)

    ref = expression.SpriteGroupRef(expression.Identifier(name), [], None,
                                    varaction2)
    action_list.append(varaction2)
    action2.add_ref(ref, parent_action)

    return ref
Beispiel #12
0
def get_failed_cb_result(feature, action_list, parent_action, pos):
    """
    Get a sprite group reference to use for a failed callback
    The actions needed are created on first use, then cached in L{failed_cb_results}

    @param feature: Feature to use
    @type feature: C{int}

    @param action_list: Action list to append any extra actions to
    @type action_list: C{list} of L{BaseAction}

    @param parent_action: Reference to the action of which this is a result
    @type parent_action: L{BaseAction}

    @param pos: Positional context.
    @type  pos: L{Position}

    @return: Sprite group reference to use
    @rtype: L{SpriteGroupRef}
    """
    if feature in failed_cb_results:
        varaction2 = failed_cb_results[feature]
    else:
        # Create action2 (+ action1, if needed)
        # Import here to avoid circular imports
        from nml.actions import action1, action2layout, action2production, action2real

        if feature == 0x0A:
            # Industries -> production action2
            act2 = action2production.make_empty_production_action2(pos)
        elif feature in (0x07, 0x09, 0x0F, 0x11):
            # Tile layout action2
            act2 = action2layout.make_empty_layout_action2(feature, pos)
        else:
            # Normal action2
            act1_actions, act1_index = action1.make_cb_failure_action1(feature)
            action_list.extend(act1_actions)
            act2 = action2real.make_simple_real_action2(
                feature, "@CB_FAILED_REAL{:02X}".format(feature), pos,
                act1_index)
        action_list.append(act2)

        # Create varaction2, to choose between returning graphics and 0, depending on CB
        varact2parser = Varaction2Parser(feature, get_scope(feature))
        varact2parser.parse_expr(
            expression.Variable(expression.ConstantNumeric(0x0C),
                                mask=expression.ConstantNumeric(0xFFFF)))

        varaction2 = Action2Var(feature, "@CB_FAILED{:02X}".format(feature),
                                pos, 0x89)
        varaction2.var_list = varact2parser.var_list

        varaction2.ranges.append(
            VarAction2Range(
                expression.ConstantNumeric(0),
                expression.ConstantNumeric(0),
                expression.ConstantNumeric(0),
                "graphics callback -> return 0",
            ))
        varaction2.default_result = expression.SpriteGroupRef(
            expression.Identifier(act2.name), [], None, act2)
        varaction2.default_comment = "Non-graphics callback, return graphics result"
        action2.add_ref(varaction2.default_result, varaction2)

        action_list.append(varaction2)
        failed_cb_results[feature] = varaction2

    ref = expression.SpriteGroupRef(expression.Identifier(varaction2.name), [],
                                    None, varaction2)
    action2.add_ref(ref, parent_action)
    return ref
Beispiel #13
0
def parse_varaction2(switch_block):
    global return_action_id
    return_action_id = 0

    action6.free_parameters.save()
    act6 = action6.Action6()
    action_list = action2real.create_spriteset_actions(switch_block)

    feature = next(iter(switch_block.feature_set))
    var_scope = get_scope(feature, switch_block.var_range)
    varaction2 = Action2Var(
        feature,
        switch_block.name.value,
        switch_block.pos,
        switch_block.var_range,
        switch_block.register_map[feature],
    )

    expr = reduce_varaction2_expr(switch_block.expr, var_scope)

    offset = 4  # first var

    parser = Varaction2Parser(feature, var_scope)
    parser.parse_expr(expr)
    action_list.extend(parser.extra_actions)
    for mod in parser.mods:
        act6.modify_bytes(mod.param, mod.size, mod.offset + offset)
    varaction2.var_list = parser.var_list
    offset += parser.var_list_size + 1  # +1 for the byte num-ranges
    for proc in parser.proc_call_list:
        action2.add_ref(proc, varaction2, True)

    none_result = None
    if any(x is not None and x.value is None
           for x in [r.result for r in switch_block.body.ranges] +
           [switch_block.body.default]):
        # Computed result is returned in at least one result
        if len(switch_block.body.ranges) == 0:
            # There is only a default, which is 'return computed result', so we're fine
            none_result = expression.ConstantNumeric(
                0)  # Return value does not matter
        else:
            # Add an extra action to return the computed value
            extra_actions, none_result = create_return_action(
                expression.Variable(expression.ConstantNumeric(0x1C)),
                feature,
                switch_block.name.value + "@return",
                0x89,
            )
            action_list.extend(extra_actions)

    used_ranges = []
    for r in switch_block.body.ranges:
        comment = str(r.min) + " .. " + str(r.max) + ": "

        range_result, range_comment = parse_result(r.result.value, action_list,
                                                   act6, offset, varaction2,
                                                   none_result,
                                                   switch_block.var_range)
        comment += range_comment
        offset += 2  # size of result

        range_min, offset, check_min = parse_minmax(r.min, r.unit, action_list,
                                                    act6, offset)
        range_max, offset, check_max = parse_minmax(r.max, r.unit, action_list,
                                                    act6, offset)

        range_overlap = False
        if check_min and check_max:
            for existing_range in used_ranges:
                if existing_range[
                        0] <= range_min.value and range_max.value <= existing_range[
                            1]:
                    generic.print_warning(
                        generic.Warning.GENERIC,
                        "Range overlaps with existing ranges so it'll never be reached",
                        r.min.pos,
                    )
                    range_overlap = True
                    break
            if not range_overlap:
                used_ranges.append([range_min.value, range_max.value])
                used_ranges.sort()
                i = 0
                while i + 1 < len(used_ranges):
                    if used_ranges[i + 1][0] <= used_ranges[i][1] + 1:
                        used_ranges[i][1] = max(used_ranges[i][1],
                                                used_ranges[i + 1][1])
                        used_ranges.pop(i + 1)
                    else:
                        i += 1

        if not range_overlap:
            varaction2.ranges.append(
                VarAction2Range(range_min, range_max, range_result, comment))

    if len(switch_block.body.ranges) == 0 and (
            switch_block.body.default is None
            or switch_block.body.default.value is not None):
        # Computed result is not returned, but there are no ranges
        # Add one range, to avoid the nvar == 0 bear trap
        offset += 10
        varaction2.ranges.append(
            VarAction2Range(
                expression.ConstantNumeric(1),
                expression.ConstantNumeric(0),
                expression.ConstantNumeric(0),
                "Bogus range to avoid nvar == 0",
            ))

    # Handle default result
    if switch_block.body.default is not None:
        # there is a default value
        default_result = switch_block.body.default.value
    else:
        # Default to CB_FAILED
        default_result = expression.SpriteGroupRef(
            expression.Identifier("CB_FAILED", None), [], None)

    default, default_comment = parse_result(default_result, action_list, act6,
                                            offset, varaction2, none_result,
                                            switch_block.var_range)
    varaction2.default_result = default
    if switch_block.body.default is None:
        varaction2.default_comment = "No default specified -> fail callback"
    elif switch_block.body.default.value is None:
        varaction2.default_comment = "Return computed value"
    else:
        varaction2.default_comment = "default: " + default_comment

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

    action_list.append(varaction2)
    switch_block.set_action2(varaction2, feature)

    action6.free_parameters.restore()
    return action_list
Beispiel #14
0
 def func60x(name, value, pos):
     return expression.FunctionPtr(expression.Identifier(name, pos),
                                   parse_60x_var, value)