def preprocess_storageop(self, expr): assert isinstance(expr, expression.StorageOp) if expr.info["perm"] and not self.var_scope.has_persistent_storage: raise generic.ScriptError( "Persistent storage is not supported for feature '{}'".format( self.var_scope.name), 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.var_scope is action2var_variables.scope_towns: # 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
def finish_production_actions(produce, prod_action, action_list, varact2parser): action_list.append(prod_action) if len(varact2parser.var_list) == 0: produce.set_action2(prod_action, 0x0A) else: # Create intermediate varaction2 varaction2 = action2var.Action2Var( 0x0A, "{}@registers".format(produce.name.value), produce.pos, 0x89) varaction2.var_list = varact2parser.var_list 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) ref = expression.SpriteGroupRef(produce.name, [], None, prod_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) action2.add_ref(ref, varaction2) action2.add_ref(ref, varaction2) produce.set_action2(varaction2, 0x0A) action_list.append(varaction2) action6.free_parameters.restore() return action_list
def adjust_value(value, org_value, unit, ottd_convert_func): """ Make sure that the property value written to the NewGRF will match exactly the value as quoted @param value: The value to check, converted to base units @type value: L{Expression} @param org_value: The original value as written in the input file @type org_value: L{Expression} @param unit: The unit of the org_value @type unit: L{Unit} or C{None} @return: The adjusted value @rtype: L{Expression} """ while ottd_convert_func(value, unit) > org_value.value: value = expression.ConstantNumeric(int(value.value - 1), value.pos) lower_value = value while ottd_convert_func(value, unit) < org_value.value: value = expression.ConstantNumeric(int(value.value + 1), value.pos) higher_value = value if abs(ottd_convert_func(lower_value, unit) - org_value.value) < abs( ottd_convert_func(higher_value, unit) - org_value.value): return lower_value return higher_value
def _validate_bounding_box(self, name, value): if self.type == Action2LayoutSpriteType.GROUND: raise generic.ScriptError( name + " can not be set for ground sprites", value.pos) elif self.type == Action2LayoutSpriteType.CHILD: if name not in ("xoffset", "yoffset"): raise generic.ScriptError( name + " can not be set for child sprites", value.pos) if isinstance(value, expression.ConstantNumeric): generic.check_range(value.value, 0, 255, name, value.pos) return value else: assert self.type == Action2LayoutSpriteType.BUILDING if name in ("xoffset", "yoffset", "zoffset"): if isinstance(value, expression.ConstantNumeric): generic.check_range(value.value, -128, 127, name, value.pos) return value else: assert name in ("xextent", "yextent", "zextent") if not isinstance(value, expression.ConstantNumeric): raise generic.ScriptError( "Value of '{}' must be a compile-time constant number." .format(name), value.pos) generic.check_range(value.value, 0, 255, name, value.pos) return value # Value must be written to a register self.create_register(name, value) if self.type == Action2LayoutSpriteType.BUILDING: # For building sprites, x and y registers are always written together if name == "xoffset" and self.get_register("yoffset") is None: self.create_register("yoffset", expression.ConstantNumeric(0)) if name == "yoffset" and self.get_register("xoffset") is None: self.create_register("xoffset", expression.ConstantNumeric(0)) return expression.ConstantNumeric(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 = 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
def resolve_spritegroup_ref(self, sg_ref): """ Resolve a reference to a (sprite/palette) sprite group @param sg_ref: Reference to a sprite group @type sg_ref: L{SpriteGroupRef} @return: Sprite number (index of action1 set) to use @rtype: L{Expression} """ spriteset = action2.resolve_spritegroup(sg_ref.name) if len(sg_ref.param_list) == 0: offset = None elif len(sg_ref.param_list) == 1: id_dicts = [(spriteset.labels, lambda val, pos: expression.ConstantNumeric(val, pos))] offset = action2var.reduce_varaction2_expr(sg_ref.param_list[0], self.feature, self.extra_dicts + id_dicts) if isinstance(offset, expression.ConstantNumeric): generic.check_range(offset.value, 0, len(real_sprite.parse_sprite_data(spriteset)) - 1, "offset within spriteset", sg_ref.pos) else: raise generic.ScriptError("Expected 0 or 1 parameter, got " + str(len(sg_ref.param_list)), sg_ref.pos) num = action1.get_action1_index(spriteset) generic.check_range(num, 0, (1 << 14) - 1, "sprite", sg_ref.pos) return expression.ConstantNumeric(num), offset
def get_volume_actions(volume_list): """ Get a list of actions to set sound volumes @param volume_list: List of (sound id, volume) tuples, sorted in ascending order @type volume_list: C{list} of (C{int}, C{int})-tuples @return: A list of actions @rtype: C{list} of L{BaseAction} """ action_list = [] first_id = None # First ID in a series of consecutive IDs value_series = [] # Series of values to write in a single action for id, volume in volume_list: if first_id is None: first_id = id continue_series = first_id + len(value_series) == id if continue_series: value_series.append(volume) if not continue_series or id == volume_list[-1][0]: # Write action for this series act0, offset = create_action0(0x0C, expression.ConstantNumeric(first_id), None, None) act0.num_ids = len(value_series) act0.prop_list.append(Action0Property(0x08, [expression.ConstantNumeric(v) for v in value_series], 1)) action_list.append(act0) # start new series, if needed if not continue_series: first_id = id value_series = [volume] return action_list
def parse_grm(assignment): assert isinstance(assignment.value, expression.GRMOp) action6.free_parameters.save() action_list = [] act6 = action6.Action6() assert isinstance(assignment.param, expression.Parameter) target = assignment.param.num if isinstance(target, expression.Parameter) and isinstance(target.num, expression.ConstantNumeric): act6.modify_bytes(target.num.value, 1, 1) target = expression.ConstantNumeric(0) elif not isinstance(target, expression.ConstantNumeric): tmp_param, tmp_param_actions = get_tmp_parameter(target) act6.modify_bytes(tmp_param, 1, 1) target = expression.ConstantNumeric(0) action_list.extend(tmp_param_actions) op = nmlop.ASSIGN param1 = assignment.value.op param2 = expression.ConstantNumeric(0xFE) data = expression.ConstantNumeric(0xFF | (assignment.value.feature << 8) | (assignment.value.count << 16)) if len(act6.modifications) > 0: action_list.append(act6) action_list.append(ActionD(target, param1, op, param2, data)) action6.free_parameters.restore() return action_list
def parse(self, expr): #Preprocess the expression if isinstance(expr, expression.SpecialParameter): #do this first, since it may evaluate to a BinOp expr = expr.to_reading() if isinstance(expr, expression.BinOp): expr = self.preprocess_binop(expr) elif isinstance(expr, expression.Boolean): expr = expression.BinOp(nmlop.MINU, expr.expr, expression.ConstantNumeric(1)) elif isinstance(expr, expression.BinNot): expr = expression.BinOp(nmlop.XOR, expr.expr, expression.ConstantNumeric(0xFFFFFFFF)) elif isinstance( expr, expression.TernaryOp) and not expr.supported_by_actionD(False): expr = self.preprocess_ternaryop(expr) elif isinstance(expr, expression.StorageOp): expr = self.preprocess_storageop(expr) #Try to parse the expression to a list of variables+operators if isinstance(expr, expression.ConstantNumeric): self.parse_constant(expr) elif isinstance(expr, expression.Parameter) and isinstance( expr.num, expression.ConstantNumeric): self.parse_param(expr) elif isinstance(expr, expression.Variable): self.parse_variable(expr) elif expr.supported_by_actionD(False): self.parse_via_actionD(expr) elif isinstance(expr, expression.BinOp): self.parse_binop(expr) elif isinstance(expr, expression.Not): self.parse_not(expr) elif isinstance(expr, expression.String): self.parse_string(expr) elif isinstance(expr, (VarAction2LoadTempVar, VarAction2LoadLayoutParam)): self.var_list.append(expr) self.var_list_size += expr.get_size() elif isinstance(expr, expression.SpriteGroupRef): self.parse_proc_call(expr) else: expr.supported_by_action2(True) assert False #supported_by_action2 should have raised the correct error already
def parse_boolean(assignment): assert isinstance(assignment.value, expression.Boolean) actions = parse_actionD(ParameterAssignment(assignment.param, expression.ConstantNumeric(0))) expr = nmlop.CMP_NEQ(assignment.value.expr, 0) cond_block = conditional.Conditional( expr, [ParameterAssignment(assignment.param, expression.ConstantNumeric(1))], None ) actions.extend(conditional.ConditionalList([cond_block]).get_action_list()) return actions
def industry_layout_count(name, args, pos, info): if len(args) < 2 or len(args) > 3: raise generic.ScriptError("'{}'() requires between 2 and 3 argument(s), encountered {:d}".format(name, len(args)), pos) grfid = expression.ConstantNumeric(0xFFFFFFFF) if len(args) == 2 else args[2] extra_params = [] extra_params.append( (0x100, grfid) ) extra_params.append( (0x101, expression.BinOp(nmlop.AND, args[1], expression.ConstantNumeric(0xFF)).reduce()) ) return (args[0], extra_params)
def industry_town_count(name, args, pos, info): if len(args) < 1 or len(args) > 2: raise generic.ScriptError("'{}'() requires between 1 and 2 argument(s), encountered {:d}".format(name, len(args)), pos) grfid = expression.ConstantNumeric(0xFFFFFFFF) if len(args) == 1 else args[1] extra_params = [] extra_params.append( (0x100, grfid) ) extra_params.append( (0x101, expression.ConstantNumeric(0x0100)) ) return (args[0], extra_params)
def parse_var(info, pos): param = expression.ConstantNumeric( info['param']) if 'param' in info else None res = expression.Variable( expression.ConstantNumeric(info['var']), expression.ConstantNumeric(info['start']), expression.ConstantNumeric((1 << info['size']) - 1), param, pos) if 'value_function' in info: return info['value_function'](res, info) return res
def parse_hasbit(assignment): assert isinstance(assignment.value, expression.BinOp) and ( assignment.value.op == nmlop.HASBIT or assignment.value.op == nmlop.NOTHASBIT ) actions = parse_actionD(ParameterAssignment(assignment.param, expression.ConstantNumeric(0))) cond_block = conditional.Conditional( assignment.value, [ParameterAssignment(assignment.param, expression.ConstantNumeric(1))], None ) actions.extend(conditional.ConditionalList([cond_block]).get_action_list()) return actions
def cargo_profit_value(value): # In NFO, calculation is (amount * price_factor * cb_result) / 8192 # Units of the NML price factor differ by about 41.12, i.e. 1 NML unit = 41 NFO units # That'd make the formula (amount * price_factor * cb_result) / (8192 / 41) # This is almost (error 0.01%) equivalent to the following, which is what this calculation does # (amount * price_factor * (cb_result * 329 / 256)) / 256 # This allows us to report a factor of 256 in the documentation, which makes a lot more sense than 199.804... # Not doing the division here would improve accuracy, but limits the range of the return value too much value = expression.BinOp(nmlop.MUL, value, expression.ConstantNumeric(329), value.pos) return expression.BinOp(nmlop.DIV, value, expression.ConstantNumeric(256), value.pos)
def parse_var(name, info, pos): if "replaced_by" in info: generic.print_warning("'{}' is deprecated, consider using '{}' instead".format(name, info["replaced_by"]), pos) param = expression.ConstantNumeric(info["param"]) if "param" in info else None res = expression.Variable( expression.ConstantNumeric(info["var"]), expression.ConstantNumeric(info["start"]), expression.ConstantNumeric((1 << info["size"]) - 1), param, pos, ) if "value_function" in info: return info["value_function"](res, info) return res
def validate_size(self): """ Check if xpos/ypos/xsize/ysize are already set and if not, set them to 0,0,image_width,image_height. """ if self.xpos is None: with Image.open(generic.find_file(self.file.value)) as im: self.xpos = expression.ConstantNumeric(0) self.ypos = expression.ConstantNumeric(0) self.xsize = expression.ConstantNumeric(im.size[0]) self.ysize = expression.ConstantNumeric(im.size[1]) self.check_sprite_size() if self.mask_pos is None: self.mask_pos = (self.xpos, self.ypos)
def tile_offset(name, args, pos, info, min, max): if len(args) != 2: raise generic.ScriptError("'{}'() requires 2 arguments, encountered {:d}".format(name, len(args)), pos) for arg in args: if isinstance(arg, expression.ConstantNumeric): generic.check_range(arg.value, min, max, "Argument of '{}'".format(name), arg.pos) x = expression.BinOp(nmlop.AND, args[0], expression.ConstantNumeric(0xF), args[0].pos) y = expression.BinOp(nmlop.AND, args[1], expression.ConstantNumeric(0xF), args[1].pos) # Shift y left by four y = expression.BinOp(nmlop.SHIFT_LEFT, y, expression.ConstantNumeric(4), y.pos) param = expression.BinOp(nmlop.ADD, x, y, x.pos) #Make sure to reduce the result return ( param.reduce(), [] )
def parse_var(name, info, pos): if 'replaced_by' in info: generic.print_warning( "'{}' is deprecated, consider using '{}' instead".format( name, info['replaced_by']), pos) param = expression.ConstantNumeric( info['param']) if 'param' in info else None res = expression.Variable( expression.ConstantNumeric(info['var']), expression.ConstantNumeric(info['start']), expression.ConstantNumeric((1 << info['size']) - 1), param, pos) if 'value_function' in info: return info['value_function'](res, info) return res
def nearest_house_matching_criterion(name, args, pos, info): # nearest_house_matching_criterion(radius, criterion) # parameter is radius | (criterion << 6) if len(args) != 2: raise generic.ScriptError("{}() requires 2 arguments, encountered {:d}".format(name, len(args)), pos) if isinstance(args[0], expression.ConstantNumeric): generic.check_range(args[0].value, 1, 63, "{}()-parameter 1 'radius'".format(name), pos) if isinstance(args[1], expression.ConstantNumeric) and args[1].value not in (0, 1, 2): raise generic.ScriptError("Invalid value for {}()-parameter 2 'criterion'".format(name), pos) radius = expression.BinOp(nmlop.AND, args[0], expression.ConstantNumeric(0x3F, pos), pos) criterion = expression.BinOp(nmlop.AND, args[1], expression.ConstantNumeric(0x03, pos), pos) criterion = expression.BinOp(nmlop.MUL, criterion, expression.ConstantNumeric(0x40, pos), pos) retval = expression.BinOp(nmlop.OR, criterion, radius, pos).reduce() return (retval, [])
def parse_binop(self, expr): if expr.op.act2_num is None: expr.supported_by_action2(True) if (isinstance(expr.expr2, (expression.ConstantNumeric, expression.Variable)) or isinstance(expr.expr2, (VarAction2LoadTempVar, VarAction2LoadCallParam)) or (isinstance(expr.expr2, expression.Parameter) and isinstance(expr.expr2.num, expression.ConstantNumeric)) or expr.op == nmlop.VAL2): expr2 = expr.expr2 elif expr.expr2.supported_by_actionD(False): tmp_param, tmp_param_actions = actionD.get_tmp_parameter( expr.expr2) self.extra_actions.extend(tmp_param_actions) expr2 = expression.Parameter(expression.ConstantNumeric(tmp_param)) else: # The expression is so complex we need to compute it first, store the # result and load it back later. self.parse(expr.expr2) tmp_var = VarAction2StoreTempVar() self.var_list.append(nmlop.STO_TMP) self.var_list.append(tmp_var) self.var_list.append(nmlop.VAL2) # the +2 is for both operators self.var_list_size += tmp_var.get_size() + 2 expr2 = VarAction2LoadTempVar(tmp_var) # parse expr1 self.parse(expr.expr1) self.var_list.append(expr.op) self.var_list_size += 1 self.parse(expr2)
def get_callback_flags_actions(feature, id, flags): """ Get a list of actions to set the callback flags of a certain item @param feature: Feature of the item @type feature: C{int} @param id: ID of the item @type id: L{Expression} @param flags: Value of the 'callback_flags' property @type flags: C{int} @return: A list of actions @rtype: C{list} of L{BaseAction} """ assert isinstance(id, expression.ConstantNumeric) act0, offset = create_action0(feature, id, None, None) act0.num_ids = 1 assert feature in callback_flag_properties prop_info_list = callback_flag_properties[feature] if not isinstance(prop_info_list, list): prop_info_list = [prop_info_list] for prop_info in prop_info_list: act0.prop_list.append( Action0Property( prop_info['num'], parse_property_value(prop_info, expression.ConstantNumeric(flags)), prop_info['size'])) return [act0]
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)
def parse_graphics_block(graphics_block, feature, id, size, is_livery_override = False): """ Parse a graphics block (or livery override) into a list of actions, mainly action3 @param graphics_block: Graphics-block to parse @type graphics_block: L{GraphicsBlock} @param feature: Feature of the associated item @type feature: C{int} @param id: ID of the associated item @type id: L{Expression} @param size: Size of the associated item (relevant for houses only) @type size: L{ConstantNumeric} or C{None} @param is_livery_override: Whether this is a livery override instead of a normal graphics block @type is_livery_override: C{bool} @return: The resulting list of actions @rtype: L{BaseAction} """ action_list = action2real.create_spriteset_actions(graphics_block) if feature == 0x07: # Multi-tile houses need more work size_bit = size.value if size is not None else 0 for i, tile in enumerate(house_tiles[size_bit]): tile_id = id if i == 0 else expression.BinOp(nmlop.ADD, id, expression.ConstantNumeric(i, id.pos), id.pos).reduce() action_list.extend(parse_graphics_block_single_id(graphics_block, feature, tile_id, is_livery_override, tile, id)) else: action_list.extend(parse_graphics_block_single_id(graphics_block, feature, id, is_livery_override)) return action_list
def get_disable_actions(disable): """ Get the action list for a disable_item block @param disable: Disable block @type disable: L{DisableItem} @return: A list of resulting actions @rtype: C{list} of L{BaseAction} """ feature = disable.feature.value if feature not in disable_info: raise generic.ScriptError("disable_item() is not available for feature {:d}.".format(feature), disable.pos) if disable.first_id is None: # No ids set -> disable all assert disable.last_id is None first = 0 num = disable_info[feature]["num"] else: first = disable.first_id.value if disable.last_id is None: num = 1 else: num = disable.last_id.value - first + 1 act0 = Action0(feature, first) act0.num_ids = num for prop in disable_info[feature]["props"]: act0.prop_list.append( Action0Property(prop["num"], num * [expression.ConstantNumeric(prop["value"])], prop["size"]) ) return [act0]
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 register_names(self): if self.name: if not isinstance(self.name, expression.Identifier): raise generic.ScriptError( "Item parameter 2 'name' should be an identifier", self.pos) if self.name.value in global_constants.item_names: existing_id = global_constants.item_names[self.name.value].id if self.id is not None and existing_id.value != self.id.value: raise generic.ScriptError( ("Duplicate item with name '{}'." " This item has already been assigned to id {:d}, cannot reassign to id {:d}" ).format(self.name.value, existing_id.value, self.id.value), self.pos, ) self.id = existing_id # We may have to reserve multiple item IDs for houses num_ids = action0.house_sizes[ self.size.value] if self.size is not None else 1 if self.id is None: self.id = expression.ConstantNumeric( action0.get_free_id(self.feature.value, num_ids, self.pos)) elif not action0.check_id_range(self.feature.value, self.id.value, num_ids, self.id.pos): action0.mark_id_used(self.feature.value, self.id.value, num_ids) if self.name is not None: global_constants.item_names[self.name.value] = self base_statement.BaseStatementList.register_names(self)
def parse_special_check(assignment): check = assignment.value assert isinstance(check, expression.SpecialCheck) actions = parse_actionD(ParameterAssignment(assignment.param, expression.ConstantNumeric(check.results[0]))) value = check.value if check.mask is not None: value &= check.mask value += check.mask << 32 assert check.varsize == 8 else: assert check.varsize <= 4 actions.append(action7.SkipAction(9, check.varnum, check.varsize, check.op, value, 1)) actions.extend(parse_actionD(ParameterAssignment(assignment.param, expression.ConstantNumeric(check.results[1])))) return actions
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 get_snowlinetable_action(snowline_table): assert len(snowline_table) == 12 * 32 action6.free_parameters.save() action_list = [] tmp_param_map = {} # Cache for tmp parameters act6 = action6.Action6() act0, offset = create_action0(0x08, expression.ConstantNumeric(0), act6, action_list) act0.num_ids = 1 offset += 1 # Skip property number data_table = [] idx = 0 while idx < len(snowline_table): val = snowline_table[idx] if isinstance(val, expression.ConstantNumeric): data_table.append(val.value) idx += 1 continue if idx + 3 >= len(snowline_table): tmp_param, tmp_param_actions = actionD.get_tmp_parameter(val) tmp_param_map[val] = tmp_param act6.modify_bytes(tmp_param, 1, offset + idx) action_list.extend(tmp_param_actions) data_table.append(0) idx += 1 continue # Merge the next 4 values together in a single parameter. val2 = nmlop.SHIFT_LEFT(snowline_table[idx + 1], 8) val3 = nmlop.SHIFT_LEFT(snowline_table[idx + 2], 16) val4 = nmlop.SHIFT_LEFT(snowline_table[idx + 3], 24) expr = nmlop.OR(val, val2) expr = nmlop.OR(expr, val3) expr = nmlop.OR(expr, val4) expr = expr.reduce() # Cache lookup, saves some ActionDs if expr in tmp_param_map: tmp_param, tmp_param_actions = tmp_param_map[expr], [] else: tmp_param, tmp_param_actions = actionD.get_tmp_parameter(expr) tmp_param_map[expr] = tmp_param act6.modify_bytes(tmp_param, 4, offset + idx) action_list.extend(tmp_param_actions) data_table.extend([0, 0, 0, 0]) idx += 4 act0.prop_list.append( ByteListProp(0x10, "".join([chr(x) for x in data_table]))) if len(act6.modifications) > 0: action_list.append(act6) action_list.append(act0) action6.free_parameters.restore() return action_list