async def parse(cls, data: ParseData) -> ItemConfig: """Parse from config files.""" vers: dict[str, dict[str, lazy_conf.LazyConf]] = {} styles: dict[str, lazy_conf.LazyConf] all_config = get_config( data.info, 'items', pak_id=data.pak_id, prop_name='all_conf', source=f'<ItemConfig {data.pak_id}:{data.id} all_conf>', ) for ver in data.info.find_all('Version'): ver_id = ver['ID', 'VER_DEFAULT'] vers[ver_id] = styles = {} for sty_block in ver.find_all('Styles'): for style in sty_block: styles[style.real_name] = lazy_conf.from_file( utils.PackagePath(data.pak_id, f'items/{style.value}.cfg'), source= f'<ItemConfig {data.pak_id}:{data.id} in "{style.real_name}">', ) return ItemConfig( data.id, all_config, vers, )
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 parse_item_folder( folders_to_parse: set[str], filesystem: FileSystem, pak_id: str, ) -> dict[str, ItemVariant]: """Parse through the data in item/ folders. folders is a dict, with the keys set to the folder names we want. The values will be filled in with itemVariant values """ folders: dict[str, ItemVariant] = {} for fold in folders_to_parse: prop_path = 'items/' + fold + '/properties.txt' editor_path = 'items/' + fold + '/editoritems.txt' config_path = 'items/' + fold + '/vbsp_config.cfg' first_item: EditorItem | None = None extra_items: list[EditorItem] = [] try: props = filesystem.read_prop(prop_path).find_key('Properties') f = filesystem[editor_path].open_str() except FileNotFoundError as err: raise IOError('"' + pak_id + ':items/' + fold + '" not valid! ' 'Folder likely missing! ') from err with f: tok = Tokenizer(f, editor_path) for tok_type, tok_value in tok: if tok_type is Token.STRING: if tok_value.casefold() != 'item': raise tok.error('Unknown item option "{}"!', tok_value) if first_item is None: first_item = EditorItem.parse_one(tok) else: extra_items.append(EditorItem.parse_one(tok)) elif tok_type is not Token.NEWLINE: raise tok.error(tok_type) if first_item is None: raise ValueError(f'"{pak_id}:items/{fold}/editoritems.txt has no ' '"Item" block!') try: editor_vmf = VMF.parse( filesystem.read_prop(editor_path[:-3] + 'vmf')) except FileNotFoundError: pass else: editoritems_vmf.load(first_item, editor_vmf) first_item.generate_collisions() # extra_items is any extra blocks (offset catchers, extent items). # These must not have a palette section - it'll override any the user # chooses. for extra_item in extra_items: extra_item.generate_collisions() for subtype in extra_item.subtypes: if subtype.pal_pos is not None: LOGGER.warning( f'"{pak_id}:items/{fold}/editoritems.txt has ' f'palette set for extra item blocks. Deleting.') subtype.pal_icon = subtype.pal_pos = subtype.pal_name = None # In files this is specified as PNG, but it's always really VTF. try: all_icon = FSPath(props['all_icon']).with_suffix('.vtf') except LookupError: all_icon = None folders[fold] = ItemVariant( editoritems=first_item, editor_extra=extra_items, # Add the folder the item definition comes from, # so we can trace it later for debug messages. source=f'<{pak_id}>/items/{fold}', pak_id=pak_id, vbsp_config=lazy_conf.BLANK, authors=sep_values(props['authors', '']), tags=sep_values(props['tags', '']), desc=desc_parse(props, f'{pak_id}:{prop_path}', pak_id), ent_count=props['ent_count', ''], url=props['infoURL', None], icons={ prop.name: img.Handle.parse( prop, pak_id, 64, 64, subfolder='items', ) for prop in props.find_children('icon') }, all_name=props['all_name', None], all_icon=all_icon, ) if Item.log_ent_count and not folders[fold].ent_count: LOGGER.warning( '"{id}:{path}" has missing entity count!', id=pak_id, path=prop_path, ) # If we have one of the grouping icon definitions but not both required # ones then notify the author. has_name = folders[fold].all_name is not None has_icon = folders[fold].all_icon is not None if (has_name or has_icon or 'all' in folders[fold].icons) and (not has_name or not has_icon): LOGGER.warning( 'Warning: "{id}:{path}" has incomplete grouping icon ' 'definition!', id=pak_id, path=prop_path, ) folders[fold].vbsp_config = lazy_conf.from_file( utils.PackagePath(pak_id, config_path), missing_ok=True, source=folders[fold].source, ) return folders
async def parse(cls, data: ParseData): """Parse a style definition.""" info = data.info selitem_data = SelitemData.parse(info, data.pak_id) 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: dict[str, set[str]] = { 'quote': set(), 'music': set(), 'skybox': set(), 'elev': set(), } for prop in info.find_children('suggested'): try: sugg[prop.name].add(prop.value) except KeyError: LOGGER.warning('Unknown suggestion types for style {}: {}', data.id, prop.name) sugg_tup = ( sugg['quote'], sugg['music'], sugg['skybox'], sugg['elev'], ) corr_conf = info.find_key('corridors', or_blank=True) corridors = {} icon_folder = corr_conf['icon_folder', ''] for group, length in CORRIDOR_COUNTS.items(): group_prop = corr_conf.find_key(group, or_blank=True) for i in range(1, length + 1): prop = group_prop.find_key(str(i), '') if icon_folder: icon = utils.PackagePath( data.pak_id, 'corr/{}/{}/{}.jpg'.format(icon_folder, group, i)) else: icon = img.PATH_BLANK 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 LookupError: # It's OK for override styles to be missing their 'folder' # value. if data.is_override: items = [] renderables = {} vbsp = lazy_conf.BLANK else: raise ValueError( f'Style "{data.id}" missing configuration folder!') else: with data.fsys[folder + '/items.txt'].open_str() as f: items, renderables = await trio.to_thread.run_sync( EditorItem.parse, f) vbsp = lazy_conf.from_file( utils.PackagePath(data.pak_id, folder + '/vbsp_config.cfg'), missing_ok=True, source=f'Style <{data.id}>', ) return cls( style_id=data.id, selitem_data=selitem_data, items=items, renderables=renderables, config=vbsp, base_style=base, suggested=sugg_tup, has_video=has_video, corridors=corridors, vpk_name=vpk_name, )