Beispiel #1
0
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)
Beispiel #2
0
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
Beispiel #3
0
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