Ejemplo n.º 1
0
Archivo: actionB.py Proyecto: spnda/nml
def parse_error_block(error):
    action6.free_parameters.save()
    action_list = []
    act6 = action6.Action6()

    severity = actionD.write_action_value(error.severity, action_list, act6, 1,
                                          1)[0]

    langs = [0x7F]
    if isinstance(error.msg, expression.String):
        custom_msg = True
        msg_string = error.msg
        grfstrings.validate_string(msg_string)
        langs.extend(grfstrings.get_translations(msg_string))
        for lang in langs:
            assert lang is not None
    else:
        custom_msg = False
        msg = error.msg.reduce_constant().value

    if error.data is not None:
        error.data = error.data.reduce()
        if isinstance(error.data, expression.String):
            grfstrings.validate_string(error.data)
            langs.extend(grfstrings.get_translations(error.data))
            for lang in langs:
                assert lang is not None
        elif not isinstance(error.data, expression.StringLiteral):
            raise generic.ScriptError(
                "Error parameter 3 'data' should be the identifier of a custom sting",
                error.data.pos)

    params = []
    for expr in error.params:
        if isinstance(expr, expression.Parameter) and isinstance(
                expr.num, expression.ConstantNumeric):
            params.append(expr.num)
        else:
            tmp_param, tmp_param_actions = actionD.get_tmp_parameter(expr)
            action_list.extend(tmp_param_actions)
            params.append(expression.ConstantNumeric(tmp_param))

    langs = list(set(langs))
    langs.sort()
    for lang in langs:
        if custom_msg:
            msg = grfstrings.get_translation(msg_string, lang)
        if error.data is None:
            data = None
        elif isinstance(error.data, expression.StringLiteral):
            data = error.data.value
        else:
            data = grfstrings.get_translation(error.data, lang)
        if len(act6.modifications) > 0:
            action_list.append(act6)
        action_list.append(ActionB(severity, lang, msg, data, params))

    action6.free_parameters.restore()
    return action_list
Ejemplo n.º 2
0
def get_global_string_actions():
    """
    Get a list of global string actions
    i.e. print all D0xx / DCxx texts at once

    @return: A list of all D0xx / DCxx action4s
    @rtype: C{list} of L{BaseAction}
    """
    texts = []
    actions = []
    for string_range, strings in list(used_strings.items()):
        for feature_name, id in list(strings.items()):
            feature, string_name = feature_name
            texts.append( (0x7F, id, grfstrings.get_translation(string_name), feature) )
            for lang_id in grfstrings.get_translations(string_name):
                texts.append( (lang_id, id, grfstrings.get_translation(string_name, lang_id), feature) )

    last_lang = -1
    last_id = -1
    last_feature = -1
    # Sort to have a deterministic ordering and to have as much consecutive IDs as possible
    texts.sort(key=lambda text: (-1 if text[0] == 0x7F else text[0], text[1]))

    for text in texts:
        str_lang, str_id, str_text, feature = text
        # If possible, append strings to the last action 4 instead of creating a new one
        if str_lang != last_lang or str_id - 1 != last_id or feature != last_feature or len(actions[-1].texts) == 0xFF:
            actions.append(Action4(feature, str_lang, 2, str_id, [str_text]))
        else:
            actions[-1].texts.append(str_text)
        last_lang = str_lang
        last_id = str_id
        last_feature = feature
    return actions
Ejemplo n.º 3
0
def grf_name_desc_actions(root, name, desc, url, version,
                          min_compatible_version):
    if len(grfstrings.get_translations(name)) > 0:
        name_node = TextNode("NAME", name, True)
        root.subnodes.append(name_node)
    if len(grfstrings.get_translations(desc)) > 0:
        desc_node = TextNode("DESC", desc, True)
        root.subnodes.append(desc_node)
    if url is not None:
        desc_node = TextNode("URL_", url)
        root.subnodes.append(desc_node)
    version_node = BinaryNode("VRSN", 4, version.value)
    root.subnodes.append(version_node)
    min_compatible_version_node = BinaryNode("MINV", 4,
                                             min_compatible_version.value)
    root.subnodes.append(min_compatible_version_node)
