예제 #1
0
 def __init__(
     self,
     style_id,
     selitem_data: 'SelitemData',
     editor,
     config=None,
     base_style=None,
     suggested=None,
     has_video=True,
     corridor_names=utils.EmptyMapping,
 ):
     self.id = style_id
     self.selitem_data = selitem_data
     self.editor = editor
     self.base_style = base_style
     self.bases = []  # Set by setup_style_tree()
     self.suggested = suggested or {}
     self.has_video = has_video
     self.corridor_names = {
         'sp_entry': corridor_names.get('sp_entry', Property('', [])),
         'sp_exit': corridor_names.get('sp_exit', Property('', [])),
         'coop': corridor_names.get('coop', Property('', [])),
     }
     if config is None:
         self.config = Property(None, [])
     else:
         self.config = config
예제 #2
0
def get_config(prop_block, zip_file, folder, pak_id='', prop_name='config'):
    """Extract a config file refered 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, [])

    path = os.path.join(folder, prop_block.value) + '.cfg'
    try:
        with zip_file.open(path) as f:
            return Property.parse(
                f,
                pak_id + ':' + path,
            )
    except KeyError:
        print('"{}:{}" not in zip!'.format(pak_id, path))
        return Property(None, [])
예제 #3
0
파일: vrad.py 프로젝트: GLiTcH2/BEE2.4
def generate_music_script(data: Property, pack_list):
    """Generate a soundscript file for music."""
    # We also pack the filenames used for the tracks - that way funnel etc
    # only get packed when needed. Stock sounds are in VPKS or in aperturetag/,
    # we don't check there.
    # The voice attrs used in the map - we can skip tracks
    voice_attr = CONF['VoiceAttr', ''].casefold().split(';')

    funnel = data.find_key('tbeam', '')
    bounce = data.find_key('bouncegel', '')
    speed = data.find_key('speedgel', '')

    # The sounds must be present, and the items should be in the map.
    has_funnel = funnel.value and (
        'funnel' in voice_attr or
        'excursionfunnel' in voice_attr
    )
    has_bounce = bounce.value and (
        'bouncegel' in voice_attr or
        'bluegel' in voice_attr
    )
    # Speed-gel sounds also play when flinging, so keep it always.

    with open(os.path.join('bee2', 'inject', 'music_script.txt'), 'w') as file:
        # Write the base music track
        file.write(MUSIC_START.format(name='', vol='1'))
        write_sound(file, data.find_key('base'), pack_list, snd_prefix='#*')
        file.write(MUSIC_BASE)
        # The 'soundoperators' section is still open now.

        # Add the operators to play the auxilluary sounds..
        if has_funnel:
            file.write(MUSIC_FUNNEL_MAIN)
        if has_bounce:
            file.write(MUSIC_GEL_BOUNCE_MAIN)
        if speed.value:
            file.write(MUSIC_GEL_SPEED_MAIN)

        # End the main sound block
        file.write(MUSIC_END)

        if has_funnel:
            # Write the 'music.BEE2_funnel' sound entry
            file.write('\n')
            file.write(MUSIC_START.format(name='_funnel', vol='1'))
            write_sound(file, funnel, pack_list, snd_prefix='*')
            file.write(MUSIC_FUNNEL_STACK)

        if has_bounce:
            file.write('\n')
            file.write(MUSIC_START.format(name='_gel_bounce', vol='0.5'))
            write_sound(file, bounce, pack_list, snd_prefix='*')
            file.write(MUSIC_GEL_STACK)

        if speed.value:
            file.write('\n')
            file.write(MUSIC_START.format(name='_gel_speed', vol='0.5'))
            write_sound(file, speed, pack_list, snd_prefix='*')
            file.write(MUSIC_GEL_STACK)
예제 #4
0
def parse_item_folder(folders, zip_file):
    for fold in folders:
        prop_path = 'items/' + fold + '/properties.txt'
        editor_path = 'items/' + fold + '/editoritems.txt'
        config_path = 'items/' + fold + '/vbsp_config.cfg'
        try:
            with zip_file.open(prop_path, 'r') as prop_file:
                props = Property.parse(
                    prop_file, prop_path,
                ).find_key('Properties')
            with zip_file.open(editor_path, 'r') as editor_file:
                editor = Property.parse(editor_file, editor_path)
        except KeyError as err:
            # Opening the files failed!
            raise IOError(
                '"items/' + fold + '" not valid!'
                'Folder likely missing! '
                ) from err

        editor_iter = Property.find_all(editor, 'Item')
        folders[fold] = {
            'auth':     sep_values(props['authors', '']),
            'tags':     sep_values(props['tags', '']),
            'desc':     list(desc_parse(props)),
            'ent':      props['ent_count', '??'],
            'url':      props['infoURL', None],
            'icons':    {p.name: p.value for p in props['icon', []]},
            'all_name': props['all_name', None],
            'all_icon': props['all_icon', None],
            'vbsp':     Property(None, []),

            # The first Item block found
            'editor': next(editor_iter),
            # Any extra blocks (offset catchers, extent items)
            'editor_extra': list(editor_iter),
        }

        if LOG_ENT_COUNT and folders[fold]['ent'] == '??':
            print('Warning: "{}" has missing entity count!'.format(prop_path))

        # If we have at least 1, but not all of the grouping icon
        # definitions then notify the author.
        num_group_parts = (
            (folders[fold]['all_name'] is not None)
            + (folders[fold]['all_icon'] is not None)
            + ('all' in folders[fold]['icons'])
        )
        if 0 < num_group_parts < 3:
            print(
                'Warning: "{}" has incomplete grouping icon definition!'.format(
                    prop_path)
            )

        try:
            with zip_file.open(config_path, 'r') as vbsp_config:
                folders[fold]['vbsp'] = Property.parse(vbsp_config, config_path)
        except KeyError:
            folders[fold]['vbsp'] = Property(None, [])
예제 #5
0
    def parse(cls, data):
        """Parse a style definition."""
        info = data.info
        selitem_data = get_selitem_data(info)
        base = info['base', '']
        has_video = utils.conv_bool(info['has_video', '1'])

        sugg = info.find_key('suggested', [])
        sugg = (
            sugg['quote', '<NONE>'],
            sugg['music', '<NONE>'],
            sugg['skybox', 'SKY_BLACK'],
            sugg['goo', 'GOO_NORM'],
            sugg['elev', '<NONE>'],
            )

        corridors = info.find_key('corridors', [])
        corridors = {
            'sp_entry': corridors.find_key('sp_entry', []),
            'sp_exit':  corridors.find_key('sp_exit', []),
            'coop':     corridors.find_key('coop', []),
        }

        short_name = selitem_data.short_name or None
        if base == '':
            base = None
        folder = 'styles/' + info['folder']
        config = folder + '/vbsp_config.cfg'
        with data.zip_file.open(folder + '/items.txt', 'r') as item_data:
            items = Property.parse(
                item_data,
                data.pak_id+':'+folder+'/items.txt'
            )

        try:
            with data.zip_file.open(config, 'r') as vbsp_config:
                vbsp = Property.parse(
                    vbsp_config,
                    data.pak_id+':'+config,
                )
        except KeyError:
            vbsp = None
        return cls(
            style_id=data.id,
            name=selitem_data.name,
            author=selitem_data.auth,
            desc=selitem_data.desc,
            icon=selitem_data.icon,
            editor=items,
            config=vbsp,
            base_style=base,
            short_name=short_name,
            suggested=sugg,
            has_video=has_video,
            corridor_names=corridors,
            )
예제 #6
0
파일: vrad.py 프로젝트: GLiTcH2/BEE2.4
def gen_sound_manifest(additional, excludes):
    """Generate a new game_sounds_manifest.txt file.

    This includes all the current scripts defined, plus any custom ones.
    Excludes is a list of scripts to remove from the listing - this allows
    overriding the sounds without VPK overrides.
    """
    if not additional:
        return  # Don't pack, there aren't any new sounds..

    orig_manifest = os.path.join(
        '..',
        SOUND_MAN_FOLDER.get(CONF['game_id', ''], 'portal2'),
        'scripts',
        'game_sounds_manifest.txt',
    )

    try:
        with open(orig_manifest) as f:
            props = Property.parse(f, orig_manifest).find_key(
                'game_sounds_manifest', [],
            )
    except FileNotFoundError:  # Assume no sounds
        props = Property('game_sounds_manifest', [])

    scripts = [prop.value for prop in props.find_all('precache_file')]

    for script in additional:
        scripts.append(script)

        # For our packed scripts, force the game to load them
        # (we know they're used).
        scripts.append('!' + script)

    for script in excludes:
        try:
            scripts.remove(script)
        except ValueError:
            LOGGER.warning(
                '"{}" should be excluded, but it\'s'
                ' not in the manifest already!',
                script,
            )

    # Build and unbuild it to strip other things out - Valve includes a bogus
    # 'new_sound_scripts_must_go_below_here' entry..
    new_props = Property('game_sounds_manifest', [
        Property('precache_file', file)
        for file in scripts
    ])

    inject_loc = os.path.join('bee2', 'inject', 'soundscript_manifest.txt')
    with open(inject_loc, 'w') as f:
        for line in new_props.export():
            f.write(line)
    LOGGER.info('Written new soundscripts_manifest..')
예제 #7
0
    def parse(cls, data):
        """Parse a style definition."""
        info = data.info
        selitem_data = get_selitem_data(info)
        base = info['base', '']
        has_video = utils.conv_bool(info['has_video', '1'])

        sugg = info.find_key('suggested', [])
        sugg = (
            sugg['quote', '<NONE>'],
            sugg['music', '<NONE>'],
            sugg['skybox', 'SKY_BLACK'],
            sugg['goo', 'GOO_NORM'],
            sugg['elev', '<NONE>'],
        )

        corridors = info.find_key('corridors', [])
        corridors = {
            'sp_entry': corridors.find_key('sp_entry', []),
            'sp_exit': corridors.find_key('sp_exit', []),
            'coop': corridors.find_key('coop', []),
        }

        if base == '':
            base = None
        folder = 'styles/' + info['folder']
        config = folder + '/vbsp_config.cfg'
        with data.zip_file.open(folder + '/items.txt', 'r') as item_data:
            items = Property.parse(item_data,
                                   data.pak_id + ':' + folder + '/items.txt')

        try:
            with data.zip_file.open(config, 'r') as vbsp_config:
                vbsp = Property.parse(
                    vbsp_config,
                    data.pak_id + ':' + config,
                )
        except KeyError:
            vbsp = None
        return cls(
            style_id=data.id,
            selitem_data=selitem_data,
            editor=items,
            config=vbsp,
            base_style=base,
            suggested=sugg,
            has_video=has_video,
            corridor_names=corridors,
        )
예제 #8
0
def get_config(prop_block, zip_file, folder, pak_id='', prop_name='config'):
    """Extract a config file refered 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, [])

    path = os.path.join(folder, prop_block.value) + '.cfg'
    try:
        with zip_file.open(path) as f:
            return Property.parse(f,
            pak_id + ':' + path,
            )
    except KeyError:
        print('"{}:{}" not in zip!'.format(pak_id, path))
        return Property(None, [])
