# Toolchain: MSYS2 UCRT64, Python, Switch content tools
Set up 2026-10-20. Package set follows `refs/LASRecomp/tools/setup_windows.ps1` plus cmake, ninja, gcc, git, unzip.
`setup_windows.ps1` itself was not run.
## MSYS2 UCRT64 (`C:\msys64`)
Update: `C:\msys64\usr\bin\bash.exe -lc "pacman -Syuu --noconfirm"` (twice: first pass updates the core and exits).
Install:
pacman -S --needed --noconfirm make mingw-w64-ucrt-x86_64-{clang,lld,sdl3,opus,libvpx,ffmpeg,vulkan-devel,shaderc,
spirv-tools,pkgconf,gdb,vulkan-memory-allocator,vulkan-validation-layers,nasm,cmake,ninja,gcc,make} base-devel git unzip zip p7zip
| Package | Version |
|---|---|
| clang / lld | 23.1.3 |
| gcc | 16.2.0 |
| make (msys) / mingw32-make | 4.4.1 |
| sdl3 | 3.4.18 |
| opus | 1.6.1 (`libopus-0.dll`, voice) |
| libvpx | 1.17.0 (`libvpx.a`, linked statically for the movies) |
| ffmpeg | 9.0.2 (never linked: the MSYS2 build is GPLv3) |
| vulkan-devel group (headers, loader, utility libs, spirv-headers, spirv-tools, validation layers) | 1.4.363 |
| shaderc (glslc) | 2026.4 |
| vulkan-memory-allocator | 3.4.0, header at `/ucrt64/include/vma/vk_mem_alloc.h` |
| pkgconf | 3.0.9 |
| cmake / ninja | 4.4.4 / 1.13.2 |
| nasm / gdb | 3.02 / 18.1 |
| git / unzip / zip / p7zip | 2.56.0 / 6.0 / 3.0 / 17.06 |
Use from Git Bash: `export PATH="/c/msys64/ucrt64/bin:/c/msys64/usr/bin:$PATH"`. `tools/build_game.py` copies
`SDL3.dll`, `libiconv-2.dll` and `libopus-0.dll` next to `totk.exe`, so the game runs without MSYS2 on `PATH`.
Verify:
clang --version; ld.lld --version
pkgconf --modversion sdl3 vulkan opus vpx
glslc --version; ffmpeg -version; spirv-val --version
### Vulkan back end
# all part of the install line above plus spirv-cross and glslang; --needed makes this a check
pacman -S --needed mingw-w64-ucrt-x86_64-{vulkan-devel,vulkan-memory-allocator,vulkan-validation-layers,shaderc,spirv-tools,spirv-cross,glslang}
Verify:
pkgconf --modversion vulkan # 1.4.363
spirv-val --version; spirv-dis --version
ls /ucrt64/include/vma/vk_mem_alloc.h /ucrt64/lib/libvulkan-1.dll.a
vulkaninfo --summary # Vulkan SDK; lists the discrete GPU, Vulkan 1.3 or newer
`tools/build_game.py` links `-lvulkan-1 -luser32 -lgdi32 -ldwmapi` and adds `/ucrt64/include/vma` to the runtime include
path (`#include "vk_mem_alloc.h"`). At run time `vulkan-1.dll` comes from `System32`.
## Python
uv 0.13.3 at `%USERPROFILE%\.local\bin\uv.exe` (standalone installer, not winget).
| venv | Python | Purpose | Pins |
|---|---|---|---|
| `.venv` | 3.12.15 (uv-managed) | lifter / generator (capstone, unicorn, lz4, keystone, lief, pyelftools), oead 1.3.0, evfl, quicktex, PyYAML, Pillow, numpy, cryptography, nsz, zstandard, pyinstaller | `tools/requirements.txt` |
| `.venv-py314` | 3.14.7 (system) | MacNativeGames TotK scripts: they use stdlib `compression.zstd` (3.14+) | `tools/requirements-py314.txt` |
Version pins taken from LASRecomp `Makefile` (capstone 5.0.9, unicorn 2.1.4, lz4 4.4.5, pyinstaller 6.22.3) and
`refs/MacNativeGames/games/totk/requirements*.txt` (cryptography 50.0.2, nsz 5.0.0, Pillow 12.3.0, brotli 1.2.0).
`capstone.__version__` reports `5.0.7` although the installed wheel is 5.0.9; the wheel's metadata is right.
Recreate:
uv venv --python 3.12 .venv
uv pip install --python .venv/Scripts/python.exe -r tools/requirements.txt
uv venv --python 3.14 .venv-py314
uv pip install --python .venv-py314/Scripts/python.exe -r tools/requirements-py314.txt
Verify:
.venv\Scripts\python -c "import capstone, unicorn, lz4; print(capstone.__version__, unicorn.__version__)"
.venv\Scripts\python -c "import capstone; md=capstone.Cs(capstone.CS_ARCH_ARM64, capstone.CS_MODE_ARM); print([i.mnemonic for i in md.disasm(bytes.fromhex('1f2003d5'),0)])"
# -> ['nop']
.venv-py314\Scripts\python -c "import compression.zstd, capstone, unicorn, lz4, cryptography, nsz"
## Switch content tools (`toolchain\`)
| Tool | Path | Status |
|---|---|---|
| hactool 1.4.0 @ `1d64a83450e025622f3468c28fc4164dad2c5ef6` (the MacNativeGames pin) | `toolchain\hactool\hactool.exe` | built, static, depends only on system DLLs |
| hactoolnet | - | not installed: `github.com/Thealexbarney/LibHac` returns 404 (repo, releases and assets gone) |
hactool source: `toolchain\src\hactool` (git checkout of the pinned commit). Build: UCRT64 gcc, `config.mk` =
`CC=gcc EXEEXT=.exe CFLAGS=-O2 ... LDFLAGS=... -static`, `make -j8`.
Applied the portable part of MacNativeGames `build_app.py:build_hactool()` (kept as
`toolchain\hactool\portable-patches.diff` in the local toolchain folder, not in the repository):
1. `MNG_TITLE_KEYS` env var selects the title-key file.
2. BKTR: allow 0x200-aligned padding after the subsection table (`nca.c`).
3. BKTR: accept the subsection total-size layout of newer cumulative updates (`nca.c`).
Ours (kept locally as `toolchain\hactool\unicode-patches.diff`): `hactool.manifest` with `UTF-8`,
embedded through `hactool.rc` (Makefile rule `hactool.res.o`, `windres`). hactool converts argv, `HOME` and
`USERPROFILE` from UTF-8 to UTF-16 (`filepath.c`); without the manifest they arrive in the ANSI code page, and a
non-ASCII user profile or path stops it with `Failed to convert ... to UTF-16!`. Needs Windows 10 1903 or later.
Rebuild: in an MSYS2 UCRT64 shell (`MSYSTEM=UCRT64 C:\msys64\usr\bin\bash.exe -lc ...`) in `toolchain\src\hactool`,
`make clean && make -j8`, then copy `hactool.exe` to `toolchain\hactool\`. gcc writes temporary files to `TMP`; where
it cannot (sandboxed shells), point `TMP` and `TEMP` at a writable folder.
Not applied: the `MNG_BASE_RANGE_OFFSET/SIZE` base-file hook. It uses `funopen` (BSD/macOS only); on Windows the
base NCA must be extracted to a file first.
Verify: `toolchain\hactool\hactool.exe` prints `hactool (c) SciresM.` and usage.
Keys: hactool reads `-k `, else `$HOME\.switch\prod.keys` (falls back to `%USERPROFILE%` when `HOME` is
unset; MSYS shells set `HOME`, so pass `-k` explicitly). Use your own keys; no keys are in this repo.