Ejemplo n.º 4
0
    def prepare_output(self, sprite_num):
        # Resolve references to earlier townname actions
        blocks = set()
        for part in self.parts:
            blocks.update(part.resolve_townname_id())

        # Allocate a number for this action F.
        if self.name is None or isinstance(self.name, expression.Identifier):
            self.id_number = get_free_id()
            if isinstance(self.name, expression.Identifier):
                if self.name.value in named_numbers:
                    raise generic.ScriptError(
                        'Cannot define town name "{}", it is already in use'.
                        format(self.name), self.pos)
                named_numbers[
                    self.name.
                    value] = self.id_number  # Add name to the set 'safe' names.
        else:
            numbered_numbers.add(
                self.id_number)  # Add number to the set of 'safe' numbers.

        # Ask existing action F for the lowest available bit.
        if len(town_names_blocks) == 0: startbit = 0
        else:
            startbit = max(block.free_bit
                           for block in town_names_blocks.values())

        town_names_blocks[
            self.id_number] = self  # Add self to the available blocks.

        # Allocate random bits to all parts.
        for part in self.parts:
            num_bits = part.assign_bits(startbit)
            # Prevent overlap if really needed
            if len(part.pieces) > 1: startbit += num_bits
        self.free_bit = startbit

        if startbit > 32:
            raise generic.ScriptError(
                "Not enough random bits for the town name generation ({:d} needed, 32 available)"
                .format(startbit), self.pos)

        # Pull style names if needed.
        if self.style_name is not None:
            grfstrings.validate_string(self.style_name)
            self.style_names = [
                (lang_id, grfstrings.get_translation(self.style_name, lang_id))
                for lang_id in grfstrings.get_translations(self.style_name)
            ]
            self.style_names.append(
                (0x7F, grfstrings.get_translation(self.style_name)))
            self.style_names.sort()
            if len(self.style_names) == 0:
                raise generic.ScriptError(
                    'Style "{}" defined, but no translations found for it'.
                    format(self.style_name.name.value), self.pos)
        else:
            self.style_names = []
Ejemplo n.º 5
0
 def write(self, file):
     if not self.skip_default_langid:
         self.write_type_id(file)
         file.print_bytex(0x7F)
         file.print_string(grfstrings.get_translation(self.string))
         file.newline()
     for lang_id in grfstrings.get_translations(self.string):
         self.write_type_id(file)
         file.print_bytex(lang_id)
         file.print_string(grfstrings.get_translation(self.string, lang_id))
         file.newline()
Ejemplo n.º 6
0
 def get_size(self):
     if self.skip_default_langid:
         size = 0
     else:
         size = 6 + grfstrings.get_string_size(
             grfstrings.get_translation(self.string))
     for lang_id in grfstrings.get_translations(self.string):
         # 6 is for "T" (1), id (4), langid (1)
         size += 6 + grfstrings.get_string_size(
             grfstrings.get_translation(self.string, lang_id))
     return size
