Version 3.2.0: fast lists with loading spinners, names from the game files, INT docs and screenshots, cleanup

This commit is contained in:
2026-10-10 14:58:17 +02:00
parent fbde809370
commit 1586abb8e9
40 changed files with 449 additions and 19279 deletions

View File

@@ -5,7 +5,8 @@ Sources, in order:
1. data/catalog/<ARCHIVE>.csv -- shipped catalog (instant) when the WAD fingerprint matches;
2. otherwise a header scan of the WAD (a few seconds, cached in the user cache), classified with
`classify` and labelled with data/catalog/characters.csv (speaker per sound name).
Names follow the community hash list (ackit/data/hashlist.txt): an entry with one WEM is NAME
Names come from the game: a WAD entry ID is the Wwise ID (FNV-1) of a soundbank listed in the game's
CookedPCConsole/CookedBanks.txt (`bank_names`); an entry with one WEM is NAME
(alias NAME_1), entries with n variations are NAME_1..NAME_n (alias NAME#i); stream-pack media are
named by their ID or <Pack>_<ID>.
"""
@@ -21,10 +22,11 @@ from dataclasses import dataclass, asdict
from . import banks, classify, subtitles
from . import lines as lines_mod
from .game import wad_fingerprint
from .bnk import fnv1
from .lines import bank_langs
from .inject import pack_bnk_path
from .pack import Pack
from .paths import catalog_dir, package_data, user_dir
from .paths import catalog_dir, user_dir
from .wad import Wad
FIELDS = ["archive", "target", "name", "aliases", "group", "category", "bank", "language", "line", "character",
@@ -78,21 +80,28 @@ class Sound:
# --- static data ---------------------------------------------------------------------------
_names = None
_names = {} # cooked folder -> {Wwise ID: bank name}
def hash_names():
global _names
if _names is None:
_names = {}
for ln in open(package_data("hashlist.txt"), encoding="latin-1"):
p = ln.rstrip("\r\n").split("\t")
if len(p) >= 2 and p[1]:
try:
_names[int(p[0], 16)] = p[1]
except ValueError:
pass
return _names
def bank_names(cooked):
"""{Wwise ID: name} of the game's soundbanks, from its own CookedBanks.txt (ID = FNV-1 of the name). They
name the WAD entries (each entry holds the streamed media of one bank) and the stream packs."""
key = os.path.normcase(os.path.abspath(cooked))
if key not in _names:
out = {}
p = os.path.join(cooked, "CookedBanks.txt")
if os.path.exists(p):
for ln in open(p, encoding="latin-1"):
n = ln.strip()
if n:
out.setdefault(fnv1(n), n)
_names[key] = out
return _names[key]
def cooked_of(wad_path):
"""CookedPCConsole folder of a WAD (WADs are in CookedPCConsole/<language or SFX>/)."""
return os.path.dirname(os.path.dirname(os.path.abspath(wad_path)))
def load_characters(path=None, lang=None):
@@ -180,7 +189,7 @@ def _walk_entry(f, offset, size):
def scan_archive(archive, wad_path, characters=None, progress=None):
"""Header scan of one WAD -> [Sound]."""
names = hash_names()
names = bank_names(cooked_of(wad_path))
characters = characters if characters is not None else load_characters(lang=archive)
wad = Wad(wad_path)
out = []