def pre_process(self): for choice in self.choices: choice.reduce_expressions(next(iter(self.feature_set))) for dep_list in (self.dependent, self.independent): for i, dep in enumerate(dep_list[:]): if dep.is_return: raise generic.ScriptError( "Expected a random_switch identifier after (in)dependent, not a return.", dep.pos) dep_list[i] = dep.value.reduce(global_constants.const_list) # Make sure, all [in]dependencies refer to existing random switch blocks if (not isinstance(dep_list[i], expression.SpriteGroupRef) ) or len(dep_list[i].param_list) > 0: raise generic.ScriptError( "Value for (in)dependent should be an identifier", dep_list[i].pos) spritegroup = action2.resolve_spritegroup(dep_list[i].name) if not isinstance(spritegroup, RandomSwitch): raise generic.ScriptError( "Value of (in)dependent '{}' should refer to a random_switch." .format(dep_list[i].name.value), dep_list[i].pos) self.triggers = self.triggers.reduce_constant( global_constants.const_list) if not (0 <= self.triggers.value <= 255): raise generic.ScriptError( "random_switch parameter 4 'triggers' out of range 0..255, encountered " + str(self.triggers.value), self.triggers.pos) switch_base_class.pre_process(self)
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 pre_process(self): feature = next(iter(self.feature_set)) # var_feature is really weird for type=BACKWARD/FORWARD. # Expressions in cases will still refer to the origin vehicle. var_feature = action2var_variables.varact2parent_scope[feature] if self.type.value == "PARENT" else feature for choice in self.choices: choice.reduce_expressions(var_feature) for dep_list in (self.dependent, self.independent): for i, dep in enumerate(dep_list[:]): if dep.is_return: raise generic.ScriptError( "Expected a random_switch identifier after (in)dependent, not a return.", dep.pos ) dep_list[i] = dep.value.reduce(global_constants.const_list) # Make sure, all [in]dependencies refer to existing random switch blocks if (not isinstance(dep_list[i], expression.SpriteGroupRef)) or len(dep_list[i].param_list) > 0: raise generic.ScriptError("Value for (in)dependent should be an identifier", dep_list[i].pos) spritegroup = action2.resolve_spritegroup(dep_list[i].name) if not isinstance(spritegroup, RandomSwitch): raise generic.ScriptError( "Value of (in)dependent '{}' should refer to a random_switch.".format(dep_list[i].name.value), dep_list[i].pos, ) self.triggers = self.triggers.reduce_constant(global_constants.const_list) if not (0 <= self.triggers.value <= 255): raise generic.ScriptError( "random_switch parameter 4 'triggers' out of range 0..255, encountered " + str(self.triggers.value), self.triggers.pos, ) switch_base_class.pre_process(self)
def __init__(self, sg_ref): if not isinstance(action2.resolve_spritegroup(sg_ref.name), (switch.Switch, switch.RandomSwitch)): raise generic.ScriptError("Block with name '{}' is not a valid procedure".format(sg_ref.name), sg_ref.pos) if not sg_ref.is_procedure: raise generic.ScriptError("Unexpected identifier encountered: '{}'".format(sg_ref.name), sg_ref.pos) VarAction2Var.__init__(self, 0x7E, 0, 0, comment=str(sg_ref)) # Reference to the called action2 self.sg_ref = sg_ref
def pre_process(self): self.spriteset_list = [ x.reduce(global_constants.const_list) for x in self.spriteset_list ] for sg_ref in self.spriteset_list: if not (isinstance(sg_ref, expression.SpriteGroupRef) and action2.resolve_spritegroup(sg_ref.name).is_spriteset()): raise generic.ScriptError("Expected a sprite set reference", sg_ref.pos) if len(sg_ref.param_list) != 0: raise generic.ScriptError( "Spritesets referenced from a spritegroup may not have parameters.", sg_ref.pos)
def lookup_random_action2(sg_ref): """ Lookup a sprite group reference to find the corresponding random action2 @param sg_ref: Reference to random action2 @type sg_ref: L{SpriteGroupRef} @return: Random action2 corresponding to this sprite group, or None if N/A @rtype: L{Action2Random} or C{None} """ spritegroup = action2.resolve_spritegroup(sg_ref.name) act2 = spritegroup.random_act2 assert act2 is None or isinstance(act2, Action2Random) return act2
def get_real_action2s(spritegroup, feature): loaded_list = [] loading_list = [] actions = [] if feature not in action2.features_sprite_group: raise generic.ScriptError("Sprite groups that combine sprite sets are not supported for feature '{}'.".format(general.feature_name(feature)), spritegroup.pos) # First make sure that all referenced real sprites are put in a single action1 spriteset_list = [] for view in spritegroup.spriteview_list: spriteset_list.extend([action2.resolve_spritegroup(sg_ref.name) for sg_ref in view.spriteset_list]) actions.extend(action1.add_to_action1(spriteset_list, feature, spritegroup.pos)) view_names = sorted([view.name.value for view in spritegroup.spriteview_list]) if feature in (0x00, 0x01, 0x02, 0x03): if view_names != sorted(['loading', 'loaded']): raise generic.ScriptError("Expected a 'loading' and a 'loaded' (list of) sprite set(s).", spritegroup.pos) elif feature in (0x05, 0x0B, 0x0D, 0x10): msg = "Sprite groups for feature {:02X} will not be supported in the future, as they are no longer needed. Directly refer to sprite sets instead." msg = msg.format(feature) generic.print_warning(msg, spritegroup.pos) if view_names != ['default']: raise generic.ScriptError("Expected only a 'default' (list of) sprite set(s).", spritegroup.pos) for view in spritegroup.spriteview_list: if len(view.spriteset_list) == 0: raise generic.ScriptError("Expected at least one sprite set, encountered 0.", view.pos) for set_ref in view.spriteset_list: spriteset = action2.resolve_spritegroup(set_ref.name) action1_index = action1.get_action1_index(spriteset) if view.name.value == 'loading': loading_list.append(action1_index) else: loaded_list.append(action1_index) actions.append(Action2Real(feature, spritegroup.name.value + " - feature {:02X}".format(feature), spritegroup.pos, loaded_list, loading_list)) spritegroup.set_action2(actions[-1], feature) return actions
def parse_proc_call(self, expr): assert isinstance(expr, expression.SpriteGroupRef) var_access = VarAction2ProcCallVar(expr) target = action2.resolve_spritegroup(expr.name) refs = expr.collect_references() # Fill param registers for the call tmp_vars = [] for i, param in enumerate(expr.param_list): if i > 0: # No operator before first param as per advanced VarAct2 syntax self.var_list.append(nmlop.VAL2) self.var_list_size += 1 if refs != [expr]: # For f(x, g(y)), x can be overwritten by y if f and g share the same param registers # Use temporary variables as an intermediate step store_tmp = VarAction2StoreTempVar() tmp_vars.append(( VarAction2LoadTempVar(store_tmp), VarAction2StoreCallParam( target.register_map[self.action_feature][i]), )) else: store_tmp = VarAction2StoreCallParam( target.register_map[self.action_feature][i]) self.parse_expr(reduce_varaction2_expr(param, self.var_scope)) self.var_list.append(nmlop.STO_TMP) self.var_list.append(store_tmp) self.var_list_size += store_tmp.get_size( ) + 1 # Add 1 for operator # Fill param registers with temporary variables if needed for (src, dest) in tmp_vars: self.var_list.append(nmlop.VAL2) self.var_list.append(src) self.var_list.append(nmlop.STO_TMP) self.var_list.append(dest) self.var_list_size += src.get_size() + dest.get_size( ) + 2 # Add 2 for operators if expr.param_list: self.var_list.append(nmlop.VAL2) self.var_list_size += 1 self.var_list.append(var_access) self.var_list_size += var_access.get_size() self.proc_call_list.append(expr)
def get_action2_id(self, feature): """ Get the action2 set-ID that this reference maps to @param feature: Feature of the action2 @type feature: C{int} @return: The set ID @rtype: C{int} """ if self.act2 is not None: return self.act2.id if self.name.value == 'CB_FAILED': return 0 # 0 serves as a failed CB result because it is never used try: spritegroup = action2.resolve_spritegroup(self.name) except generic.ScriptError: raise AssertionError("Illegal action2 reference '{}' encountered.".format(self.name.value)) return spritegroup.get_action2(feature).id
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 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
def is_read_only(self): if self.name.value != "CB_FAILED": spritegroup = action2.resolve_spritegroup(self.name) return spritegroup.is_read_only() return True
def reduce(self, id_dicts=None, unknown_id_fatal=True): if self.name.value != "CB_FAILED" and not self.is_procedure: spritegroup = action2.resolve_spritegroup(self.name) if spritegroup.optimise(): return spritegroup.optimised return self