#!/usr/bin/env python3 """Tái tạo native_host/fluidsynth_runtime/ — tập DLL runtime cho SFHost (T14). FluidSynth native bridge (SFHost.cpp) load libfluidsynth-3.dll + dependency closure từ thư mục này (gitignore — KHÔNG commit DLL ~13MB vào git). Cách dùng: python scripts/dl_fluidsynth_runtime.py [--out native_host/fluidsynth_runtime] Tải package MSYS2 mingw64 (repo.msys2.org), giải nén, copy closure DLL. Cần network; không cần MSYS2/vcpkg cài sẵn. """ import argparse import os import re import shutil import tarfile import tempfile import urllib.request import zstandard BASE = "https://repo.msys2.org/mingw/mingw64/" DEFAULT_OUT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "fluidsynth_runtime") # Package chứa closure DLL của libfluidsynth-3.dll (FluidSynth 2.5.6, MSYS2 mingw64). PACKAGES = [ "mingw-w64-x86_64-fluidsynth", "mingw-w64-x86_64-glib2", "mingw-w64-x86_64-libsndfile", "mingw-w64-x86_64-gcc-libs", "mingw-w64-x86_64-libiconv", "mingw-w64-x86_64-gettext-runtime", "mingw-w64-x86_64-pcre2", "mingw-w64-x86_64-zlib-ng-compat", "mingw-w64-x86_64-libffi", "mingw-w64-x86_64-libogg", "mingw-w64-x86_64-libvorbis", "mingw-w64-x86_64-flac", "mingw-w64-x86_64-mpg123", "mingw-w64-x86_64-opus", "mingw-w64-x86_64-xz", "mingw-w64-x86_64-lame", "mingw-w64-x86_64-wavpack", "mingw-w64-x86_64-libwinpthread", "mingw-w64-x86_64-portaudio", "mingw-w64-x86_64-readline", "mingw-w64-x86_64-termcap", "mingw-w64-x86_64-sdl3", ] # Closure DLL (xác minh bằng dumpbin /dependents BFS, 22 file). # ponytail: closure snapshot theo FluidSynth 2.5.6 mingw64 — nếu upgrade # libfluidsynth, chạy lại BFS dumpbin để cập nhật list này. NEEDED = [ "libfluidsynth-3.dll", "libglib-2.0-0.dll", "libgmodule-2.0-0.dll", "libgomp-1.dll", "libgcc_s_seh-1.dll", "libstdc++-6.dll", "libsndfile-1.dll", "libFLAC.dll", "libogg-0.dll", "libvorbis-0.dll", "libvorbisenc-2.dll", "libopus-0.dll", "libmpg123-0.dll", "libmp3lame-0.dll", "libiconv-2.dll", "libintl-8.dll", "libpcre2-8-0.dll", "libwinpthread-1.dll", "libportaudio.dll", "libreadline8.dll", "libtermcap-0.dll", "SDL3.dll", ] def latest(files, prefix): cands = [f for f in files if f.startswith(prefix + "-") and f.endswith(".pkg.tar.zst") and ".sig" not in f] return sorted(cands)[-1] def main(): ap = argparse.ArgumentParser() ap.add_argument("--out", default=DEFAULT_OUT) ap.add_argument("--cache", default=None, help="thư mục chứa .pkg.tar.zst đã tải") args = ap.parse_args() cache = args.cache or tempfile.mkdtemp(prefix="msys2_pkgs_") stage = tempfile.mkdtemp(prefix="msys2_stage_") print("== liệt kê mirror") html = urllib.request.urlopen(BASE, timeout=60).read().decode("utf-8", "ignore") files = re.findall(r'href="([^"]+\.pkg\.tar\.zst)"', html) dctx = zstandard.ZstdDecompressor() for pkg in PACKAGES: fname = latest(files, pkg) path = os.path.join(cache, fname) if not os.path.exists(path): print("== tải", fname) urllib.request.urlretrieve(BASE + fname, path) else: print("== có sẵn", fname) with open(path, "rb") as f: with dctx.stream_reader(f) as r: with tarfile.open(fileobj=r, mode="r|") as tf: for m in tf: if m.isfile(): tf.extract(m, stage, filter="data") bin_dir = os.path.join(stage, "mingw64", "bin") os.makedirs(args.out, exist_ok=True) missing = [] for dll in NEEDED: src = os.path.join(bin_dir, dll) if not os.path.exists(src): missing.append(dll) continue shutil.copy2(src, os.path.join(args.out, dll)) print("== copy", dll) if missing: raise SystemExit("THIẾU DLL trong stage: " + ", ".join(missing)) print("OK —", len(NEEDED), "DLL ->", args.out) if __name__ == "__main__": main()