Wordwrapping working

This commit is contained in:
dazedanon 2026-03-18 06:17:02 -05:00
parent 38a6819722
commit c5fd435aef

View file

@ -355,11 +355,13 @@ def extract_dialogue(raw):
# ── pack ─────────────────────────────────────────────────────────────────────
def patch_ystb(raw, translations):
def patch_ystb(raw, translations, wrap_nl_bytes=b'\x0a'):
"""
Patch translated text into a YSTB binary.
translations : {src_text: tl_text}
wrap_nl_bytes : bytes to insert as line-break separator (default b'\x0a').
YU-RIS native newline is b'\xef\xf0' (two-byte control code).
Returns patched bytes, or None if nothing changed (no matches found).
Strategy:
@ -411,6 +413,9 @@ def patch_ystb(raw, translations):
new_bytes = tl.encode('cp932')
except Exception:
new_bytes = tl.encode('utf-8')
# Replace sentinel 0x01 with the requested newline bytes
if wrap_nl_bytes != b'\x0a':
new_bytes = new_bytes.replace(b'\x01', wrap_nl_bytes)
attr_replacements[attr_idx] = new_bytes
elif a_type == 3:
@ -824,6 +829,25 @@ def patch_font_sizes(raw, style_sizes):
return bytes(new_header) + instr_enc + new_attrs_enc + new_vals_enc + tail
# ── word-wrap helper ────────────────────────────────────────────────────────────
def _wrap_text(text, width):
"""
Word-wrap `text` to at most `width` characters per line.
Returns the wrapped string with '\n' (0x0a) as the line separator.
Preserves any manual '\n' already present in the text.
"""
import textwrap
paragraphs = text.split('\n')
lines = []
for para in paragraphs:
if len(para) <= width:
lines.append(para)
else:
lines.extend(textwrap.wrap(para, width=width) or [''])
return '\n'.join(lines)
# ── CLI commands ──────────────────────────────────────────────────────────────
def cmd_extract(args):
@ -882,6 +906,12 @@ def cmd_extract(args):
def cmd_pack(args):
json_dir = args.json_dir
ysbin_dir = args.ysbin_dir
wrap_width = getattr(args, 'wrap', 0) or 0
wrap_nl_str = getattr(args, 'wrap_newline', 'eff0') or 'eff0'
try:
wrap_nl_bytes = bytes.fromhex(wrap_nl_str.replace(' ', '').replace('0x', ''))
except ValueError:
wrap_nl_bytes = b'\xef\xf0'
patched = 0
skipped = 0
@ -948,6 +978,14 @@ def cmd_pack(args):
if not e.get('src') or not e.get('tl') or e['tl'] == e['src']:
continue
tl = e['tl']
# Word-wrap English speech text if requested.
# Only wrap if the translation looks like English (no CJK).
if wrap_width and tl and not any(0x3000 <= ord(c) <= 0x9fff for c in tl):
tl = _wrap_text(tl, wrap_width)
# If a non-default newline byte is requested, store a sentinel
# that will be replaced after cp932 encoding below.
if wrap_nl_bytes != b'\x0a':
tl = tl.replace('\n', '\x01') # temp sentinel (0x01 unused in text)
key = e.get('_raw', e['src'])
# Re-attach the ORIGINAL speaker prefix from _raw so the engine
# continues to recognise the name in its es.CHAR.NAME registry.
@ -974,7 +1012,7 @@ def cmd_pack(args):
with open(ybn_path, 'rb') as f:
raw = f.read()
result = patch_ystb(raw, translations)
result = patch_ystb(raw, translations, wrap_nl_bytes=wrap_nl_bytes)
if result is None:
print(f' SKIP {fname}: no matching strings found in binary')
skipped += 1
@ -1375,6 +1413,10 @@ def main():
p_pack = sub.add_parser('pack', help='JSON → .ybn (patch in-place)')
p_pack.add_argument('--ysbin-dir', default='data/ysbin')
p_pack.add_argument('--json-dir', default='json')
p_pack.add_argument('--wrap', type=int, default=0, metavar='N',
help='Auto word-wrap English lines to N chars (0 = off, try 50)')
p_pack.add_argument('--wrap-newline', default='eff0', metavar='HEX',
help='Hex bytes for line separator (default: eff0 = YU-RIS native newline 0xEF 0xF0)')
# repack-ypf
p_ypf = sub.add_parser('repack-ypf', help='data/ysbin/*.ybn → patched .ypf')