예제 #9
0
 def __init__(
         self,
         style_id,
         name,
         author,
         desc,
         icon,
         editor,
         config=None,
         base_style=None,
         short_name=None,
         suggested=None,
         has_video=True,
         corridor_names=utils.EmptyMapping,
         ):
     self.id = style_id
     self.auth = author
     self.name = name
     self.desc = desc
     self.icon = icon
     self.short_name = name if short_name is None else short_name
     self.editor = editor
     self.base_style = base_style
     self.bases = []  # Set by setup_style_tree()
     self.suggested = suggested or {}
     self.has_video = has_video
     self.corridor_names = {
         'sp_entry': corridor_names.get('sp_entry', Property('', [])),
         'sp_exit':  corridor_names.get('sp_exit', Property('', [])),
         'coop':     corridor_names.get('coop', Property('', [])),
     }
     if config is None:
         self.config = Property(None, [])
     else:
         self.config = config
예제 #10
0
 def __init__(
         self,
         style_id,
         selitem_data: 'SelitemData',
         editor,
         config=None,
         base_style=None,
         suggested=None,
         has_video=True,
         corridor_names=utils.EmptyMapping,
         ):
     self.id = style_id
     self.selitem_data = selitem_data
     self.editor = editor
     self.base_style = base_style
     self.bases = []  # Set by setup_style_tree()
     self.suggested = suggested or {}
     self.has_video = has_video
     self.corridor_names = {
         'sp_entry': corridor_names.get('sp_entry', Property('', [])),
         'sp_exit':  corridor_names.get('sp_exit', Property('', [])),
         'coop':     corridor_names.get('coop', Property('', [])),
     }
     if config is None:
         self.config = Property(None, [])
     else:
         self.config = config
예제 #11
0
def parse(posfile, propfile, path):
    "Parse through the given palette file to get all data."
    props = Property.parse(propfile, path + ':properties.txt')
    name = "Unnamed"
    opts = {}
    for option in props:
        if option.name == "name":
            name = option.value
        else:
            opts[option.name.casefold()] = option.value
    pos = []
    for dirty_line in posfile:
        line = utils.clean_line(dirty_line)
        if line:
            # Lines follow the form
            # "ITEM_BUTTON_FLOOR", 2
            # for subtype 3 of the button
            if line.startswith('"'):
                val = line.split('",')
                if len(val) == 2:
                    pos.append((
                        val[0][1:], # Item ID
                        int(val[1].strip()), # Item subtype
                        ))
                else:
                    LOGGER.warning('Malformed row "{}"!', line)
                    return None
    return Palette(name, pos, opts, filename=path)
