def get_production_actions(produce): """ Get the action list that implements the given produce-block in nfo. @param produce: Produce-block to parse. @type produce: L{Produce} """ action_list = [] act6 = action6.Action6() action6.free_parameters.save() result_list = [] varact2parser = action2var.Varaction2Parser(0x0A, 0x0A) if all(x.supported_by_actionD(False) for x in produce.param_list): version = 0 offset = 4 for i, param in enumerate(produce.param_list): result, offset = actionD.write_action_value( param, action_list, act6, offset, 2 if i < 5 else 1) result_list.append(result.value) else: version = 1 for param in produce.param_list: result_list.append(resolve_prodcb_register(param, varact2parser)) if len(act6.modifications) > 0: action_list.append(act6) prod_action = Action2Production(produce.name.value, produce.pos, version, result_list[0:3], result_list[3:5], result_list[5]) return finish_production_actions(produce, prod_action, action_list, varact2parser)
def create_action3(feature, id, action_list, act6, is_livery_override): # Vehicles use an extended byte size = 2 if feature <= 3 else 1 offset = 4 if feature <= 3 else 3 id, offset = actionD.write_action_value(id, action_list, act6, offset, size) return Action3(feature, id.value, is_livery_override)
def parse_minmax(value, unit_str, action_list, act6, offset): """ Parse a min or max value in a switch block. @param value: Value to parse @type value: L{Expression} @param unit_str: Unit to use @type unit_str: C{str} or C{None} @param action_list: List to append any extra actions to @type action_list: C{list} of L{BaseAction} @param act6: Action6 to add any modifications to @type act6: L{Action6} @param offset: Current offset to use for action6 @type offset: C{int} @return: A tuple of two values: - The value to use as min/max - Whether the resulting range may need a sanity check @rtype: C{tuple} of (L{ConstantNumeric} or L{SpriteGroupRef}), C{bool} """ if unit_str is not None: raise generic.ScriptError( "Using a unit is in switch-ranges is not (temporarily) not supported", value.pos) result, offset = actionD.write_action_value(value, action_list, act6, offset, 4) check_range = isinstance(value, expression.ConstantNumeric) return (result, offset, check_range)
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 parse_actionA(replaces): """ Parse replace-block to ActionA. @param replaces: Replace-block to parse. @type replaces: L{ReplaceSprite} """ action_list = [] action6.free_parameters.save() act6 = action6.Action6() real_sprite_list = real_sprite.parse_sprite_data(replaces) block_list = [] total_sprites = len(real_sprite_list) offset = 2 # Skip 0A and <num-sets> sprite_offset = 0 # Number of sprites already covered by previous [<num-sprites> <first-sprite>]-pairs while total_sprites > 0: this_block = min(total_sprites, 255) # number of sprites in this block total_sprites -= this_block offset += 1 # Skip <num-sprites> first_sprite = replaces.start_id # number of first sprite if sprite_offset != 0: first_sprite = expression.BinOp( nmlop.ADD, first_sprite, expression.ConstantNumeric(sprite_offset, first_sprite.pos), first_sprite.pos).reduce() first_sprite, offset = actionD.write_action_value( first_sprite, action_list, act6, offset, 2) block_list.append((this_block, first_sprite.value)) sprite_offset += this_block # increase first-sprite for next block if len(act6.modifications) > 0: action_list.append(act6) action6.free_parameters.restore() action_list.append(ActionA(block_list)) action_list.extend(real_sprite_list) return action_list
def create_action0(feature, id, act6, action_list): """ Create an action0 with variable id @param feature: Feature of the action0 @type feature: C{int} @param id: ID of the corresponding item @type id: L{Expression} @param act6: Action6 to add any modifications to @type act6: L{Action6} @param action_list: Action list to append any extra actions to @type action_list: C{list} of L{BaseAction} @return: A tuple of (resulting action0, offset to use for action6) @rtype: C{tuple} of (L{Action0}, C{int}) """ id, offset = actionD.write_action_value(id, action_list, act6, 5, 2) action0 = Action0(feature, id.value) return (action0, offset)
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
def get_production_actions(produce): """ Get the action list that implements the given produce-block in nfo. @param produce: Produce-block to parse. @type produce: L{Produce} """ action_list = [] act6 = action6.Action6() action6.free_parameters.save() result_list = [] varact2parser = action2var.Varaction2Parser(0x0A) if all(x.supported_by_actionD(False) for x in produce.param_list): version = 0 offset = 4 for i, param in enumerate(produce.param_list): result, offset = actionD.write_action_value( param, action_list, act6, offset, 2 if i < 5 else 1) result_list.append(result.value) else: version = 1 for i, param in enumerate(produce.param_list): if isinstance(param, expression.StorageOp) and param.name == 'LOAD_TEMP' and \ isinstance(param.register, expression.ConstantNumeric): # We can load a register directly result_list.append( action2var.VarAction2Var(0x7D, 0, 0xFFFFFFFF, param.register.value)) else: if len(varact2parser.var_list) != 0: varact2parser.var_list.append(nmlop.VAL2) varact2parser.var_list_size += 1 varact2parser.parse_expr( action2var.reduce_varaction2_expr(param, 0x0A)) store_tmp = action2var.VarAction2StoreTempVar() result_list.append(action2var.VarAction2LoadTempVar(store_tmp)) varact2parser.var_list.append(nmlop.STO_TMP) varact2parser.var_list.append(store_tmp) varact2parser.var_list_size += store_tmp.get_size( ) + 1 # Add 1 for operator if len(act6.modifications) > 0: action_list.append(act6) prod_action = Action2Production(produce.name.value, produce.pos, version, result_list[0:3], result_list[3:5], result_list[5]) 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