Exemple #1
0
def write_sound(
    file: StringIO,
    snds: Property,
    pack_list: PackList,
    snd_prefix: str = '*',
) -> None:
    """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\t{\n')
        for snd in snds:
            file.write('\t"wave" "{sndchar}{file}"\n'.format(
                file=snd.value.lstrip(SOUND_CHARS),
                sndchar=snd_prefix,
            ))
            pack_list.pack_file('sound/' + snd.value.casefold())
        file.write('\t}\n')
    else:
        file.write('"wave" "{sndchar}{file}"\n'.format(
            file=snds.value.lstrip(SOUND_CHARS),
            sndchar=snd_prefix,
        ))
        pack_list.pack_file('sound/' + snds.value.casefold())
Exemple #2
0
def write_sound(
    file: StringIO,
    snds: Property,
    pack_list: PackList,
    snd_prefix: str='*',
) -> None:
    """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\t{\n')
        for snd in snds:
            file.write(
                '\t"wave" "{sndchar}{file}"\n'.format(
                    file=snd.value.lstrip(SOUND_CHARS),
                    sndchar=snd_prefix,
                )
            )
            pack_list.pack_file('sound/' + snd.value.casefold())
        file.write('\t}\n')
    else:
        file.write(
            '"wave" "{sndchar}{file}"\n'.format(
                file=snds.value.lstrip(SOUND_CHARS),
                sndchar=snd_prefix,
            )
        )
        pack_list.pack_file('sound/' + snds.value.casefold())
Exemple #3
0
def write_sound(
    file: StringIO,
    snds: List[str],
    pack_list: PackList,
    snd_prefix: str = '*',
) -> None:
    """Write either a single sound, or multiple rndsound.

    snd_prefix is the prefix for each filename - *, #, @, etc.
    """
    if len(snds) > 1:
        file.write('"rndwave"\n\t{\n')
        for snd in snds:
            file.write('\t"wave" "{sndchar}{file}"\n'.format(
                file=snd.lstrip(SND_CHARS),
                sndchar=snd_prefix,
            ))
            pack_list.pack_file('sound/' + snd.casefold())
        file.write('\t}\n')
    else:
        file.write('"wave" "{sndchar}{file}"\n'.format(
            file=snds[0].lstrip(SND_CHARS),
            sndchar=snd_prefix,
        ))
        pack_list.pack_file('sound/' + snds[0].casefold())
Exemple #4
0
def team_control_point(pack: PackList, ent: Entity) -> None:
    """Special '_locked' materials."""
    for kvalue in ['team_icon_0', 'team_icon_1', 'team_icon_2']:
        mat = ent[kvalue]
        if mat:
            pack.pack_file('materials/{}.vmt'.format(mat), FileType.MATERIAL)
            pack.pack_file('materials/{}_locked.vmt'.format(mat),
                           FileType.MATERIAL)
Exemple #5
0
def pack_ent_class(pack: PackList, clsname: str) -> None:
    """Call to pack another entity class."""
    reslist = CLASS_RESOURCES[clsname]
    if callable(reslist):
        raise ValueError(
            "Can't pack \"{}\", has a custom function!".format(clsname))
    for fname, ftype in reslist:
        pack.pack_file(fname, ftype)
Exemple #6
0
def skybox_swapper(pack: PackList, ent: Entity) -> None:
    """This needs to pack a skybox."""
    sky_name = ent['skyboxname']
    for suffix in ['bk', 'dn', 'ft', 'lf', 'rt', 'up']:
        pack.pack_file(
            'materials/skybox/{}{}.vmt'.format(sky_name, suffix),
            FileType.MATERIAL,
        )
        pack.pack_file(
            'materials/skybox/{}{}_hdr.vmt'.format(sky_name, suffix),
            FileType.MATERIAL,
            optional=True,
        )
Exemple #7
0
def move_rope(pack: PackList, ent: Entity) -> None:
    """Implement move_rope and keyframe_rope resources."""
    old_shader_type = conv_int(ent['RopeShader'])
    if old_shader_type == 0:
        pack.pack_file('materials/cable/cable.vmt', FileType.MATERIAL)
    elif old_shader_type == 1:
        pack.pack_file('materials/cable/rope.vmt', FileType.MATERIAL)
    else:
        pack.pack_file('materials/cable/chain.vmt', FileType.MATERIAL)
    pack.pack_file('materials/cable/rope_shadowdepth.vmt', FileType.MATERIAL)
Exemple #8
0
def main(argv: List[str]) -> None:
    LOGGER.info('BEE2 VRAD hook started!')

    args = " ".join(argv)
    fast_args = argv[1:]
    full_args = argv[1:]

    if not fast_args:
        # No arguments!
        LOGGER.info(
            'No arguments!\n'
            "The BEE2 VRAD takes all the regular VRAD's "
            'arguments, with some extra arguments:\n'
            '-force_peti: Force enabling map conversion. \n'
            "-force_hammer: Don't convert the map at all.\n"
            "If not specified, the map name must be \"preview.bsp\" to be "
            "treated as PeTI.")
        sys.exit()

    # The path is the last argument to vrad
    # P2 adds wrong slashes sometimes, so fix that.
    fast_args[-1] = path = os.path.normpath(argv[-1])  # type: str

    LOGGER.info("Map path is " + path)

    load_config()

    for a in fast_args[:]:
        if a.casefold() in (
                "-both",
                "-final",
                "-staticproplighting",
                "-staticproppolys",
                "-textureshadows",
        ):
            # remove final parameters from the modified arguments
            fast_args.remove(a)
        elif a in ('-force_peti', '-force_hammer', '-no_pack'):
            # we need to strip these out, otherwise VRAD will get confused
            fast_args.remove(a)
            full_args.remove(a)

    fast_args = ['-bounce', '2', '-noextra'] + fast_args

    # Fast args: -bounce 2 -noextra -game $gamedir $path\$file
    # Final args: -both -final -staticproplighting -StaticPropPolys
    # -textureshadows  -game $gamedir $path\$file

    if not path.endswith(".bsp"):
        path += ".bsp"

    if not os.path.exists(path):
        raise ValueError('"{}" does not exist!'.format(path))
    if not os.path.isfile(path):
        raise ValueError('"{}" is not a file!'.format(path))

    # If VBSP thinks it's hammer, trust it.
    if CONF.bool('is_hammer', False):
        is_peti = edit_args = False
    else:
        is_peti = True
        # Detect preview via knowing the bsp name. If we are in preview,
        # check the config file to see what was specified there.
        if os.path.basename(path) == "preview.bsp":
            edit_args = not CONF.bool('force_full', False)
        else:
            # publishing - always force full lighting.
            edit_args = False

    if '-force_peti' in args or '-force_hammer' in args:
        # we have override commands!
        if '-force_peti' in args:
            LOGGER.warning('OVERRIDE: Applying cheap lighting!')
            is_peti = edit_args = True
        else:
            LOGGER.warning('OVERRIDE: Preserving args!')
            is_peti = edit_args = False

    LOGGER.info('Final status: is_peti={}, edit_args={}', is_peti, edit_args)

    # Grab the currently mounted filesystems in P2.
    game = find_gameinfo(argv)
    root_folder = game.path.parent
    fsys = game.get_filesystem()

    fsys_tag = fsys_mel = None
    if is_peti and 'mel_vpk' in CONF:
        fsys_mel = VPKFileSystem(CONF['mel_vpk'])
        fsys.add_sys(fsys_mel)
    if is_peti and 'tag_dir' in CONF:
        fsys_tag = RawFileSystem(CONF['tag_dir'])
        fsys.add_sys(fsys_tag)

    LOGGER.info('Reading BSP')
    bsp_file = BSP(path)

    bsp_ents = bsp_file.read_ent_data()

    zip_data = BytesIO()
    zip_data.write(bsp_file.get_lump(BSP_LUMPS.PAKFILE))
    zipfile = ZipFile(zip_data, mode='a')

    # Mount the existing packfile, so the cubemap files are recognised.
    fsys.systems.append((ZipFileSystem('', zipfile), ''))

    fsys.open_ref()

    LOGGER.info('Done!')

    LOGGER.info('Reading our FGD files...')
    fgd = load_fgd()

    packlist = PackList(fsys)
    packlist.load_soundscript_manifest(
        str(root_folder / 'bin/bee2/sndscript_cache.vdf'))

    # We nee to add all soundscripts in scripts/bee2_snd/
    # This way we can pack those, if required.
    for soundscript in fsys.walk_folder('scripts/bee2_snd/'):
        if soundscript.path.endswith('.txt'):
            packlist.load_soundscript(soundscript, always_include=False)

    if is_peti:
        LOGGER.info('Adding special packed files:')
        music_data = CONF.find_key('MusicScript', [])
        if music_data:
            packlist.pack_file('scripts/BEE2_generated_music.txt',
                               PackType.SOUNDSCRIPT,
                               data=generate_music_script(
                                   music_data, packlist))

        for filename, arcname in inject_files():
            LOGGER.info('Injecting "{}" into packfile.', arcname)
            with open(filename, 'rb') as f:
                packlist.pack_file(arcname, data=f.read())

    LOGGER.info('Run transformations...')
    run_transformations(bsp_ents, fsys, packlist)

    LOGGER.info('Scanning map for files to pack:')
    packlist.pack_from_bsp(bsp_file)
    packlist.pack_fgd(bsp_ents, fgd)
    packlist.eval_dependencies()
    LOGGER.info('Done!')

    if is_peti:
        packlist.write_manifest()
    else:
        # Write with the map name, so it loads directly.
        packlist.write_manifest(os.path.basename(path)[:-4])

    # We need to disallow Valve folders.
    pack_whitelist = set()  # type: Set[FileSystem]
    pack_blacklist = set()  # type: Set[FileSystem]
    if is_peti:
        pack_blacklist |= {
            RawFileSystem(root_folder / 'portal2_dlc2'),
            RawFileSystem(root_folder / 'portal2_dlc1'),
            RawFileSystem(root_folder / 'portal2'),
            RawFileSystem(root_folder / 'platform'),
            RawFileSystem(root_folder / 'update'),
        }
        if fsys_mel is not None:
            pack_whitelist.add(fsys_mel)
        if fsys_tag is not None:
            pack_whitelist.add(fsys_tag)

    if '-no_pack' not in args:
        # Cubemap files packed into the map already.
        existing = set(zipfile.infolist())

        LOGGER.info('Writing to BSP...')
        packlist.pack_into_zip(
            zipfile,
            ignore_vpk=True,
            whitelist=pack_whitelist,
            blacklist=pack_blacklist,
        )

        LOGGER.info(
            'Packed files:\n{}', '\n'.join([
                zipinfo.filename for zipinfo in zipfile.infolist()
                if zipinfo.filename not in existing
            ]))

    dump_files(zipfile)

    zipfile.close()  # Finalise the zip modification

    # Copy the zipfile into the BSP file, and adjust the headers.
    bsp_file.lumps[BSP_LUMPS.PAKFILE].data = zip_data.getvalue()
    # Copy new entity data.
    bsp_file.lumps[BSP_LUMPS.ENTITIES].data = BSP.write_ent_data(bsp_ents)

    bsp_file.save()
    LOGGER.info(' - BSP written!')

    if is_peti:
        mod_screenshots()

    if edit_args:
        LOGGER.info("Forcing Cheap Lighting!")
        run_vrad(fast_args)
    else:
        if is_peti:
            LOGGER.info(
                "Publishing - Full lighting enabled! (or forced to do so)")
        else:
            LOGGER.info("Hammer map detected! Not forcing cheap lighting..")
        run_vrad(full_args)

    LOGGER.info("BEE2 VRAD hook finished!")
Exemple #9
0
def generate(bsp: VMF, pack_list: PackList):
    """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.
    tracks: Dict[Channel, List[str]] = {}
    sync_funnel = False

    for conf_ent in bsp.by_class['bee2_music_channel']:
        conf_ent.remove()
        channel = Channel(conf_ent['channel'].casefold())
        if channel is Channel.TBEAM:
            sync_funnel = srctools.conv_bool(conf_ent['sync'])

        track = []
        for i in itertools.count(1):
            snd = conf_ent[f'track{i:02}']
            if not snd:
                break
            track.append(snd)
        if track:
            tracks[channel] = track

    if not tracks:
        return None  # No music.

    if Channel.BASE not in tracks:
        tracks[Channel.BASE] = ['bee2/silent_lp.wav']
        # Don't sync to a 2-second sound.
        sync_funnel = False

    file = StringIO()

    # Write the base music track
    file.write(MUSIC_START.format(name='', vol='1'))
    write_sound(file, tracks[Channel.BASE], pack_list, snd_prefix='#*')
    file.write(MUSIC_BASE)
    # The 'soundoperators' section is still open now.

    # Add the operators to play the auxiliary sounds..
    if Channel.TBEAM in tracks:
        file.write(MUSIC_FUNNEL_MAIN)
    if Channel.BOUNCE in tracks:
        file.write(MUSIC_GEL_BOUNCE_MAIN)
    if Channel.SPEED in tracks:
        file.write(MUSIC_GEL_SPEED_MAIN)

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

    if Channel.TBEAM in tracks:
        # Write the 'music.BEE2_funnel' sound entry
        file.write('\n')
        file.write(MUSIC_START.format(name='_funnel', vol='1'))
        write_sound(file, tracks[Channel.TBEAM], pack_list)
        # Some tracks want the funnel music to sync with the normal
        # track, others randomly choose a start.
        file.write(MUSIC_FUNNEL_SYNC_STACK
                   if sync_funnel else MUSIC_FUNNEL_RAND_STACK)
        file.write(MUSIC_FUNNEL_UPDATE_STACK)

    if Channel.BOUNCE in tracks:
        file.write('\n')
        file.write(MUSIC_START.format(name='_gel_bounce', vol='0.5'))
        write_sound(file, tracks[Channel.BOUNCE], pack_list)
        # Fade in fast (we never get false positives, but fade out slow
        # since this disables when falling back..
        file.write(MUSIC_GEL_STACK.format(fadein=0.25, fadeout=1.5))

    if Channel.SPEED in tracks:
        file.write('\n')
        file.write(MUSIC_START.format(name='_gel_speed', vol='0.5'))
        write_sound(file, tracks[Channel.SPEED], pack_list)
        # We need to shut off the sound fast, so portals don't confuse it.
        # Fade in slow so it doesn't make much sound (and also as we get
        # up to speed). We stop almost immediately on gel too.
        file.write(MUSIC_GEL_STACK.format(fadein=0.5, fadeout=0.1))

    pack_list.pack_file('scripts/BEE2_generated_music.txt',
                        FileType.SOUNDSCRIPT,
                        data=file.getvalue().encode())
Exemple #10
0
def vgui_movie_display(pack: PackList, ent: Entity):
    """Mark the BIK movie as being used, though it can't be packed."""
    pack.pack_file(ent['MovieFilename'])
Exemple #11
0
def color_correction_volume(pack: PackList, ent: Entity) -> None:
    """Pack the color correction file for this too."""
    pack.pack_file(ent['filename'])
Exemple #12
0
def color_correction(pack: PackList, ent: Entity) -> None:
    """Pack the color correction file."""
    pack.pack_file(ent['filename'])
Exemple #13
0
def main(argv: List[str]) -> None:
    LOGGER.info('BEE2 VRAD hook started!')
        
    args = " ".join(argv)
    fast_args = argv[1:]
    full_args = argv[1:]
    
    if not fast_args:
        # No arguments!
        LOGGER.info(
            'No arguments!\n'
            "The BEE2 VRAD takes all the regular VRAD's "
            'arguments, with some extra arguments:\n'
            '-force_peti: Force enabling map conversion. \n'
            "-force_hammer: Don't convert the map at all.\n"
            "If not specified, the map name must be \"preview.bsp\" to be "
            "treated as PeTI."
        )
        sys.exit()

    # The path is the last argument to vrad
    # P2 adds wrong slashes sometimes, so fix that.
    fast_args[-1] = path = os.path.normpath(argv[-1])  # type: str

    LOGGER.info("Map path is " + path)

    load_config()

    for a in fast_args[:]:
        if a.casefold() in (
                "-both",
                "-final",
                "-staticproplighting",
                "-staticproppolys",
                "-textureshadows",
                ):
            # remove final parameters from the modified arguments
            fast_args.remove(a)
        elif a in ('-force_peti', '-force_hammer', '-no_pack'):
            # we need to strip these out, otherwise VRAD will get confused
            fast_args.remove(a)
            full_args.remove(a)

    fast_args = ['-bounce', '2', '-noextra'] + fast_args

    # Fast args: -bounce 2 -noextra -game $gamedir $path\$file
    # Final args: -both -final -staticproplighting -StaticPropPolys
    # -textureshadows  -game $gamedir $path\$file

    if not path.endswith(".bsp"):
        path += ".bsp"

    if not os.path.exists(path):
        raise ValueError('"{}" does not exist!'.format(path))
    if not os.path.isfile(path):
        raise ValueError('"{}" is not a file!'.format(path))

    # If VBSP thinks it's hammer, trust it.
    if CONF.bool('is_hammer', False):
        is_peti = edit_args = False
    else:
        is_peti = True
        # Detect preview via knowing the bsp name. If we are in preview,
        # check the config file to see what was specified there.
        if os.path.basename(path) == "preview.bsp":
            edit_args = not CONF.bool('force_full', False)
        else:
            # publishing - always force full lighting.
            edit_args = False

    if '-force_peti' in args or '-force_hammer' in args:
        # we have override commands!
        if '-force_peti' in args:
            LOGGER.warning('OVERRIDE: Applying cheap lighting!')
            is_peti = edit_args = True
        else:
            LOGGER.warning('OVERRIDE: Preserving args!')
            is_peti = edit_args = False

    LOGGER.info('Final status: is_peti={}, edit_args={}', is_peti, edit_args)

    # Grab the currently mounted filesystems in P2.
    game = find_gameinfo(argv)
    root_folder = game.path.parent
    fsys = game.get_filesystem()

    fsys_tag = fsys_mel = None
    if is_peti and 'mel_vpk' in CONF:
        fsys_mel = VPKFileSystem(CONF['mel_vpk'])
        fsys.add_sys(fsys_mel)
    if is_peti and 'tag_dir' in CONF:
        fsys_tag = RawFileSystem(CONF['tag_dir'])
        fsys.add_sys(fsys_tag)

    LOGGER.info('Reading BSP')
    bsp_file = BSP(path)

    bsp_ents = bsp_file.read_ent_data()

    zip_data = BytesIO()
    zip_data.write(bsp_file.get_lump(BSP_LUMPS.PAKFILE))
    zipfile = ZipFile(zip_data, mode='a')

    # Mount the existing packfile, so the cubemap files are recognised.
    fsys.systems.append((ZipFileSystem('', zipfile), ''))

    fsys.open_ref()

    LOGGER.info('Done!')

    LOGGER.info('Reading our FGD files...')
    fgd = load_fgd()

    packlist = PackList(fsys)
    packlist.load_soundscript_manifest(
        str(root_folder / 'bin/bee2/sndscript_cache.vdf')
    )

    # We nee to add all soundscripts in scripts/bee2_snd/
    # This way we can pack those, if required.
    for soundscript in fsys.walk_folder('scripts/bee2_snd/'):
        if soundscript.path.endswith('.txt'):
            packlist.load_soundscript(soundscript, always_include=False)

    if is_peti:
        LOGGER.info('Adding special packed files:')
        music_data = CONF.find_key('MusicScript', [])
        if music_data:
            packlist.pack_file(
                'scripts/BEE2_generated_music.txt',
                PackType.SOUNDSCRIPT,
                data=generate_music_script(music_data, packlist)
            )

        for filename, arcname in inject_files():
            LOGGER.info('Injecting "{}" into packfile.', arcname)
            with open(filename, 'rb') as f:
                packlist.pack_file(arcname, data=f.read())

    LOGGER.info('Run transformations...')
    run_transformations(bsp_ents, fsys, packlist)

    LOGGER.info('Scanning map for files to pack:')
    packlist.pack_from_bsp(bsp_file)
    packlist.pack_fgd(bsp_ents, fgd)
    packlist.eval_dependencies()
    LOGGER.info('Done!')

    if is_peti:
        packlist.write_manifest()
    else:
        # Write with the map name, so it loads directly.
        packlist.write_manifest(os.path.basename(path)[:-4])

    # We need to disallow Valve folders.
    pack_whitelist = set()  # type: Set[FileSystem]
    pack_blacklist = set()  # type: Set[FileSystem]
    if is_peti:
        pack_blacklist |= {
            RawFileSystem(root_folder / 'portal2_dlc2'),
            RawFileSystem(root_folder / 'portal2_dlc1'),
            RawFileSystem(root_folder / 'portal2'),
            RawFileSystem(root_folder / 'platform'),
            RawFileSystem(root_folder / 'update'),
        }
        if fsys_mel is not None:
            pack_whitelist.add(fsys_mel)
        if fsys_tag is not None:
            pack_whitelist.add(fsys_tag)

    if '-no_pack' not in args:
        # Cubemap files packed into the map already.
        existing = set(zipfile.infolist())

        LOGGER.info('Writing to BSP...')
        packlist.pack_into_zip(
            zipfile,
            ignore_vpk=True,
            whitelist=pack_whitelist,
            blacklist=pack_blacklist,
        )

        LOGGER.info('Packed files:\n{}', '\n'.join([
            zipinfo.filename
            for zipinfo in zipfile.infolist()
            if zipinfo.filename not in existing
        ]))

    dump_files(zipfile)

    zipfile.close()  # Finalise the zip modification

    # Copy the zipfile into the BSP file, and adjust the headers.
    bsp_file.lumps[BSP_LUMPS.PAKFILE].data = zip_data.getvalue()
    # Copy new entity data.
    bsp_file.lumps[BSP_LUMPS.ENTITIES].data = BSP.write_ent_data(bsp_ents)

    bsp_file.save()
    LOGGER.info(' - BSP written!')

    if is_peti:
        mod_screenshots()

    if edit_args:
        LOGGER.info("Forcing Cheap Lighting!")
        run_vrad(fast_args)
    else:
        if is_peti:
            LOGGER.info("Publishing - Full lighting enabled! (or forced to do so)")
        else:
            LOGGER.info("Hammer map detected! Not forcing cheap lighting..")
        run_vrad(full_args)

    LOGGER.info("BEE2 VRAD hook finished!")
Exemple #14
0
def parse(
    vmf: VMF, pack: PackList
) -> Tuple[int, Dict[Tuple[str, str, int], VacObject], Dict[str, str], ]:
    """Parse out the cube objects from the map.

    The return value is the number of objects, a dict of objects, and the
    filenames of the script generated for each group.
    The dict is (group, model, skin) -> object.
    """
    cube_objects: Dict[Tuple[str, str, int], VacObject] = {}
    vac_objects: Dict[str, List[VacObject]] = defaultdict(list)

    for i, ent in enumerate(vmf.by_class['comp_vactube_object']):
        offset = Vec.from_str(ent['origin']) - Vec.from_str(ent['offset'])
        obj = VacObject(
            f'obj_{i:x}',
            ent['group'],
            ent['model'],
            ent['cube_model'],
            offset,
            srctools.conv_int(ent['weight']),
            srctools.conv_int(ent['tv_skin']),
            srctools.conv_int(ent['cube_skin']),
            srctools.conv_int(ent['skin']),
        )
        vac_objects[obj.group].append(obj)
        # Convert the ent into a precache ent, stripping the other keyvalues.
        mdl_name = ent['model']
        ent.keys = {'model': mdl_name}
        make_precache_prop(ent)
        pack.pack_file(mdl_name, FileType.MODEL, skinset={obj.skin_vac})

        if obj.model_drop:
            cube_objects[obj.group,
                         obj.model_drop.replace('\\', '/'),
                         obj.skin_drop, ] = obj

    # Generate and pack the vactube object scripts.
    # Each group is the same, so it can be shared among them all.
    codes = {}
    for group, objects in sorted(vac_objects.items(), key=lambda t: t[0]):
        # First, see if there's a common multiple among the weights, allowing
        # us to simplify.
        multiple = objects[0].weight
        for obj in objects[1:]:
            multiple = math.gcd(multiple, obj.weight)
        if multiple > 1:
            LOGGER.info('Group "{}" has common factor of {}, simplifying.',
                        group, multiple)
        code = []
        for i, obj in enumerate(objects):
            obj.weight /= multiple
            if obj.model_drop:
                model_code = f'"{obj.model_drop}"'
            else:
                model_code = 'null'
            code.append(
                f'{obj.id} <- obj("{obj.model_vac}", {obj.skin_vac}, '
                f'{model_code}, {obj.weight}, "{obj.offset}", {obj.skin_tv});')
        codes[group] = pack.inject_vscript('\n'.join(code))

    return len(vac_objects), cube_objects, codes
Exemple #15
0
def main(args: List[str]) -> None:
    """Main script."""
    parser = argparse.ArgumentParser(description=__doc__)

    parser.add_argument(
        "-f",
        "--filter",
        help="filter output to only display resources in this subfolder. "
        "This can be used multiple times.",
        type=str.casefold,
        action='append',
        metavar='folder',
        dest='filters',
    )
    parser.add_argument(
        "-u",
        "--unused",
        help="Instead of showing depenencies, show files in the filtered "
        "folders that are unused.",
        action='store_true',
    )
    parser.add_argument(
        "game",
        help="either location of a gameinfo.txt file, or any root folder.",
    )
    parser.add_argument(
        "path",
        help="the files to load. The path can have a single * in the "
        "filename to match files with specific extensions and a prefix.",
    )

    result = parser.parse_args(args)

    if result.unused and not result.filters:
        raise ValueError(
            'At least one filter must be provided in "unused" mode.')

    if result.game:
        try:
            fsys = Game(result.game).get_filesystem()
        except FileNotFoundError:
            fsys = FileSystemChain(RawFileSystem(result.game))
    else:
        fsys = FileSystemChain()

    packlist = PackList(fsys)

    file_path: str = result.path
    print('Finding files...')
    with fsys:
        if '*' in file_path:  # Multiple files
            if file_path.count('*') > 1:
                raise ValueError('Multiple * in path!')
            prefix, suffix = file_path.split('*')
            folder, prefix = os.path.split(prefix)
            prefix = prefix.casefold()
            suffix = suffix.casefold()
            print(f'Prefix: {prefix!r}, suffix: {suffix!r}')
            print(f'Searching folder {folder}...')

            files = []
            for file in fsys.walk_folder(folder):
                file_path = file.path.casefold()
                if not os.path.basename(file_path).startswith(prefix):
                    continue
                if file_path.endswith(suffix):
                    print(' ' + file.path)
                    files.append(file)
        else:  # Single file
            files = [fsys[file_path]]
        for file in files:
            ext = file.path[-4:].casefold()
            if ext == '.vmf':
                with file.open_str() as f:
                    vmf_props = Property.parse(f)
                    vmf = VMF.parse(vmf_props)
                packlist.pack_fgd(vmf, fgd)
                del vmf, vmf_props  # Hefty, don't want to keep.
            elif ext == '.bsp':
                child_sys = fsys.get_system(file)
                if not isinstance(child_sys, RawFileSystem):
                    raise ValueError('Cannot inspect BSPs in VPKs!')
                bsp = BSP(os.path.join(child_sys.path, file.path))
                packlist.pack_from_bsp(bsp)
                packlist.pack_fgd(bsp.read_ent_data(), fgd)
                del bsp
            else:
                packlist.pack_file(file.path)
        print('Evaluating dependencies...')
        packlist.eval_dependencies()
        print('Done.')

        if result.unused:
            print('Unused files:')
            used = set(packlist.filenames())
            for folder in result.filters:
                for file in fsys.walk_folder(folder):
                    if file.path.casefold() not in used:
                        print(' ' + file.path)
        else:
            print('Dependencies:')
            for filename in packlist.filenames():
                if not result.filters or any(
                        map(filename.casefold().startswith, result.filters)):
                    print(' ' + filename)