예제 #12
0
def load_conf():
    """Read the config and build our dictionaries."""
    global INST_SPECIAL
    with open('bee2/instances.cfg') as f:
        prop_block = Property.parse(
            f, 'bee2/instances.cfg'
        ).find_key('Allinstances')

    for prop in prop_block:
        INSTANCE_FILES[prop.real_name] = [
            inst.value.casefold()
            for inst in
            prop
        ]

    INST_SPECIAL = {
        key.casefold(): resolve(val_string)
        for key, val_string in
        SPECIAL_INST.items()
    }

    INST_SPECIAL['indpan'] = (
        INST_SPECIAL['indpancheck'] +
        INST_SPECIAL['indpantimer']
    )
    INST_SPECIAL['white_frames'] = (
        resolve('<ITEM_ENTRY_DOOR:7>') + resolve('<ITEM_EXIT_DOOR:4>')
    )
    INST_SPECIAL['black_frames'] = (
        resolve('<ITEM_ENTRY_DOOR:8>') + resolve('<ITEM_EXIT_DOOR:5>')
    )
예제 #13
0
 def parse(cls, data):
     """Parse a skybox definition."""
     config_dir = data.info['config', '']
     selitem_data = get_selitem_data(data.info)
     mat = data.info['material', 'sky_black']
     if config_dir == '':  # No config at all
         config = Property(None, [])
     else:
         path = 'skybox/' + config_dir + '.cfg'
         try:
             with data.zip_file.open(path, 'r') as conf:
                 config = Property.parse(conf)
         except KeyError:
             print(config_dir + '.cfg not in zip!')
             config = Property(None, [])
     return cls(
         data.id,
         selitem_data.name,
         selitem_data.icon,
         config,
         mat,
         selitem_data.auth,
         selitem_data.desc,
         selitem_data.short_name,
     )
예제 #14
0
def parse(posfile, propfile, path):
    "Parse through the given palette file to get all data."
    props = Property.parse(propfile, path + ':properties.txt')
    name = "Unnamed"
    opts = {}
    for option in props:
        if option.name == "name":
            name = option.value
        else:
            opts[option.name.casefold()] = option.value
    pos = []
    for dirty_line in posfile:
        line = utils.clean_line(dirty_line)
        if line:
            # Lines follow the form
            # "ITEM_BUTTON_FLOOR", 2
            # for subtype 3 of the button
            if line.startswith('"'):
                val = line.split('",')
                if len(val) == 2:
                    pos.append((
                        val[0][1:], # Item ID
                        int(val[1].strip()), # Item subtype
                        ))
                else:
                    print("Malformed row '"+line+"'!")
                    return None
    return Palette(name, pos, opts, filename=path)
예제 #15
0
def load_conf(prop_block: Property):
    """Read the config and build our dictionaries."""
    global INST_SPECIAL

    for prop in prop_block.find_key("Allinstances"):
        INSTANCE_FILES[prop.real_name] = [inst.value.casefold() for inst in prop]
    INST_SPECIAL = {key.casefold(): resolve(val_string) for key, val_string in SPECIAL_INST.items()}

    # Several special items which use multiple item types!

    # Checkmark and Timer indicator panels:
    INST_SPECIAL["indpan"] = INST_SPECIAL["indpancheck"] + INST_SPECIAL["indpantimer"]

    INST_SPECIAL["door_frame"] = INST_SPECIAL["door_frame_sp"] + INST_SPECIAL["door_frame_coop"]

    INST_SPECIAL["white_frame"] = INST_SPECIAL["white_frame_sp"] + INST_SPECIAL["white_frame_coop"]

    INST_SPECIAL["black_frame"] = INST_SPECIAL["black_frame_sp"] + INST_SPECIAL["black_frame_coop"]

    # Arrival_departure_ents is set in both entry doors - it's usually the same
    # though.
    INST_SPECIAL["transitionents"] = resolve("<ITEM_ENTRY_DOOR:11>") + resolve("<ITEM_COOP_ENTRY_DOOR:4>")

    # Laser items have the offset and centered item versions.
    INST_SPECIAL["lasercatcher"] = resolve("<ITEM_LASER_CATCHER_CENTER>") + resolve("<ITEM_LASER_CATCHER_OFFSET>")

    INST_SPECIAL["laseremitter"] = resolve("<ITEM_LASER_EMITTER_CENTER>") + resolve("<ITEM_LASER_EMITTER_OFFSET>")

    INST_SPECIAL["laserrelay"] = resolve("<ITEM_LASER_RELAY_CENTER>") + resolve("<ITEM_LASER_RELAY_OFFSET>")
예제 #16
0
 def __init__(
         self,
         style_id,
         name,
         author,
         desc,
         icon,
         editor,
         config=None,
         base_style=None,
         short_name=None,
         suggested=None,
         has_video=True,
         ):
     self.id = style_id
     self.auth = author
     self.name = name
     self.desc = desc
     self.icon = icon
     self.short_name = name if short_name is None else short_name
     self.editor = editor
     self.base_style = base_style
     self.bases = []  # Set by setup_style_tree()
     self.suggested = suggested or {}
     self.has_video = has_video
     if config is None:
         self.config = Property(None, [])
     else:
         self.config = config
예제 #17
0
def find_packages(pak_dir, zips, zip_name_lst):
    """Search a folder for packages, recursing if necessary."""
    found_pak = False
    for name in os.listdir(pak_dir): # Both files and dirs
        name = os.path.join(pak_dir, name)
        is_dir = os.path.isdir(name)
        if name.endswith('.zip') and os.path.isfile(name):
            zip_file = ZipFile(name)
        elif is_dir:
            zip_file = FakeZip(name)
        if 'info.txt' in zip_file.namelist():  # Is it valid?
            zips.append(zip_file)
            zip_name_lst.append(os.path.abspath(name))
            print('Reading package "' + name + '"')
            with zip_file.open('info.txt') as info_file:
                info = Property.parse(info_file, name + ':info.txt')
            pak_id = info['ID']
            disp_name = info['Name', pak_id]
            packages[pak_id] = PackageData(
                zip_file,
                info,
                name,
                disp_name,
            )
            found_pak = True
        else:
            if is_dir:
                # This isn't a package, so check the subfolders too...
                print('Checking subdir "{}" for packages...'.format(name))
                find_packages(name, zips, zip_name_lst)
            else:
                zip_file.close()
                print('ERROR: Bad package "{}"!'.format(name))
    if not found_pak:
        print('No packages in folder!')
예제 #18
0
def load_config():
    global CONF
    utils.con_log('Loading Settings...')
    try:
        with open("bee2/vrad_config.cfg") as config:
            CONF = Property.parse(config, 'bee2/vrad_config.cfg').find_key(
                'Config', [])
    except FileNotFoundError:
        pass
    utils.con_log('Config Loaded!')
예제 #19
0
파일: vrad.py 프로젝트: goodDOS/BEE2.4
def load_config():
    global CONF
    LOGGER.info('Loading Settings...')
    try:
        with open("bee2/vrad_config.cfg") as config:
            CONF = Property.parse(config, 'bee2/vrad_config.cfg').find_key(
                'Config', []
            )
    except FileNotFoundError:
        pass
    LOGGER.info('Config Loaded!')
