Game line names across languages; RSubtitle; editor in three columns
This commit is contained in:
@@ -23,7 +23,8 @@ import time
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from ackit import banks, settings, subtitles # noqa: E402
|
||||
from ackit.catalog import scan_archive, scan_text, write_csv, hash_names # noqa: E402
|
||||
from ackit.catalog import (apply_line_names, character_of, hash_names, scan_archive, scan_text, # noqa: E402
|
||||
write_csv)
|
||||
from ackit.classify import USER_LABELS # noqa: E402
|
||||
from ackit.game import Game, default_game_dir, wad_fingerprint # noqa: E402
|
||||
from ackit.inject import read_target # noqa: E402
|
||||
@@ -87,22 +88,45 @@ def main():
|
||||
src_root = os.path.join(out_dir, "source")
|
||||
hash_names()
|
||||
|
||||
# characters from the curated lists (names come from the language they were made for: ITA)
|
||||
# 1. every package once: in-memory sounds, prefetch heads, subtitles, events (a few minutes)
|
||||
t0 = time.time()
|
||||
bank_snd, locs, sc, texts, sublocs, sub_issues, line_names = scan_text(
|
||||
game, progress=lambda k, n: print(" banks + subtitles: %d/%d files" % (k, n), flush=True)
|
||||
if k % 500 == 0 else None, characters={})
|
||||
print("packages scanned (%.0fs); %d speech sounds named by their game line" % (time.time() - t0, len(line_names)))
|
||||
|
||||
# 2. characters from the curated lists. They name ITA sounds by position (wemscan); the position of a
|
||||
# variation differs between languages, so labels go through the game's line name (valid everywhere);
|
||||
# sounds without one keep an ITA-only label.
|
||||
ref_lang = "ITA" if "ITA" in game.archives else game.languages[0]
|
||||
ref = scan_archive(ref_lang, game.wad(ref_lang), characters={})
|
||||
chars, unmatched, ambiguous = build_characters(ref, src_root)
|
||||
by_pos = {s.name: s for s in ref}
|
||||
rows = collections.defaultdict(set) # (name, language) -> characters
|
||||
for pos_name, who in chars.items():
|
||||
s = by_pos[pos_name]
|
||||
if s.key in line_names:
|
||||
rows[(line_names[s.key], "")] |= who
|
||||
else:
|
||||
rows[(pos_name, ref_lang)] |= who
|
||||
with open(os.path.join(out_dir, "characters.csv"), "w", encoding="utf-8", newline="") as f:
|
||||
w = csv.writer(f, lineterminator="\n")
|
||||
w.writerow(["name", "character"])
|
||||
for n in sorted(chars, key=str.lower):
|
||||
w.writerow([n, "; ".join(sorted(chars[n]))])
|
||||
print("characters.csv: %d sounds labelled; %d unmatched names %s; %d ambiguous %s"
|
||||
% (len(chars), len(unmatched), unmatched[:3], len(ambiguous), ambiguous[:3]))
|
||||
charmap = {n.lower(): v for n, v in ((n, "; ".join(sorted(c))) for n, c in chars.items())}
|
||||
w.writerow(["name", "character", "language"])
|
||||
for (n, lg) in sorted(rows, key=lambda r: (r[0].lower(), r[1])):
|
||||
w.writerow([n, "; ".join(sorted(rows[(n, lg)])), lg])
|
||||
print("characters.csv: %d sounds labelled (%d by line name, %d %s-only); %d unmatched names %s; %d ambiguous %s"
|
||||
% (len(rows), sum(1 for _, lg in rows if not lg), sum(1 for _, lg in rows if lg), ref_lang,
|
||||
len(unmatched), unmatched[:3], len(ambiguous), ambiguous[:3]))
|
||||
|
||||
def charmap(lang):
|
||||
return {n.lower(): "; ".join(sorted(c)) for (n, lg), c in rows.items() if not lg or lg == lang}
|
||||
|
||||
index = {"built_from": "unmodified game files", "archives": {}}
|
||||
for arch, path in game.archives.items():
|
||||
sounds = scan_archive(arch, path, characters=charmap)
|
||||
cm = charmap(arch)
|
||||
sounds = apply_line_names(scan_archive(arch, path, characters={}), line_names)
|
||||
for s in sounds:
|
||||
s.character = character_of(s, cm)
|
||||
wad = Wad(path)
|
||||
for s in sounds:
|
||||
s.sha1 = file_sha1(path, s.target, wad)
|
||||
@@ -113,11 +137,11 @@ def main():
|
||||
"with_character": sum(1 for s in sounds if s.character)}
|
||||
print("%s.csv: %d sounds %s" % (arch, len(sounds), dict(g)))
|
||||
|
||||
# in-memory sounds: loose SFX\*.bnk + banks inside the .upk packages (a few minutes)
|
||||
t0 = time.time()
|
||||
sounds, locs, sc, texts, sublocs, sub_issues = scan_text(
|
||||
game, progress=lambda k, n: print(" banks + subtitles: %d/%d files" % (k, n), flush=True)
|
||||
if k % 500 == 0 else None, characters=charmap)
|
||||
# in-memory sounds (scanned in step 1); localized speech lines carry the game's line names too
|
||||
sounds = bank_snd
|
||||
cm = charmap("") # only labels valid in every language (game line names)
|
||||
for s in sounds:
|
||||
s.character = character_of(s, cm)
|
||||
write_csv(os.path.join(out_dir, "%s.csv" % banks.ARCHIVE), sounds)
|
||||
banks.write_locations(os.path.join(out_dir, banks.LOCATIONS), locs)
|
||||
g = collections.Counter(s.group for s in sounds)
|
||||
@@ -129,8 +153,8 @@ def main():
|
||||
"prefetch_heads": {"lines": len({l.media for l in heads}), "copies": len(heads),
|
||||
"unmapped": len({l.media for l in heads if not l.sound})},
|
||||
"issues": ["%s: %s" % i for i in sc.issues]}
|
||||
print("%s.csv: %d sounds %s, %d copies in %d files, %d prefetch heads (%.0fs)"
|
||||
% (banks.ARCHIVE, len(sounds), dict(g), len(locs) - len(heads), len(sc.files), len(heads), time.time() - t0))
|
||||
print("%s.csv: %d sounds %s, %d copies in %d files, %d prefetch heads"
|
||||
% (banks.ARCHIVE, len(sounds), dict(g), len(locs) - len(heads), len(sc.files), len(heads)))
|
||||
|
||||
# subtitles (dialogue objects in the packages), one CSV per language + every stored copy
|
||||
for f in os.listdir(out_dir):
|
||||
|
||||
@@ -44,11 +44,12 @@ def main():
|
||||
# 1: browser filtered by character
|
||||
w.cmb_char.setCurrentText("Joker")
|
||||
w._filters_changed()
|
||||
w.search.setText("cinematic")
|
||||
w.search.setText("")
|
||||
w._filter_text()
|
||||
w.fit_editor()
|
||||
target = next(w.proxy.sound_at(w.proxy.index(r, 1)) for r in range(w.proxy.rowCount())
|
||||
if w.proxy.sound_at(w.proxy.index(r, 1)).seconds > 4)
|
||||
rows = [w.proxy.sound_at(w.proxy.index(r, 1)) for r in range(w.proxy.rowCount())]
|
||||
with_sub = [s for s in rows if w.ws.texts.for_sound(s, w.language)] # show the subtitle column too
|
||||
target = max(with_sub or rows, key=lambda s: min(s.seconds, 6.0))
|
||||
w.select_name(target.name)
|
||||
app.processEvents()
|
||||
# a replacement loaded for display only (not applied)
|
||||
@@ -63,7 +64,8 @@ def main():
|
||||
w.editor.sp_a.setValue(0.2)
|
||||
app.processEvents()
|
||||
w.grab().save(os.path.join(out, "main.png"))
|
||||
w.editor.grab().save(os.path.join(out, "editor.png"))
|
||||
from PySide6.QtCore import QPoint, QRect # the editor area, cut from the window (full styling)
|
||||
w.grab(QRect(w.scroll.mapTo(w, QPoint(0, 0)), w.scroll.size())).save(os.path.join(out, "editor.png"))
|
||||
# 2: category browsing
|
||||
w.clear_filters()
|
||||
w.cmb_group.setCurrentText("Speech")
|
||||
|
||||
Reference in New Issue
Block a user