Ejemplo n.º 7
0
def get_string_action4s(feature, string_range, string, id = None):
    """
    Let a string from the lang files be used in the rest of NML.
    This may involve adding actions directly, but otherwise an ID is allocated and the string will be written later

    @param feature: Feature that uses the string
    @type feature: C{int}

    @param string_range: String range to use, either a value from L{string_ranges} or C{None} if N/A (item names)
    @type string_range: C{int} or C{None}

    @param string: String to parse
    @type string: L{expression.String}

    @param id: ID to use for this string, or C{None} if it will be allocated dynamically (random_id is true for the string range)
    @type id: L{Expression} or C{None}

    @return: A tuple of two values:
                - ID of the string (useful if allocated dynamically)
                - Resulting action list to be appended
    @rtype: C{tuple} of (C{int}, C{list} of L{BaseAction})
    """
    grfstrings.validate_string(string)
    write_action4s = True
    action6.free_parameters.save()
    actions = []

    mod = None
    if string_range is not None:
        size = 2
        if string_ranges[string_range]['random_id']:
            # ID is allocated randomly, we will output the actions later
            write_action4s = False
            if (feature, string) in used_strings[string_range]:
                id_val = used_strings[string_range][(feature, string)]
            else:
                id_val = string_ranges[string_range]['ids'].pop()
                used_strings[string_range][(feature, string)] = id_val
        else:
            # ID must be supplied
            assert id is not None
            assert isinstance(id, expression.ConstantNumeric)
            id_val = id.value | (string_range << 8)
    else:
        # Not a string range, so we must have an id
        assert id is not None
        size = 3 if feature <= 3 else 1
        if isinstance(id, expression.ConstantNumeric):
            id_val = id.value
        else:
            id_val = 0
            tmp_param, tmp_param_actions = actionD.get_tmp_parameter(id)
            actions.extend(tmp_param_actions)
            # Apply ID via action4 later
            mod = (tmp_param, 2 if feature <= 3 else 1, 5 if feature <= 3 else 4)

    if write_action4s:
        strings = [(lang_id, grfstrings.get_translation(string, lang_id)) for lang_id in grfstrings.get_translations(string)]
        # Sort the strings for deterministic ordering and prepend the default language
        strings = [(0x7F, grfstrings.get_translation(string))] + sorted(strings, key=lambda lang_text: lang_text[0])

        for lang_id, text in strings:
            if mod is not None:
                act6 = action6.Action6()
                act6.modify_bytes(*mod)
                actions.append(act6)
            actions.append(Action4(feature, lang_id, size, id_val, [text]))

    action6.free_parameters.restore()

    return (id_val, actions)
Ejemplo n.º 8
0
    def prepare_output(self, sprite_num):
        # Resolve references to earlier townname actions
        referenced_blocks = []
        for part in self.parts:
            part_blocks = part.resolve_townname_id()
            if part_blocks:
                referenced_blocks.append(part_blocks)

        # Allocate a number for this action F.
        if self.name is None or isinstance(self.name, expression.Identifier):
            self.id_number = get_free_id()
            if isinstance(self.name, expression.Identifier):
                if self.name.value in named_numbers:
                    raise generic.ScriptError(
                        'Cannot define town name "{}", it is already in use'.format(self.name), self.pos
                    )
                named_numbers[self.name.value] = self.id_number  # Add name to the set 'safe' names.
        else:
            numbered_numbers.add(self.id_number)  # Add number to the set of 'safe' numbers.

        town_names_blocks[self.id_number] = self  # Add self to the available blocks.

        def assign_bits(part, startbit):
            num_bits = part.assign_bits(startbit)
            # Prevent overlap if really needed
            if len(part.pieces) > 1:
                startbit += num_bits
            return startbit

        def update_block(block, startbit):
            for part in block.parts:
                startbit = assign_bits(part, startbit)
                # Update dependant Action F
                for dep in block.deps:
                    update_block(town_names_blocks[dep], startbit)
            return startbit

        # Determine lowest available bit, and prevent overlap when needed.
        # referenced_blocks is a list of sets, each set contains references that can share the same bits,
        # but different sets can't share bits.
        startbit = 0
        for part_blocks in referenced_blocks:
            freebit = startbit
            for part_block in part_blocks:
                block = town_names_blocks[part_block]
                startbit = max(startbit, update_block(block, freebit))
                if self.id_number not in block.deps:
                    block.deps.append(self.id_number)

        # Allocate random bits to all parts.
        for part in self.parts:
            startbit = assign_bits(part, startbit)

        if startbit > 32:
            raise generic.ScriptError(
                "Not enough random bits for the town name generation ({:d} needed, 32 available)".format(startbit),
                self.pos,
            )

        # Pull style names if needed.
        if self.style_name is not None:
            grfstrings.validate_string(self.style_name)
            self.style_names = [
                (lang_id, grfstrings.get_translation(self.style_name, lang_id))
                for lang_id in grfstrings.get_translations(self.style_name)
            ]
            self.style_names.append((0x7F, grfstrings.get_translation(self.style_name)))
            self.style_names.sort()
            if len(self.style_names) == 0:
                raise generic.ScriptError(
                    'Style "{}" defined, but no translations found for it'.format(self.style_name.name.value), self.pos
                )
        else:
            self.style_names = []