예제 #20
0
파일: vrad.py 프로젝트: GLiTcH2/BEE2.4
def gen_part_manifest(additional):
    """Generate a new particle system manifest file.

    This includes all the current ones defined, plus any custom ones.
    """
    if not additional:
        return  # Don't pack, there aren't any new particles..

    orig_manifest = os.path.join(
        '..',
        GAME_FOLDER.get(CONF['game_id', ''], 'portal2'),
        'particles',
        'particles_manifest.txt',
    )

    try:
        with open(orig_manifest) as f:
            props = Property.parse(f, orig_manifest).find_key(
                'particles_manifest', [],
            )
    except FileNotFoundError:  # Assume no particles
        props = Property('particles_manifest', [])

    parts = [prop.value for prop in props.find_all('file')]

    for particle in additional:
        parts.append(particle)

    # Build and unbuild it to strip comments and similar lines.
    new_props = Property('particles_manifest', [
        Property('file', file)
        for file in parts
    ])

    inject_loc = os.path.join('bee2', 'inject', 'particles_manifest.txt')
    with open(inject_loc, 'w') as f:
        for line in new_props.export():
            f.write(line)

    LOGGER.info('Written new particles_manifest..')
예제 #21
0
def parse_item_folder(folders, zip_file, pak_id):
    for fold in folders:
        prop_path = 'items/' + fold + '/properties.txt'
        editor_path = 'items/' + fold + '/editoritems.txt'
        config_path = 'items/' + fold + '/vbsp_config.cfg'
        try:
            with zip_file.open(prop_path, 'r') as prop_file:
                props = Property.parse(
                    prop_file,
                    pak_id + ':' + prop_path,
                ).find_key('Properties')
            with zip_file.open(editor_path, 'r') as editor_file:
                editor = Property.parse(editor_file,
                                        pak_id + ':' + editor_path)
        except KeyError as err:
            # Opening the files failed!
            raise IOError('"' + pak_id + ':items/' + fold + '" not valid!'
                          'Folder likely missing! ') from err

        editor_iter = Property.find_all(editor, 'Item')
        folders[fold] = {
            'auth': sep_values(props['authors', '']),
            'tags': sep_values(props['tags', '']),
            'desc': list(desc_parse(props)),
            'ent': props['ent_count', '??'],
            'url': props['infoURL', None],
            'icons': {p.name: p.value
                      for p in props['icon', []]},
            'all_name': props['all_name', None],
            'all_icon': props['all_icon', None],
            'vbsp': Property(None, []),

            # The first Item block found
            'editor': next(editor_iter),
            # Any extra blocks (offset catchers, extent items)
            'editor_extra': list(editor_iter),
        }

        if LOG_ENT_COUNT and folders[fold]['ent'] == '??':
            print('Warning: "{}:{}" has missing entity count!'.format(
                pak_id,
                prop_path,
            ))

        # If we have at least 1, but not all of the grouping icon
        # definitions then notify the author.
        num_group_parts = ((folders[fold]['all_name'] is not None) +
                           (folders[fold]['all_icon'] is not None) +
                           ('all' in folders[fold]['icons']))
        if 0 < num_group_parts < 3:
            print('Warning: "{}:{}" has incomplete grouping icon '
                  'definition!'.format(pak_id, prop_path))
        try:
            with zip_file.open(config_path, 'r') as vbsp_config:
                folders[fold]['vbsp'] = Property.parse(
                    vbsp_config,
                    pak_id + ':' + config_path,
                )
        except KeyError:
            folders[fold]['vbsp'] = Property(None, [])
예제 #22
0
파일: gameMan.py 프로젝트: GLiTcH2/BEE2.4
def init_trans():
    """Load a copy of basemodui, used to translate item strings.

    Valve's items use special translation strings which would look ugly
    if we didn't convert them.
    """
    try:
        with open('../basemodui.txt') as trans:
            trans_prop = Property.parse(trans, 'basemodui.txt')
        for item in trans_prop.find_key("lang", []).find_key("tokens", []):
            trans_data[item.real_name] = item.value
    except IOError:
        pass
예제 #23
0
def parse_package(zip_file, info, pak_id, disp_name):
    """Parse through the given package to find all the components."""
    for pre in Property.find_key(info, 'Prerequisites', []).value:
        if pre.value not in packages:
            utils.con_log(
                'Package "' +
                pre.value +
                '" required for "' +
                pak_id +
                '" - ignoring package!'
            )
            return False
    objects = 0
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in OBJ_TYPES:
        allow_dupes = OBJ_TYPES[comp_type].allow_mult
        # Look for overrides
        for obj in info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                ParseData(zip_file, obj_id, obj, pak_id)
            )

        for obj in info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                if allow_dupes:
                    # Pretend this is an override
                    obj_override[comp_type][obj_id].append(
                        ParseData(zip_file, obj_id, obj, pak_id)
                    )
                else:
                    raise Exception('ERROR! "' + obj_id + '" defined twice!')
            objects += 1
            all_obj[comp_type][obj_id] = ObjData(
                zip_file,
                obj,
                pak_id,
                disp_name,
            )

    img_count = 0
    img_loc = os.path.join('resources', 'bee2')
    for item in zip_names(zip_file):
        item = os.path.normcase(item).casefold()
        if item.startswith("resources"):
            extract_packages.res_count += 1
            if item.startswith(img_loc):
                img_count += 1
    return objects, img_count
예제 #24
0
    def __init__(
        self,
        music_id,
        selitem_data: 'SelitemData',
        config=None,
        inst=None,
        sound=None,
    ):
        self.id = music_id
        self.config = config or Property(None, [])
        self.inst = inst
        self.sound = sound

        self.selitem_data = selitem_data
예제 #25
0
파일: vrad.py 프로젝트: SpyyZ158/BEE2.4
def gen_sound_manifest(additional, has_music=False):
    """Generate a new game_sounds_manifest.txt file.

    This includes all the current scripts defined, plus any custom ones.
    """
    orig_manifest = os.path.join(
        '..',
        SOUND_MAN_FOLDER.get(CONF['game_id', ''], 'portal2'),
        'scripts',
        'game_sounds_manifest.txt',
    )
    try:
        with open(orig_manifest) as f:
            props = Property.parse(f, orig_manifest).find_key(
                'game_sounds_manifest', [],
            )
    except FileNotFoundError:  # Assume no sounds
        props = Property('game_sounds_manifest', [])

    scripts = [prop.value for prop in props.find_all('precache_file')]

    for script in additional:
        scripts.append(script)

    # Build and unbuild it to strip other things out - Valve includes a bogus
    # 'new_sound_scripts_must_go_below_here' entry..
    new_props = Property('game_sounds_manifest', [
        Property('precache_file', file)
        for file in scripts
    ])

    inject_loc = os.path.join('bee2', 'inject', 'soundscript_manifest.txt')
    with open(inject_loc, 'w') as f:
        for line in new_props.export():
            f.write(line)
    LOGGER.info('Written new soundscripts_manifest..')
