Version 3.2.1: whole bank copies of voice lines, unique names, docs figures checked by tools/check_docs.py
This commit is contained in:
@@ -234,10 +234,11 @@ def scan_archive(archive, wad_path, characters=None, progress=None):
|
||||
_CAMEL = re.compile(r"(?<=[a-z])(?=[A-Z])")
|
||||
|
||||
|
||||
def bank_sounds(sc, characters=None):
|
||||
"""[Sound] of the in-memory sounds found by `banks.scan` (prefetch heads belong to WAD sounds)."""
|
||||
def bank_sounds(sc, characters=None, reserved=()):
|
||||
"""[Sound] of the in-memory sounds found by `banks.scan` (prefetch heads belong to WAD sounds). `reserved`:
|
||||
lower-case names taken by other archives (sfx_names)."""
|
||||
characters = characters if characters is not None else load_characters()
|
||||
names = banks.media_names(sc)
|
||||
names = banks.media_names(sc, reserved)
|
||||
every = set().union(*(e.get("langs", set()) for e in sc.media.values())) - {""}
|
||||
out = []
|
||||
for m, e in sc.media.items():
|
||||
@@ -275,13 +276,45 @@ def derive_banks(game, sc, characters=None):
|
||||
"""(bank sounds, locations, Lines) from a Scan: the lines, the prefetch heads' lines, the in-memory sounds
|
||||
with their line identities and names."""
|
||||
lines = build_lines(game, sc)
|
||||
mark_heads(sc, whole_recordings(sc, lines))
|
||||
locs = [l._replace(sound=lines.head_key(l)) if l.kind == banks.PREFETCH_HEAD else l for l in sc.locations]
|
||||
snd = bank_sounds(sc, characters)
|
||||
snd = bank_sounds(sc, characters, sfx_names(game))
|
||||
lines.names = line_names(game, sc, lines, {s.key: s.name for s in snd})
|
||||
apply_lines(snd, lines, lines.names)
|
||||
return snd, locs, lines
|
||||
|
||||
|
||||
def whole_recordings(sc, lines):
|
||||
"""Media stored in memory that are WAD recordings of their bank's language (the same media ID, identical
|
||||
bytes: 49 in the game, e.g. game-over taunts). Like prefetch heads, these copies belong to the WAD line
|
||||
and are written with it; they are not sounds of their own."""
|
||||
keys = collections.defaultdict(list)
|
||||
for l in sc.locations:
|
||||
if l.kind == banks.MEMORY:
|
||||
k = lines.head_key(l)
|
||||
keys[l.media].append(bool(k) and not k.startswith(banks.ARCHIVE + ":"))
|
||||
return {m for m, wad in keys.items() if all(wad)}
|
||||
|
||||
|
||||
def mark_heads(sc, media):
|
||||
"""Treat the bank copies of `media` as heads of WAD recordings (see whole_recordings)."""
|
||||
if not media:
|
||||
return
|
||||
for m in media:
|
||||
if m in sc.media:
|
||||
sc.media[m]["kinds"] = {banks.PREFETCH_HEAD}
|
||||
sc.prefetch.add(m)
|
||||
sc.locations = [l._replace(kind=banks.PREFETCH_HEAD) if l.media in media else l for l in sc.locations]
|
||||
|
||||
|
||||
def sfx_names(game):
|
||||
"""Lower-case names and aliases of the SFX WAD's sounds: in-memory sounds of the same bank must not take
|
||||
them (`<Bank>_<n>` is the position name of the bank's streamed sounds too)."""
|
||||
if "SFX" not in game.archives:
|
||||
return set()
|
||||
return {n.lower() for s in scan_archive("SFX", game.wad("SFX"), characters={}) for n in s.names()}
|
||||
|
||||
|
||||
def line_names(game, sc, lines, bank_names=None):
|
||||
"""{line id: name} (lines.line_names). Lines no event names keep their English recording's name: the
|
||||
position-based name in the INT WAD (`NAME_i`, or `NAME#i` where that is taken) or the bank-numbered name."""
|
||||
@@ -378,6 +411,10 @@ def scan_text(game, progress=None, characters=None):
|
||||
sc = banks.scan(game.cooked, progress, visit=col)
|
||||
names = {n for n in sc.names if n.lower().startswith("speech_")}
|
||||
names |= {n for n, _ in banks.media_names(sc).values() if n.lower().startswith("speech_")}
|
||||
lookup = [n for n in col.dialogues if n.startswith("#lookup:")]
|
||||
sc.subtitle_objects = {"dialogue_objects": sum(len(v) for n, v in col.dialogues.items() if n not in lookup),
|
||||
"dialogue_lines": len(col.dialogues) - len(lookup),
|
||||
"scripted_objects": sum(len(col.dialogues[n]) for n in lookup)}
|
||||
col.resolve(names)
|
||||
# the subtitle objects name their speech events too: an event ID is the FNV-1 hash of its name, so these
|
||||
# names find events no package name table lists (e.g. Vicki Vale's radio reports)
|
||||
@@ -519,6 +556,11 @@ class Catalog:
|
||||
key = os.path.splitext(os.path.basename(name))[0].lower() if re.search(r"\.\w{2,4}$", name) else name.lower()
|
||||
hits = self._by_name.get(key, [])
|
||||
uniq = list({id(s): s for s in hits}.values())
|
||||
if len(uniq) > 1:
|
||||
# a name wins over another sound's alias: a line named after its INT position (NAME#i) can equal
|
||||
# the position alias of another recording in another language's WAD
|
||||
own = [s for s in uniq if s.name.lower() == key]
|
||||
uniq = own if len(own) == 1 else uniq
|
||||
if len(uniq) == 1:
|
||||
return uniq[0]
|
||||
if len(uniq) > 1:
|
||||
|
||||
Reference in New Issue
Block a user