srctools.packlist

The packlist module collects a list of resources, then analyses them further to discover their dependencies. As the name implies this was originally intended for packing into BSPs, but it can be used for a variety of purposes.

PackList

class srctools.packlist.PackFile(
type: FileType,
filename: str,
data: bytes | None = None,
optional: bool = False,
source: str = '',
)

Represents a single file we are packing.

data is raw data to pack directly, instead of from the filesystem.

type: FileType
filename: str
data: bytes | None
optional: bool
source: str
property virtual: bool

Virtual files do not exist on the file system.

class srctools.packlist.PackList(fsys: FileSystemChain)

Represents a list of resources for a map.

fsys: FileSystemChain
soundscript: ManifestedFiles[Sound]
soundscapes: ManifestedFiles[Soundscape]
particles: ManifestedFiles[Particle]
choreo: dict[CRC, Entry]
skinsets: dict[str, set[int] | None]
filenames() Iterator[str]

The filenames of all packed files.

pack_file(
filename: str | PathLike[str],
data_type: FileType = FileType.GENERIC,
data: bytes | None = None,
*,
skinset: set[int] | None = None,
optional: bool = False,
source: str = '',
) None

Queue the given file to be packed.

Parameters:
  • filename – The name of the file to pack. This could be symbolic if certain file types are specified.

  • data_type – Indicates definiteively what type of file this is. If generic, this is inferred from the extension.

  • data – If provided, this file will use the given data instead of any on-disk data.

  • skinset – If the file is a model, skinset allows restricting which skins are used. If None (default), all skins may be used. Otherwise, it is a set of skins. If all uses of a model restrict the skin, only those skins need to be packed.

  • optional – If set, no errors will occur if it isn’t in the filesystem.

  • source – Human-readable string indicating the reason for packing.

inject_file(
data: bytes,
folder: str,
ext: str,
*,
prefix: str = 'INJECT',
source: str = 'inject',
) str

Inject a generated file into the map and return the full name.

The file will be named using the format folder/prefix_hex.ext. If the same file is requested twice (same folder, extension and data), only one will be included.

inject_vscript(code: str, folder: str = 'inject', prefix: str = 'INJECT') str

Specialised variant of inject_file() for VScript code specifically.

This returns the script name suitable for passing to Entity Scripts.

pack_soundscript(sound_name: str, *, source: str = '') None

Pack a soundscript or raw sound file.

pack_soundscape(soundscape_name: str, *, source: str = '') None

Pack a soundscape, and all the sounds it needs.

pack_particle(
particle_name: str,
preload: bool = False,
*,
source: str = '',
) None

Pack a particle system and the raw PCFs.

pack_breakable_chunk(chunkname: str) None

Pack the generic gib model for the given chunk name.

load_soundscript(
file: File,
*,
always_include: bool = False,
) Iterable[Sound]

Read in a soundscript and record which files use it.

If always_include is True, it will be included in the manifests even if it isn’t used.

The sounds registered by this soundscript are returned.

load_particle_system(
filename: str,
mode: FileMode = FileMode.UNKNOWN,
) Iterable[Particle]

Read in the specified particle system and record the particles for usage checking.

load_manifests(cache_folder: Path | str | None = None) None

Parse the manifests and script files for things like soundscripts or particles.

This is necessary to perform lookups by name. If the cache prefix is provided, this is used as a path and prefix for files writted to cache results to speed up later executions.

load_soundscript_manifest(cache_file: Path | str | None = None) None

Read the soundscript manifest, and read all mentioned scripts.

If cache_file is provided, it should be a path to a file used to cache the file reading for later use.

load_soundscape_manifest(cache_file: Path | str | None = None) None

Read the soundscape manifest, and read all mentioned scripts.

If cache_file is provided, it should be a path to a file used to cache the file reading for later use.

load_particle_manifest(cache_file: Path | str | None = None) None

Read the particle manifest, and read all mentioned scripts.

If cache_file is provided, it should be a path to a file used to cache the file reading for later use.

load_choreo_scenes() None

Load the scenes manifest.

write_manifest() None

Deprecated, call write_soundscript_manifest().

write_soundscript_manifest() None