예제 #26
0
def parse_package(pack: 'Package'):
    """Parse through the given package to find all the components."""
    for pre in Property.find_key(pack.info, 'Prerequisites', []):
        if pre.value not in packages:
            LOGGER.warning(
                'Package "{pre}" required for "{id}" - '
                'ignoring package!',
                pre=pre.value,
                id=pack.id,
            )
            return False
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in OBJ_TYPES:
        allow_dupes = OBJ_TYPES[comp_type].allow_mult
        # Look for overrides
        for obj in pack.info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                ParseData(pack.zip, obj_id, obj, pack.id)
            )

        for obj in pack.info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                if allow_dupes:
                    # Pretend this is an override
                    obj_override[comp_type][obj_id].append(
                        ParseData(pack.zip, obj_id, obj, pack.id)
                    )
                else:
                    raise Exception('ERROR! "' + obj_id + '" defined twice!')
            all_obj[comp_type][obj_id] = ObjData(
                pack.zip,
                obj,
                pack.id,
                pack.disp_name,
            )

    img_count = 0
    img_loc = os.path.join('resources', 'bee2')
    for item in zip_names(pack.zip):
        item = os.path.normcase(item).casefold()
        if item.startswith("resources"):
            extract_packages.res_count += 1
            if item.startswith(img_loc):
                img_count += 1
    return img_count
예제 #27
0
    def parse(cls, data):
        """Parse a voice line definition."""
        selitem_data = get_selitem_data(data.info)
        path = 'voice/' + data.info['file'] + '.voice'
        with data.zip_file.open(path, 'r') as conf:
            config = Property.parse(conf, path)

        return cls(
            data.id,
            selitem_data.name,
            config,
            selitem_data.icon,
            selitem_data.desc,
            auth=selitem_data.auth,
            short_name=selitem_data.short_name
            )
예제 #28
0
def init_trans():
    """Load a copy of basemodui, used to translate item strings.

    Valve's items use special translation strings which would look ugly
    if we didn't convert them.
    """
    global trans_data
    try:
        with open('../basemodui.txt') as trans:
            trans_prop = Property.parse(trans, 'basemodui.txt')
        trans_data = {
            item.real_name: item.value
            for item in trans_prop.find_key("lang", []).find_key("tokens", [])
        }
    except IOError:
        pass
예제 #29
0
def load_conf():
    """Read the config and build our dictionaries."""
    global INST_SPECIAL
    with open("bee2/instances.cfg") as f:
        prop_block = Property.parse(f, "bee2/instances.cfg").find_key("Allinstances")

    for prop in prop_block:
        INSTANCE_FILES[prop.real_name] = [inst.value.casefold() for inst in prop]
    INST_SPECIAL = {key.casefold(): resolve(val_string) for key, val_string in SPECIAL_INST.items()}

    INST_SPECIAL["indpan"] = INST_SPECIAL["indpancheck"] + INST_SPECIAL["indpantimer"]

    INST_SPECIAL["lasercatcher"] = resolve("<ITEM_LASER_CATCHER_CENTER>") + resolve("<ITEM_LASER_CATCHER_OFFSET>")

    INST_SPECIAL["laseremitter"] = resolve("<ITEM_LASER_EMITTER_CENTER>") + resolve("<ITEM_LASER_EMITTER_OFFSET>")

    INST_SPECIAL["laserrelay"] = resolve("<ITEM_LASER_RELAY_CENTER>") + resolve("<ITEM_LASER_RELAY_OFFSET>")
예제 #30
0
 def __init__(self,
              item_id,
              versions,
              def_version,
              needs_unlock=False,
              all_conf=None,
              unstyled=False,
              glob_desc=(),
              desc_last=False):
     self.id = item_id
     self.versions = versions
     self.def_ver = def_version
     self.def_data = def_version['def_style']
     self.needs_unlock = needs_unlock
     self.all_conf = all_conf or Property(None, [])
     self.unstyled = unstyled
     self.glob_desc = glob_desc
     self.glob_desc_last = desc_last
예제 #31
0
def parse_package(zip_file, info, pak_id, disp_name):
    """Parse through the given package to find all the components."""
    for pre in Property.find_key(info, 'Prerequisites', []).value:
        if pre.value not in packages:
            utils.con_log('Package "' + pre.value + '" required for "' +
                          pak_id + '" - ignoring package!')
            return False
    objects = 0
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in OBJ_TYPES:
        allow_dupes = OBJ_TYPES[comp_type].allow_mult
        # Look for overrides
        for obj in info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                ParseData(zip_file, obj_id, obj, pak_id))

        for obj in info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                if allow_dupes:
                    # Pretend this is an override
                    obj_override[comp_type][obj_id].append(
                        ParseData(zip_file, obj_id, obj, pak_id))
                else:
                    raise Exception('ERROR! "' + obj_id + '" defined twice!')
            objects += 1
            all_obj[comp_type][obj_id] = ObjData(
                zip_file,
                obj,
                pak_id,
                disp_name,
            )

    img_count = 0
    img_loc = os.path.join('resources', 'bee2')
    for item in zip_names(zip_file):
        item = os.path.normcase(item).casefold()
        if item.startswith("resources"):
            extract_packages.res_count += 1
            if item.startswith(img_loc):
                img_count += 1
    return objects, img_count
예제 #32
0
def load_conf():
    """Read the config and build our dictionaries."""
    global INST_SPECIAL
    with open('bee2/instances.cfg') as f:
        prop_block = Property.parse(
            f, 'bee2/instances.cfg'
        ).find_key('Allinstances')

    for prop in prop_block:
        INSTANCE_FILES[prop.real_name] = [
            inst.value.casefold()
            for inst in
            prop
        ]
    INST_SPECIAL = {
        key.casefold(): resolve(val_string)
        for key, val_string in
        SPECIAL_INST.items()
    }

    INST_SPECIAL['indpan'] = (
        INST_SPECIAL['indpancheck'] +
        INST_SPECIAL['indpantimer']
    )

    INST_SPECIAL['transitionents'] = (
        resolve('<ITEM_ENTRY_DOOR:11>') +
        resolve('<ITEM_COOP_ENTRY_DOOR:4>')
    )

    INST_SPECIAL['lasercatcher'] = (
        resolve('<ITEM_LASER_CATCHER_CENTER>') +
        resolve('<ITEM_LASER_CATCHER_OFFSET>')
    )

    INST_SPECIAL['laseremitter'] = (
        resolve('<ITEM_LASER_EMITTER_CENTER>') +
        resolve('<ITEM_LASER_EMITTER_OFFSET>')
    )

    INST_SPECIAL['laserrelay'] = (
        resolve('<ITEM_LASER_RELAY_CENTER>') +
        resolve('<ITEM_LASER_RELAY_OFFSET>')
    )
