def get_config( prop_block: Property, folder: str, pak_id: str, prop_name: str = 'config', extension: str = '.cfg', source: str = '', ) -> lazy_conf.LazyConf: """Lazily extract a config file referred to by the given property block. Looks for the prop_name key in the given prop_block. If the keyvalue has a value of "", an empty tree is returned. If it has children, a copy of them will be returned. Otherwise the value is a filename in the zip which will be parsed. If source is supplied, set_cond_source() will be run. """ prop_block = prop_block.find_key(prop_name, "") if prop_block.has_children(): prop = prop_block.copy() prop.name = "" return lazy_conf.raw_prop(prop, source=source) if prop_block.value == '': return lazy_conf.BLANK # Zips must use '/' for the separator, even on Windows! path = f'{folder}/{prop_block.value}' if len(path) < 3 or path[-4] != '.': # Add extension path += extension return lazy_conf.from_file(utils.PackagePath(pak_id, path), source=source)
def get_config( prop_block: Property, fsys: FileSystem, folder: str, pak_id='', prop_name='config', extension='.cfg', ): """Extract a config file referred to by the given property block. Looks for the prop_name key in the given prop_block. If the keyvalue has a value of "", an empty tree is returned. If it has children, a copy of them is returned. Otherwise the value is a filename in the zip which will be parsed. """ prop_block = prop_block.find_key(prop_name, "") if prop_block.has_children(): prop = prop_block.copy() prop.name = None return prop if prop_block.value == '': return Property(None, []) # Zips must use '/' for the separator, even on Windows! path = folder + '/' + prop_block.value if len(path) < 3 or path[-4] != '.': # Add extension path += extension try: return fsys.read_prop(path) except FileNotFoundError: LOGGER.warning('"{id}:{path}" not in zip!', id=pak_id, path=path) return Property(None, []) except UnicodeDecodeError: LOGGER.exception('Unable to read "{id}:{path}"', id=pak_id, path=path) raise
class Style(PakObject): """Represents a style, specifying the era a test was built in.""" def __init__( self, style_id: str, selitem_data: 'SelitemData', items: List[EditorItem], renderables: Dict[RenderableType, Renderable], config=None, base_style: Optional[str] = None, suggested: Tuple[str, str, str, str] = None, has_video: bool = True, vpk_name: str = '', corridors: Dict[Tuple[str, int], CorrDesc] = None, ) -> None: self.id = style_id self.selitem_data = selitem_data self.items = items self.renderables = renderables self.base_style = base_style # Set by post_parse() after all objects are read. # this is a list of this style, plus parents in order. self.bases: List[Style] = [] self.suggested = suggested or ('<NONE>', '<NONE>', 'SKY_BLACK', '<NONE>') self.has_video = has_video self.vpk_name = vpk_name self.corridors = {} for group, length in CORRIDOR_COUNTS.items(): for i in range(1, length + 1): try: self.corridors[group, i] = corridors[group, i] except KeyError: self.corridors[group, i] = CorrDesc('', '', '') if config is None: self.config = Property(None, []) else: self.config = config set_cond_source(self.config, 'Style <{}>'.format(style_id)) @classmethod def parse(cls, data: ParseData): """Parse a style definition.""" info = data.info # type: Property filesystem = data.fsys # type: FileSystem selitem_data = SelitemData.parse(info) base = info['base', ''] has_video = srctools.conv_bool( info['has_video', ''], not data.is_override, # Assume no video for override ) vpk_name = info['vpk_name', ''].casefold() sugg = info.find_key('suggested', []) if data.is_override: # For overrides, we default to no suggestion.. sugg = ( sugg['quote', ''], sugg['music', ''], sugg['skybox', ''], sugg['elev', ''], ) else: sugg = ( sugg['quote', '<NONE>'], sugg['music', '<NONE>'], sugg['skybox', 'SKY_BLACK'], sugg['elev', '<NONE>'], ) corr_conf = info.find_key('corridors', []) corridors = {} icon_folder = corr_conf['icon_folder', ''] for group, length in CORRIDOR_COUNTS.items(): group_prop = corr_conf.find_key(group, []) for i in range(1, length + 1): prop = group_prop.find_key(str(i), '') # type: Property if icon_folder: icon = '{}/{}/{}.jpg'.format(icon_folder, group, i) # If this doesn't actually exist, don't use this. if 'resources/bee2/corr/' + icon not in data.fsys: LOGGER.debug('No "resources/bee2/{}"!', icon) icon = '' else: icon = '' if prop.has_children(): corridors[group, i] = CorrDesc( name=prop['name', ''], icon=prop['icon', icon], desc=prop['Desc', ''], ) else: corridors[group, i] = CorrDesc( name=prop.value, icon=icon, desc='', ) if base == '': base = None try: folder = 'styles/' + info['folder'] except IndexError: # It's OK for override styles to be missing their 'folder' # value. if data.is_override: items = [] renderables = {} vbsp = None else: raise ValueError( f'Style "{data.id}" missing configuration folder!') else: with filesystem: with filesystem[folder + '/items.txt'].open_str() as f: items, renderables = EditorItem.parse(f) try: vbsp = filesystem.read_prop(folder + '/vbsp_config.cfg') except FileNotFoundError: vbsp = None return cls( style_id=data.id, selitem_data=selitem_data, items=items, renderables=renderables, config=vbsp, base_style=base, suggested=sugg, has_video=has_video, corridors=corridors, vpk_name=vpk_name, ) def add_over(self, override: 'Style') -> None: """Add the additional commands to ourselves.""" self.items.extend(override.items) self.renderables.update(override.renderables) self.config += override.config self.selitem_data += override.selitem_data self.has_video = self.has_video or override.has_video # If overrides have suggested IDs, use those. Unset values = ''. self.suggested = tuple(over_sugg or self_sugg for self_sugg, over_sugg in zip( self.suggested, override.suggested)) @classmethod def post_parse(cls) -> None: """Assign the bases lists for all styles.""" all_styles: Dict[str, Style] = {} for style in cls.all(): all_styles[style.id] = style for style in all_styles.values(): base = [] b_style = style while b_style is not None: # Recursively find all the base styles for this one if b_style in base: # Already hit this! raise Exception('Loop in bases for "{}"!'.format( b_style.id)) base.append(b_style) b_style = all_styles.get(b_style.base_style, None) # Just append the style.base_style to the list, # until the style with that ID isn't found anymore. style.bases = base def __repr__(self) -> str: return f'<Style: {self.id}>' def export( self ) -> Tuple[List[EditorItem], Dict[RenderableType, Renderable], Property]: """Export this style, returning the vbsp_config and editoritems. This is a special case, since styles should go first in the lists. """ vbsp_config = Property(None, []) vbsp_config += self.config.copy() return self.items, self.renderables, vbsp_config