Produce and pack a soundscript manifest file for this map.

It will be packed such that it can override the master manifest with sv_soundemitter_flush.

write_particles_manifest(manifest_name: str) None

Write a particles manifest, so that used particles can be loaded.

pack_from_bsp(bsp: BSP) None

Pack files found in BSP data (excluding entities).

pack_fgd(
vmf: VMF,
fgd: FGD,
mapname: str = '',
tags: Iterable[str] = (),
) None

Deprecated version of pack_from_ents(). The FGD parameter is no longer necessary.

pack_from_ents(
vmf: VMF,
mapname: str = '',
tags: Iterable[str] = (),
) None

Analyse the map to pack files, using an internal database of keyvalues.

‘detailmaterial’ is handled in pack_from_bsp(), we only need to include it if a detail sprite is actually present in the BSP.

pack_into_zip(bsp: ~srctools.bsp.BSP, *, whitelist: ~collections.abc.Iterable[~srctools.filesys.FileSystem] = (), blacklist: ~collections.abc.Iterable[~srctools.filesys.FileSystem] = (), callback: ~collections.abc.Callable[[str], bool | None] = <function PackList.<lambda>>, dump_loc: ~pathlib.Path | None = None, only_dump: bool = False, ignore_vpk: bool = True) None

Pack all our files into the packfile in the BSP.

The filesystem is used to find files to pack. First it is passed to the callback (if provided), which should return True/False to determine if the file should be packed. If it returns None, then the whitelist/blacklist is checked. Filesystems must be in the whitelist and not in the blacklist, if provided. If ignore_vpk is True, files in VPK won’t be packed unless that system is in allow_filesys. If dump_loc is set, files will be copied there as well. If only_dump is set, they won’t be packed at all.

eval_dependencies() None

Add files to the list which need to also be packed.

This requires parsing through many files.

“Manifest” Files

class srctools.packlist.FileMode

Bases: Enum

Mode for files we may want to pack like soundscripts or particles.

UNKNOWN = 'unknown'
INCLUDE = 'include'
PRELOAD = 'preload'
EXCLUDE = 'exclude'
property is_used: bool

Return if this file should be packed into the map.

class srctools.packlist.ManifestedFiles(
name: str,
pack_type: FileType,
parse_func: Callable[[File], dict[str, ParsedT]],
name_to_parsed: dict[str, tuple[str, ParsedT | None]] = ...,
packed_ids: set[str] = ...,
files: dict[str, FileMode] = ...,
unparsed_file: dict[str, File] = ...,
cache: dict[str, tuple[int, list[str]]] = ...,
cache_changed: bool = True,
)

Handles a file type which contains a bunch of named objects.

We parse those to load the names, then when the names are referenced we pack the files they’re defined in.

name: str
pack_type: FileType
parse_func: Callable[[File], dict[str, ParsedT]]
name_to_parsed: dict[str, tuple[str, ParsedT | None]]
packed_ids: set[str]
force_exclude(filename: str) None

Mark this soundscript file as excluded.

load_cache(filename: str | PathLike[str]) None

Load the cache data. If the file is invalid, this does nothing.

save_cache(filename: str | PathLike[str], force: bool = False) None

Write back new cache data.

add_cached_file(
filename: str,
file: File,
mode: FileMode = FileMode.UNKNOWN,
) None

Load a file which may have been cached.

If the file is new we parse immediately, otherwise defer until actually required.

add_file(
filename: str,
items: Iterable[tuple[str, ParsedT]],
mode: FileMode = FileMode.UNKNOWN,
) None

Add a file with its parsed items.

fetch_data(identifier: str) tuple[str, ParsedT]

Fetch the parsed form of this data and the file it’s in, without packing.

pack_and_get(
lst: PackList,
identifier: str,
preload: bool = False,
source: str = '',
) ParsedT

Pack the associated filename, then return the data.

for ... in packed_files() Iterator[tuple[str, FileMode]]

Yield the used files in order.

Functions

srctools.packlist.unify_path(path: str) str

Convert paths to a unique form.

srctools.packlist.strip_extension(filename: str) str

Strip extensions from a filename, like Q_StripExtension().