예제 #33
0
def flag_random(inst, res: Property):
    """Randomly is either true or false."""
    if res.has_children():
        chance = res['chance', '100']
        seed = res['seed', '']
    else:
        chance = res.value
        seed = ''

    # Allow ending with '%' sign
    chance = utils.conv_int(chance.rstrip('%'), 100)

    random.seed('random_chance_{}:{}_{}_{}'.format(
        seed,
        inst['targetname', ''],
        inst['origin'],
        inst['angles'],
    ))
    return random.randrange(100) < chance
예제 #34
0
def parse_package(zip_file, info, pak_id, disp_name):
    """Parse through the given package to find all the components."""
    global res_count
    for pre in Property.find_key(info, 'Prerequisites', []).value:
        if pre.value not in packages:
            utils.con_log(
                'Package "' +
                pre.value +
                '" required for "' +
                pak_id +
                '" - ignoring package!'
            )
            return False
    objects = 0
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in obj_types:
        # Look for overrides
        for obj in info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                (zip_file, obj)
            )

        for obj in info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                raise Exception('ERROR! "' + obj_id + '" defined twice!')
            objects += 1
            all_obj[comp_type][obj_id] = ObjData(
                zip_file,
                obj,
                pak_id,
                disp_name,
            )

    if res_count != -1:
        for item in zip_names(zip_file):
            if item.startswith("resources"):
                res_count += 1
        loader.set_length("RES", res_count)
    return objects
