def prepare_act2_output(self): """ Prepare this node for outputting. This sets the feature and makes sure it is correct. @return: True iff parsing of this node is needed @rtype: C{bool} """ if not cls_is_referenced: return True if not self._prepared: self._prepared = True # copy, since we're going to modify ref_nodes = self._referencing_nodes.copy() for node in ref_nodes: used = node.prepare_act2_output() if not used: node._remove_reference(self) # now determine the feature if self._has_explicit_feature(): # by this time, feature should be set assert len(self.feature_set) == 1 for n in self._referencing_nodes: if n.feature_set != self.feature_set: msg = "Cannot refer to block '{}' with feature '{}', expected feature is '{}'" msg = msg.format( self.name.value, general.feature_name( next(iter(self.feature_set))), general.feature_name( n.feature_set.difference( self.feature_set).pop()), ) raise generic.ScriptError(msg, n.pos) elif len(self._referencing_nodes) != 0: for n in self._referencing_nodes: # Add the features from all calling blocks to the set self.feature_set.update(n.feature_set) if len(self._referencing_nodes) == 0 and ( not self.optimised or self.optimised is self): # if we can be 'not used', there ought to be a way to refer to this block assert self.name is not None generic.print_warning( generic.Warning.OPTIMISATION, "Block '{}' is not referenced, ignoring.".format( self.name.value), self.pos, ) return len(self._referencing_nodes) != 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 = 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 get_property_info_list(feature, name): """ Find information on a single property, based on feature and name/number @param feature: Feature of the associated item @type feature: C{int} @param name: Name (or number) of the property @type name: L{Identifier} or L{ConstantNumeric} @return: A list of dictionaries with property information @rtype: C{list} of C{dict} """ # Validate feature assert feature in range(0, len(properties)) # guaranteed by item if properties[feature] is None: raise generic.ScriptError( "Setting properties for feature '{}' is not possible, no properties are defined." .format(general.feature_name(feature)), name.pos, ) if isinstance(name, expression.Identifier): prop_name = name.value if prop_name not in properties[feature]: raise generic.ScriptError("Unknown property name: " + prop_name, name.pos) prop_info_list = properties[feature][prop_name] if not isinstance(prop_info_list, list): prop_info_list = [prop_info_list] elif isinstance(name, expression.ConstantNumeric): for prop_info_list in properties[feature].values(): if not isinstance(prop_info_list, list): prop_info_list = [prop_info_list] # Only non-compound properties may be set by number if len(prop_info_list) == 1 and "num" in prop_info_list[ 0] and prop_info_list[0]["num"] == name.value: break else: raise generic.ScriptError("Unknown property number: " + str(name), name.pos) else: raise AssertionError() for prop_info in prop_info_list: if "replaced_by" in prop_info: generic.print_warning( generic.Warning.DEPRECATION, "'{}' is deprecated, consider using '{}' instead".format( prop_name, prop_info["replaced_by"]), name.pos, ) if "warning" in prop_info: generic.print_warning(generic.Warning.GENERIC, prop_info["warning"], name.pos) return prop_info_list
def get_property_info_list(feature, name): """ Find information on a single property, based on feature and name/number @param feature: Feature of the associated item @type feature: C{int} @param name: Name (or number) of the property @type name: L{Identifier} or L{ConstantNumeric} @return: A list of dictionaries with property information @rtype: C{list} of C{dict} """ global properties #Validate feature assert feature in range(0, len(properties)) #guaranteed by item if properties[feature] is None: raise generic.ScriptError( "Setting properties for feature '{}' is not possible, no properties are defined." .format(general.feature_name(feature)), name.pos) if isinstance(name, expression.Identifier): if not name.value in properties[feature]: raise generic.ScriptError("Unknown property name: " + name.value, name.pos) prop_info_list = properties[feature][name.value] if not isinstance(prop_info_list, list): prop_info_list = [prop_info_list] elif isinstance(name, expression.ConstantNumeric): for p in properties[feature]: prop_info_list = properties[feature][p] if not isinstance(prop_info_list, list): prop_info_list = [prop_info_list] # Only non-compound properties may be set by number if len(prop_info_list) == 1 and 'num' in prop_info_list[ 0] and prop_info_list[0]['num'] == name.value: break else: raise generic.ScriptError("Unknown property number: " + str(name), name.pos) else: assert False for prop_info in prop_info_list: if 'warning' in prop_info: generic.print_warning(prop_info['warning'], name.pos) return prop_info_list
def preprocess_storageop(self, expr): assert isinstance(expr, expression.StorageOp) max = 0xF if expr.info['perm'] else 0x10F if isinstance( expr.register, expression.ConstantNumeric) and expr.register.value > max: raise generic.ScriptError( "Register number must be in range 0..{:d}, encountered {:d}.". format(max, expr.register.value), expr.pos) 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 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_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.").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