mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 18:01:03 +00:00
dlaa and the DLSS modes need nvngx_dlss.dll next to totk.exe, which the release does not ship, so most players never had DLSS. A new setup step after prepare downloads NVIDIA's own copy from the DLSS SDK repository (github.com/NVIDIA/DLSS, tag v310.9.1, NVIDIA RTX SDK license) and checks its size and SHA-256. The window has a tick box for it, on by default and named with its source and licence; the CLI takes --dlss. The step runs on Windows with an NVIDIA RTX GPU (the host check's Vulkan devices), keeps a DLSS library the player already put there, and on a failed download warns and lets the game run without DLSS; the next setup run tries again. Texts in English, Russian, Brazilian Portuguese and Simplified Chinese, docs/installer.md step 7, tests in tests/installer_test.py (DlssTest, with a file: URL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
4.6 KiB
Python
95 lines
4.6 KiB
Python
"""Command line: python -m installer --base FILE --update FILE --keys FILE --lang CODE --dest DIR [--firmware DIR]
|
|
[--root DIR] [--temp DIR] [--verify] [--dlss]
|
|
|
|
A first install needs every option. --firmware names the Switch firmware folder (an unpacked firmware or an emulator's
|
|
Contents/registered); the setup extracts the system shared fonts from it when the language is Chinese, Korean or
|
|
Traditional Chinese. On a folder with totk-install.json (resume after a cancel or failure, or update with a newer
|
|
release) only --dest is needed: --lang defaults to the recorded language, and the game files are needed only when the
|
|
extraction has to run again. Prints JSON events on stdout (events.py). Exit code: 0 done, 1 failed, 2 bad command
|
|
line, 3 cancelled (Ctrl+C or Ctrl+Break).
|
|
|
|
--dlss downloads NVIDIA's DLSS library (nvngx_dlss.dll) from NVIDIA's DLSS SDK repository into the install folder on
|
|
Windows with an NVIDIA RTX GPU (steps.py, Dlss).
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import signal
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from . import layout as layouts
|
|
from . import state as statefile
|
|
from .events import Reporter
|
|
from .runner import Context, run_steps
|
|
from .steps import all_steps, migrate_state
|
|
|
|
EXIT = {'ok': 0, 'failed': 1, 'cancelled': 3}
|
|
|
|
|
|
class Parser(argparse.ArgumentParser):
|
|
def error(self, message):
|
|
Reporter().error('cli.usage', {'detail': message})
|
|
sys.exit(2)
|
|
|
|
|
|
# The temp folder inside the install folder when the layout names none (the Linux release); the check step does not
|
|
# count it when it requires an empty install folder
|
|
TEMP_IN_DEST = '.setup-temp'
|
|
|
|
|
|
def temp_folder(base, dest):
|
|
"""One temp folder per install folder, so two installs never share generated files."""
|
|
tag = hashlib.sha256(str(dest).lower().encode()).hexdigest()[:8]
|
|
return base / f'{dest.name or "root"}-{tag}'
|
|
|
|
|
|
def cancel(signum, frame):
|
|
raise KeyboardInterrupt
|
|
|
|
|
|
def main(argv=None):
|
|
ap = Parser(prog='python -m installer', description=__doc__.splitlines()[0])
|
|
ap.add_argument('--base', type=Path, help='base game, XCI or NSP (or NSZ, XCZ: unpacked first)')
|
|
ap.add_argument('--update', type=Path, help='the update NSP or NSZ: 1.1.0, 1.2.1 or 1.4.3')
|
|
ap.add_argument('--keys', type=Path, help='prod.keys')
|
|
ap.add_argument('--firmware', type=Path, help='Switch firmware folder (Contents/registered) for the shared fonts '
|
|
'of Chinese, Korean and Traditional Chinese text')
|
|
ap.add_argument('--lang', help='game language, a Switch language code (en-US, en-GB, ja, de, ru, ...)')
|
|
ap.add_argument('--dest', type=Path, required=True, help='install folder')
|
|
ap.add_argument('--root', type=Path, help='release folder or repository (default: the folder above installer/)')
|
|
ap.add_argument('--temp', type=Path, help='temp folder (default: temp/ of the release on Windows, .setup-temp in '
|
|
'the install folder on Linux, build/install-temp in a repository)')
|
|
ap.add_argument('--verify', action='store_true', help='hash every kept file, also those this release made')
|
|
ap.add_argument('--dlss', action='store_true', help="download NVIDIA's nvngx_dlss.dll (NVIDIA RTX SDK license) next "
|
|
'to the game, for dlaa and the DLSS modes')
|
|
args = ap.parse_args(argv)
|
|
|
|
# The window cancels with Ctrl+Break to the installer's process group (CREATE_NEW_PROCESS_GROUP), which also reaches
|
|
# the tools it runs; Python's default for it ends the process without recording anything.
|
|
for name in ('SIGBREAK', 'SIGTERM'):
|
|
if hasattr(signal, name):
|
|
signal.signal(getattr(signal, name), cancel)
|
|
layout = layouts.find(args.root)
|
|
dest = args.dest.resolve()
|
|
base = args.temp or layout.temp
|
|
temp = temp_folder(base.resolve(), dest) if base else dest / TEMP_IN_DEST
|
|
log = temp / 'logs' / time.strftime('install-%Y%m%d-%H%M%S.log')
|
|
rep = Reporter(log_path=log)
|
|
t0 = time.monotonic()
|
|
try:
|
|
state = statefile.load(dest)
|
|
except statefile.StateError as e:
|
|
rep.error(e.id, {'path': str(e.path)})
|
|
rep.emit('end', result='failed', seconds=0)
|
|
return 1
|
|
ctx = Context(layout, dest, temp, rep, args, state=state, verify=args.verify)
|
|
migrate_state(ctx)
|
|
steps = all_steps()
|
|
rep.emit('start', release=layout.release, mode=ctx.mode, dev=layout.dev, dest=str(dest), log=str(log),
|
|
steps=[s.id for s in steps])
|
|
result = run_steps(steps, ctx)
|
|
rep.emit('end', result=result, seconds=round(time.monotonic() - t0, 1))
|
|
rep.close()
|
|
return EXIT[result]
|