예제 #35
0
def get_config(
        prop_block,
        zip_file,
        folder,
        pak_id='',
        prop_name='config',
        extension='.cfg',
        ):
    """Extract a config file refered 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 seperator, even on Windows!
    path = folder + '/' + prop_block.value
    if len(path) < 3 or path[-4] != '.':
        # Add extension
        path += extension
    try:
        with zip_file.open(path) as f:
            return Property.parse(
                f,
                pak_id + ':' + path,
            )
    except KeyError:
        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
예제 #36
0
def find_packages(pak_dir, zips, zip_name_lst):
    """Search a folder for packages, recursing if necessary."""
    found_pak = False
    for name in os.listdir(pak_dir):  # Both files and dirs
        name = os.path.join(pak_dir, name)
        is_dir = os.path.isdir(name)
        if name.endswith('.zip') and os.path.isfile(name):
            zip_file = ZipFile(name)
        elif is_dir:
            zip_file = FakeZip(name)
        else:
            utils.con_log('Extra file: ', name)
            continue

        if 'info.txt' in zip_file.namelist():  # Is it valid?
            zips.append(zip_file)
            zip_name_lst.append(os.path.abspath(name))
            print('Reading package "' + name + '"')
            with zip_file.open('info.txt') as info_file:
                info = Property.parse(info_file, name + ':info.txt')
            pak_id = info['ID']
            disp_name = info['Name', pak_id]
            packages[pak_id] = PackageData(
                zip_file,
                info,
                name,
                disp_name,
            )
            found_pak = True
        else:
            if is_dir:
                # This isn't a package, so check the subfolders too...
                print('Checking subdir "{}" for packages...'.format(name))
                find_packages(name, zips, zip_name_lst)
            else:
                zip_file.close()
                print('ERROR: Bad package "{}"!'.format(name))
    if not found_pak:
        print('No packages in folder!')
예제 #37
0
파일: backup.py 프로젝트: SpyyZ158/BEE2.4
    def from_file(cls, path, zip_file):
        """Initialise from a file.

        path is the file path for the map inside the zip, without extension.
        zip_file is either a ZipFile or FakeZip object.
        """
        with zip_file.open(path + '.p2c') as file:
            props = Property.parse(file, path)
        props = props.find_key('portal2_puzzle', [])

        title = props['title', None]
        if title is None:
            title = '<' + path.rsplit('/', 1)[-1] + '.p2c>'

        return cls(
            path=path,
            zip_file = zip_file,
            title=title,
            desc=props['description', '...'],
            is_coop=utils.conv_bool(props['coop', '0']),
            create_time=Date(props['timestamp_created', '']),
            mod_time=Date(props['timestamp_modified', '']),
        )
예제 #38
0
    def parse(cls, data):
        """Parse a music definition."""
        selitem_data = get_selitem_data(data.info)
        inst = data.info['instance', None]
        sound = data.info['soundscript', None]

        config_dir = 'music/' + data.info['config', '']
        try:
            with data.zip_file.open(config_dir) as conf:
                config = Property.parse(conf, config_dir)
        except KeyError:
            config = Property(None, [])
        return cls(
            data.id,
            selitem_data.name,
            selitem_data.icon,
            selitem_data.auth,
            selitem_data.desc,
            short_name=selitem_data.short_name,
            inst=inst,
            sound=sound,
            config=config,
            )
예제 #39
0
파일: vrad.py 프로젝트: SpyyZ158/BEE2.4
def write_sound(file, snds: Property, pack_list, snd_prefix='*'):
    """Write either a single sound, or multiple rndsound.

    snd_prefix is the prefix for each filename - *, #, @, etc.
    """
    if snds.has_children():
        file.write(' "rndwave"\n  {\n')
        for snd in snds:
            file.write(
                '  "wave" "{sndchar}{file}"\n'.format(
                    file=snd.value,
                    sndchar=snd_prefix,
                )
            )
            pack_list.add('sound/' + snd.value.casefold())
        file.write('  }\n')
    else:
        file.write(
            ' "wave" "{sndchar}{file}"\n'.format(
                file=snds.value,
                sndchar=snd_prefix,
            )
        )
        pack_list.add('sound/' + snds.value.casefold())
예제 #40
0
파일: backup.py 프로젝트: Coolasp1e/BEE2.4
    def from_file(cls, path, zip_file):
        """Initialise from a file.

        path is the file path for the map inside the zip, without extension.
        zip_file is either a ZipFile or FakeZip object.
        """
        # Some P2Cs may have non-ASCII characters in descriptions, so we
        # need to read it as bytes and convert to utf-8 ourselves - zips
        # don't convert encodings automatically for us.
        with zip_open_bin(zip_file, path + '.p2c') as file:
            props = Property.parse(
                # Decode the P2C as UTF-8, and skip unknown characters.
                # We're only using it for display purposes, so that should
                # be sufficent.
                EncodedFile(
                    file,
                    data_encoding='utf-8',
                    errors='replace',
                ),
                path,
            )
        props = props.find_key('portal2_puzzle', [])

        title = props['title', None]
        if title is None:
            title = '<' + path.rsplit('/', 1)[-1] + '.p2c>'

        return cls(
            path=path,
            zip_file=zip_file,
            title=title,
            desc=props['description', '...'],
            is_coop=utils.conv_bool(props['coop', '0']),
            create_time=Date(props['timestamp_created', '']),
            mod_time=Date(props['timestamp_modified', '']),
        )
예제 #41
0
def load_conf(prop_block: Property):
    """Read the config and build our dictionaries."""
    global INST_SPECIAL

    for prop in prop_block.find_key('Allinstances', []):
        INSTANCE_FILES[prop.real_name] = [
            inst.value.casefold()
            for inst in
            prop
        ]

    for prop in prop_block.find_key('CustInstances', []):
        CUST_INST_FILES[prop.real_name] = {
            inst.name: inst.value.casefold()
            for inst in
            prop
        }

    INST_SPECIAL = {
        key.casefold(): resolve(val_string, silent=True)
        for key, val_string in
        SPECIAL_INST.items()
    }

    # Several special items which use multiple item types!

    # Checkmark and Timer indicator panels:
    INST_SPECIAL['indpan'] = (
        INST_SPECIAL['indpancheck'] +
        INST_SPECIAL['indpantimer']
    )

    INST_SPECIAL['door_frame'] = (
        INST_SPECIAL['door_frame_sp'] +
        INST_SPECIAL['door_frame_coop']
    )

    INST_SPECIAL['white_frame'] = (
        INST_SPECIAL['white_frame_sp'] +
        INST_SPECIAL['white_frame_coop']
    )

    INST_SPECIAL['black_frame'] = (
        INST_SPECIAL['black_frame_sp'] +
        INST_SPECIAL['black_frame_coop']
    )

    # Arrival_departure_ents is set in both entry doors - it's usually the same
    # though.
    INST_SPECIAL['transitionents'] = (
        resolve('<ITEM_ENTRY_DOOR:11>') +
        resolve('<ITEM_COOP_ENTRY_DOOR:4>')
    )

    # Laser items have the offset and centered item versions.
    INST_SPECIAL['lasercatcher'] = (
        resolve('<ITEM_LASER_CATCHER_CENTER>', silent=True) +
        resolve('<ITEM_LASER_CATCHER_OFFSET>', silent=True)
    )

    INST_SPECIAL['laseremitter'] = (
        resolve('<ITEM_LASER_EMITTER_CENTER>', silent=True) +
        resolve('<ITEM_LASER_EMITTER_OFFSET>', silent=True)
    )

    INST_SPECIAL['laserrelay'] = (
        resolve('<ITEM_LASER_RELAY_CENTER>', silent=True) +
        resolve('<ITEM_LASER_RELAY_OFFSET>', silent=True)
    )

    LOGGER.warning('None in vals: {}', None in INST_SPECIAL.values())
예제 #42
0
    def export(
        self,
        style,
        all_items,
        music,
        skybox,
        voice,
        style_vars,
        elevator,
        pack_list,
        editor_sounds,
        should_refresh=False,
    ):
        """Generate the editoritems.txt and vbsp_config.

        - If no backup is present, the original editoritems is backed up
        - We unlock the mandatory items if specified
        -
        """
        print('-' * 20)
        print('Exporting Items and Style for "' + self.name + '"!')
        print('Style =', style)
        print('Music =', music)
        print('Voice =', voice)
        print('Skybox =', skybox)
        print('Elevator = ', elevator)
        print('Style Vars:\n  {')
        for key, val in style_vars.items():
            print('  {} = {!s}'.format(key, val))
        print('  }')
        print(len(pack_list), 'Pack Lists!')
        print(len(editor_sounds), 'Editor Sounds!')
        print('-' * 20)

        # VBSP, VRAD, editoritems
        export_screen.set_length('BACK', len(FILES_TO_BACKUP))
        export_screen.set_length(
            'CONF',
            # VBSP_conf, Editoritems, instances, gameinfo, pack_lists,
            # editor_sounds
            6 +
            # Don't add the voicelines to the progress bar if not selected
            (0 if voice is None else len(VOICE_PATHS)),
        )
        # files in compiler/
        export_screen.set_length('COMP', len(os.listdir('../compiler')))

        if should_refresh:
            export_screen.set_length('RES', extract_packages.res_count)
        else:
            export_screen.skip_stage('RES')

        export_screen.show()
        export_screen.grab_set_global()  # Stop interaction with other windows

        vbsp_config = style.config.copy()

        # Editoritems.txt is composed of a "ItemData" block, holding "Item" and
        # "Renderables" sections.
        editoritems = Property("ItemData", list(style.editor.find_all('Item')))

        for item in sorted(all_items):
            item_block, editor_parts, config_part = all_items[item].export()
            editoritems += item_block
            editoritems += editor_parts
            vbsp_config += config_part

        if voice is not None:
            vbsp_config += voice.config

        if skybox is not None:
            vbsp_config.set_key(
                ('Textures', 'Special', 'Sky'),
                skybox.material,
            )
            vbsp_config += skybox.config

        if style.has_video:
            if elevator is None:
                # Use a randomised video
                vbsp_config.set_key(
                    ('Elevator', 'type'),
                    'RAND',
                )
            elif elevator.id == 'VALVE_BLUESCREEN':
                # This video gets a special script and handling
                vbsp_config.set_key(
                    ('Elevator', 'type'),
                    'BSOD',
                )
            else:
                # Use the particular selected video
                vbsp_config.set_key(
                    ('Elevator', 'type'),
                    'FORCE',
                )
                vbsp_config.set_key(
                    ('Elevator', 'horiz'),
                    elevator.horiz_video,
                )
                vbsp_config.set_key(
                    ('Elevator', 'vert'),
                    elevator.vert_video,
                )
        else:  # No elevator video for this style
            vbsp_config.set_key(
                ('Elevator', 'type'),
                'NONE',
            )

        if music is not None:
            if music.sound is not None:
                vbsp_config.set_key(
                    ('Options', 'music_SoundScript'),
                    music.sound,
                )
            if music.inst is not None:
                vbsp_config.set_key(
                    ('Options', 'music_instance'),
                    music.inst,
                )

            vbsp_config.set_key(('Options', 'music_ID'), music.id)
            vbsp_config += music.config

        if voice is not None:
            vbsp_config.set_key(
                ('Options', 'voice_pack'),
                voice.id,
            )
            vbsp_config.set_key(('Options', 'voice_char'),
                                ','.join(voice.chars))

        vbsp_config.set_key(
            ('Options', 'BEE2_loc'),
            os.path.dirname(
                os.getcwd())  # Go up one dir to our actual location
        )

        vbsp_config.ensure_exists('StyleVars')
        vbsp_config['StyleVars'] += [
            Property(key, utils.bool_as_int(val))
            for key, val in style_vars.items()
        ]

        pack_block = Property('PackList', [])
        # A list of materials which will casue a specific packlist to be used.
        pack_triggers = Property('PackTriggers', [])

        for key, pack in pack_list.items():
            pack_block.append(
                Property(key, [Property('File', file) for file in pack.files]))
            for trigger_mat in pack.trigger_mats:
                pack_triggers.append(
                    Property('Material', [
                        Property('Texture', trigger_mat),
                        Property('PackList', pack.id),
                    ]))
        if pack_triggers.value:
            vbsp_config.append(pack_triggers)

        # If there are multiple of these blocks, merge them together
        # They will end up in this order.
        vbsp_config.merge_children(
            'Textures',
            'Fizzler',
            'Options',
            'StyleVars',
            'Conditions',
            'Voice',
            'PackTriggers',
        )

        for name, file, ext in FILES_TO_BACKUP:
            item_path = self.abs_path(file + ext)
            backup_path = self.abs_path(file + '_original' + ext)
            if os.path.isfile(item_path) and not os.path.isfile(backup_path):
                print('Backing up original ' + name + '!')
                shutil.copy(item_path, backup_path)
            export_screen.step('BACK')

        # This is the connections "heart" icon and "error" icon
        editoritems += style.editor.find_key("Renderables", [])

        # Build a property tree listing all of the instances for each item
        all_instances = Property("AllInstances", [])
        for item in editoritems.find_all("Item"):
            item_prop = Property(item['Type'], [])
            all_instances.append(item_prop)
            for inst_block in item.find_all("Exporting", "instances"):
                for inst in inst_block:
                    item_prop.append(Property('Instance', inst['Name']))

        if style_vars.get('UnlockDefault', False):
            print('Unlocking Items!')
            for item in editoritems.find_all('Item'):
                # If the Unlock Default Items stylevar is enabled, we
                # want to force the corridors and obs room to be
                # deletable and copyable
                # Also add DESIRES_UP, so they place in the correct orientation
                if item['type', ''] in _UNLOCK_ITEMS:
                    editor_section = item.find_key("Editor", [])
                    editor_section['deletable'] = '1'
                    editor_section['copyable'] = '1'
                    editor_section['DesiredFacing'] = 'DESIRES_UP'

        print('Editing Gameinfo!')
        self.edit_gameinfo(True)

        export_screen.step('CONF')

        print('Writing Editoritems!')
        os.makedirs(self.abs_path('portal2_dlc2/scripts/'), exist_ok=True)
        with open(self.abs_path('portal2_dlc2/scripts/editoritems.txt'),
                  'w') as editor_file:
            for line in editoritems.export():
                editor_file.write(line)
        export_screen.step('CONF')

        print('Writing VBSP Config!')
        os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True)
        with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w') as vbsp_file:
            for line in vbsp_config.export():
                vbsp_file.write(line)
        export_screen.step('CONF')

        print('Writing instance list!')
        with open(self.abs_path('bin/bee2/instances.cfg'), 'w') as inst_file:
            for line in all_instances.export():
                inst_file.write(line)
        export_screen.step('CONF')

        print('Writing packing list!')
        with open(self.abs_path('bin/bee2/pack_list.cfg'), 'w') as pack_file:
            for line in pack_block.export():
                pack_file.write(line)
        export_screen.step('CONF')

        print('Editing game_sounds!')
        self.add_editor_sounds(editor_sounds.values())
        export_screen.step('CONF')

        if voice is not None:
            for prefix, dest, pretty in VOICE_PATHS:
                path = os.path.join(
                    os.getcwd(),
                    '..',
                    'config',
                    'voice',
                    prefix + voice.id + '.cfg',
                )
                print(path)
                if os.path.isfile(path):
                    shutil.copy(
                        path,
                        self.abs_path('bin/bee2/{}voice.cfg'.format(dest)))
                    print('Written "{}voice.cfg"'.format(dest))
                else:
                    print('No ' + pretty + ' voice config!')
                export_screen.step('CONF')

        print('Copying Custom Compiler!')
        for file in os.listdir('../compiler'):
            print('\t* compiler/{0} -> bin/{0}'.format(file))
            shutil.copy(os.path.join('../compiler', file),
                        self.abs_path('bin/'))
            export_screen.step('COMP')

        if should_refresh:
            print('Copying Resources!')
            self.refresh_cache()

        export_screen.grab_release()
        export_screen.reset()  # Hide loading screen, we're done
예제 #43
0
from datetime import datetime
from zipfile import ZipFile
from io import BytesIO

import os
import os.path
import stat
import shutil
import sys
import subprocess

from property_parser import Property
from BSP import BSP, BSP_LUMPS
import utils

CONF = Property('Config')
SCREENSHOT_DIR = os.path.join(
    '..',
    'portal2',  # This is hardcoded into P2, it won't change for mods.
    'puzzles',
    # Then the <random numbers> folder
)
# Locations of resources we need to pack
RES_ROOT = [
    os.path.join('..', loc) for loc in ('bee2', 'bee2_dev', 'portal2_dlc2')
]


def quote(txt):
    return '"' + txt + '"'
예제 #44
0
class Style:
    def __init__(
        self,
        style_id,
        selitem_data: 'SelitemData',
        editor,
        config=None,
        base_style=None,
        suggested=None,
        has_video=True,
        corridor_names=utils.EmptyMapping,
    ):
        self.id = style_id
        self.selitem_data = selitem_data
        self.editor = editor
        self.base_style = base_style
        self.bases = []  # Set by setup_style_tree()
        self.suggested = suggested or {}
        self.has_video = has_video
        self.corridor_names = {
            'sp_entry': corridor_names.get('sp_entry', Property('', [])),
            'sp_exit': corridor_names.get('sp_exit', Property('', [])),
            'coop': corridor_names.get('coop', Property('', [])),
        }
        if config is None:
            self.config = Property(None, [])
        else:
            self.config = config

    @classmethod
    def parse(cls, data):
        """Parse a style definition."""
        info = data.info
        selitem_data = get_selitem_data(info)
        base = info['base', '']
        has_video = utils.conv_bool(info['has_video', '1'])

        sugg = info.find_key('suggested', [])
        sugg = (
            sugg['quote', '<NONE>'],
            sugg['music', '<NONE>'],
            sugg['skybox', 'SKY_BLACK'],
            sugg['goo', 'GOO_NORM'],
            sugg['elev', '<NONE>'],
        )

        corridors = info.find_key('corridors', [])
        corridors = {
            'sp_entry': corridors.find_key('sp_entry', []),
            'sp_exit': corridors.find_key('sp_exit', []),
            'coop': corridors.find_key('coop', []),
        }

        if base == '':
            base = None
        folder = 'styles/' + info['folder']
        config = folder + '/vbsp_config.cfg'
        with data.zip_file.open(folder + '/items.txt', 'r') as item_data:
            items = Property.parse(item_data,
                                   data.pak_id + ':' + folder + '/items.txt')

        try:
            with data.zip_file.open(config, 'r') as vbsp_config:
                vbsp = Property.parse(
                    vbsp_config,
                    data.pak_id + ':' + config,
                )
        except KeyError:
            vbsp = None
        return cls(
            style_id=data.id,
            selitem_data=selitem_data,
            editor=items,
            config=vbsp,
            base_style=base,
            suggested=sugg,
            has_video=has_video,
            corridor_names=corridors,
        )

    def add_over(self, override: 'Style'):
        """Add the additional commands to ourselves."""
        self.editor.extend(override.editor)
        self.config.extend(override.config)
        self.selitem_data.auth.extend(override.selitem_data.auth)

    def __repr__(self):
        return '<Style:' + self.id + '>'