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
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 = []
def pre_process(self): if None in (self.name, self.desc, self.grfid, self.version, self.min_compatible_version): raise generic.ScriptError("A GRF-block requires the 'name', 'desc', 'grfid', 'version' and 'min_compatible_version' properties to be set.", self.pos) self.grfid = self.grfid.reduce() global_constants.constant_numbers['GRFID'] = expression.parse_string_to_dword(self.grfid) self.name = self.name.reduce() if not isinstance(self.name, expression.String): raise generic.ScriptError("GRF-name must be a string", self.name.pos) grfstrings.validate_string(self.name) self.desc = self.desc.reduce() if not isinstance(self.desc, expression.String): raise generic.ScriptError("GRF-description must be a string", self.desc.pos) grfstrings.validate_string(self.desc) if self.url is not None: self.url = self.url.reduce() if not isinstance(self.url, expression.String): raise generic.ScriptError("URL must be a string", self.url.pos) grfstrings.validate_string(self.url) self.version = self.version.reduce_constant() self.min_compatible_version = self.min_compatible_version.reduce_constant() global param_stats param_num = 0 for param in self.params: param.pre_process(expression.ConstantNumeric(param_num)) param_num = param.num.value + 1 if param_num > param_stats[1]: raise generic.ScriptError("No free parameters available. Consider assigning <num> manually and combine multiple bool parameters into a single bitmask parameter using <bit>.", self.pos) if param_num > param_stats[0]: param_stats[0] = param_num
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)
def __init__(self, id, string, skip_default_langid=False): Action14Node.__init__(self, "T", id) self.string = string grfstrings.validate_string(self.string) self.skip_default_langid = skip_default_langid
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 = []