Source code for scdatatools.cli.commands.p4k
import logging
import shutil
import sys
from pathlib import Path
from nubia import command, argument
from rich.progress import Progress
from . import common
logger = logging.getLogger(__name__)
[docs]@command(help="Utilities for interacting with p4k files.")
class p4k:
[docs] @command(help="Extract files from a P4K file", aliases=["x"])
@argument("single", description="Extract first matching file only", aliases=["-1"])
@argument(
"output",
description="The output directory to extract files into or the output path if --single. "
"Defaults to current directory",
aliases=["-o"],
)
@argument(
"file_filter",
description="Posix style file filter of which files to extract. Defaults to '*'",
aliases=["-f"],
)
@common.sc_dir_argument
@common.extraction_args(exclude=["output"])
def extract(
self,
sc_dir: str,
output: str = ".",
file_filter: str = "*",
convert_cryxml: str = "",
extract_model_assets: bool = False,
unsplit_textures: bool = False,
convert_textures: str = "",
convert_models: bool = False,
single: bool = False,
no_overwrite: bool = False,
):
output = Path(output).absolute()
sc = common.open_sc_dir(sc_dir)
file_filter = file_filter.strip("'").strip('"')
try:
p = sc.p4k
except KeyboardInterrupt:
sys.exit(0)
unsplit_textures = unsplit_textures or convert_textures != ""
converters = []
converter_options = dict()
if convert_cryxml:
converter_options.update({"cryxml_converter_fmt": convert_cryxml})
converters.append("cryxml_converter")
if extract_model_assets:
converters.append("model_assets_extractor")
if unsplit_textures:
converters.append("ddstexture_converter")
converter_options.update(
{
"ddstexture_converter_unsplit": True,
"ddstexture_converter_replace": not no_overwrite,
}
)
if convert_textures:
converter_options["ddstexture_converter_fmt"] = convert_textures
else:
converter_options["ddstexture_converter_fmt"] = "dds"
if convert_models:
converters.append("cgf_converter")
# convert spaces in material names for dae conversion
converter_options["cryxml_converter_mtl_fix_names"] = True
if single:
print(f"Extracting first match for filter '{file_filter}' to {output}")
print("=" * 80)
found_files = p.search(file_filter)
if not found_files:
sys.stderr.write(f"No files found for filter")
sys.exit(2)
extract_file = found_files[0]
print(f"Extracting {extract_file.filename}")
if output.name:
# given an output name - use it instead of the name in the P4K
output.parent.mkdir(parents=True, exist_ok=True)
with p.open(extract_file) as source, open(str(output), "wb") as target:
shutil.copyfileobj(source, target)
else:
output.mkdir(parents=True, exist_ok=True)
p.extract(extract_file, path=str(output), converters=converters)
else:
print(f"Extracting files into {output} with filter '{file_filter}'")
print("=" * 80)
output.mkdir(parents=True, exist_ok=True)
try:
with Progress() as progbar:
extracting_task = progbar.add_task("Extracting files", total=None)
def log(msg, progress=None, total=None, level=None, exc_info=None):
level = level or logging.INFO
logger.log(level, msg, exc_info=exc_info)
progbar.update(extracting_task, total=total, completed=progress)
p.extract_filter(
file_filter=file_filter,
path=str(output),
converters=converters,
converter_options=converter_options,
overwrite=not no_overwrite,
monitor=log,
)
except KeyboardInterrupt:
pass