You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
8.2 KiB
215 lines
8.2 KiB
#!/usr/bin/env python |
|
import csv |
|
import pathlib |
|
|
|
root = pathlib.Path(__file__).resolve().parent.parent |
|
translation_dummy_path = root.joinpath("Source/translation_dummy.cpp") |
|
|
|
base_paths = { |
|
"classdat": root.joinpath("assets/txtdata/classes/classdat.tsv"), |
|
"monstdat": root.joinpath("assets/txtdata/monsters/monstdat.tsv"), |
|
"unique_monstdat": root.joinpath("assets/txtdata/monsters/unique_monstdat.tsv"), |
|
"itemdat": root.joinpath("assets/txtdata/items/itemdat.tsv"), |
|
"unique_itemdat": root.joinpath("assets/txtdata/items/unique_itemdat.tsv"), |
|
"item_prefixes": root.joinpath("assets/txtdata/items/item_prefixes.tsv"), |
|
"item_suffixes": root.joinpath("assets/txtdata/items/item_suffixes.tsv"), |
|
"questdat": root.joinpath("assets/txtdata/quests/questdat.tsv"), |
|
"spelldat": root.joinpath("assets/txtdata/spells/spelldat.tsv"), |
|
"textdat": root.joinpath("assets/txtdata/text/textdat.tsv"), |
|
} |
|
|
|
hf_paths = { |
|
"monstdat": root.joinpath("mods/Hellfire/txtdata/monsters/monstdat.tsv"), |
|
"unique_itemdat": root.joinpath("mods/Hellfire/txtdata/items/unique_itemdat.tsv"), |
|
"item_prefixes": root.joinpath("mods/Hellfire/txtdata/items/item_prefixes.tsv"), |
|
"item_suffixes": root.joinpath("mods/Hellfire/txtdata/items/item_suffixes.tsv"), |
|
"spelldat": root.joinpath("mods/Hellfire/txtdata/spells/spelldat.tsv"), |
|
} |
|
|
|
seen_pairs = set() |
|
|
|
def write_entry(temp_source, var_name, context, string_value, use_p): |
|
if not string_value: |
|
return |
|
key = (context, string_value) |
|
if key in seen_pairs: |
|
return |
|
seen_pairs.add(key) |
|
if use_p: |
|
temp_source.write(f'const char *{var_name} = P_("{context}", "{string_value}");\n') |
|
else: |
|
temp_source.write(f'const char *{var_name} = N_("{string_value}");\n') |
|
|
|
replacement_table = str.maketrans( |
|
' -', |
|
'__', |
|
'\'' |
|
) |
|
|
|
def create_identifier(value, prefix = '', suffix = ''): |
|
return prefix + value.upper().translate(replacement_table) + suffix |
|
|
|
def escape_cpp_string(s): |
|
"""Escape a string for use in a C++ string literal.""" |
|
return s.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n') |
|
|
|
def process_srt_file(srt_path, temp_source, prefix="SUBTITLE"): |
|
"""Parse an SRT file and extract subtitle text for translation.""" |
|
if not srt_path.exists(): |
|
return |
|
try: |
|
with open(srt_path, 'r', encoding='utf-8') as f: |
|
content = f.read() |
|
except Exception: |
|
return |
|
|
|
lines = content.split('\n') |
|
text = "" |
|
subtitle_index = 0 |
|
i = 0 |
|
while i < len(lines): |
|
line = lines[i] |
|
# Remove \r if present (matching C++ parser behavior) |
|
if line and line.endswith('\r'): |
|
line = line[:-1] |
|
|
|
# Skip empty lines (end of subtitle block) |
|
if not line: |
|
if text: |
|
# Remove trailing newline from text |
|
text = text.rstrip('\n') |
|
if text: |
|
var_name = f'{prefix}_{subtitle_index}' |
|
escaped_text = escape_cpp_string(text) |
|
write_entry(temp_source, var_name, "subtitle", escaped_text, False) |
|
subtitle_index += 1 |
|
text = "" |
|
i += 1 |
|
continue |
|
|
|
# Check if line is a number (subtitle index) - skip it |
|
if line.strip().isdigit(): |
|
i += 1 |
|
continue |
|
|
|
# Check if line contains --> (timestamp line) - skip it |
|
if '-->' in line: |
|
i += 1 |
|
continue |
|
|
|
# Otherwise it's subtitle text |
|
if text: |
|
text += "\n" |
|
text += line |
|
i += 1 |
|
|
|
# Handle last subtitle if file doesn't end with blank line |
|
if text: |
|
text = text.rstrip('\n') |
|
if text: |
|
var_name = f'{prefix}_{subtitle_index}' |
|
escaped_text = escape_cpp_string(text) |
|
write_entry(temp_source, var_name, "subtitle", escaped_text, False) |
|
|
|
def process_files(paths, temp_source): |
|
# Classes |
|
if "classdat" in paths: |
|
with open(paths["classdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
var_name = create_identifier(row['className'], 'CLASS_', '_NAME') |
|
write_entry(temp_source, var_name, "default", row['className'], False) |
|
|
|
# Monsters |
|
with open(paths["monstdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for row in reader: |
|
var_name = create_identifier(row['_monster_id'], '', '_NAME') |
|
write_entry(temp_source, var_name, "monster", row['name'], True) |
|
|
|
if "unique_monstdat" in paths: |
|
with open(paths["unique_monstdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for row in reader: |
|
var_name = create_identifier(row['name'], '', '_NAME') |
|
write_entry(temp_source, var_name, "monster", row['name'], True) |
|
|
|
# Items |
|
if "itemdat" in paths: |
|
with open(paths["itemdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
name = row['name'] |
|
if name in ('Scroll of None', 'Non Item', 'Book of '): |
|
continue |
|
shortName = row['shortName'] |
|
if row['id']: |
|
base_name = create_identifier(row['id']) |
|
else: |
|
base_name = create_identifier(str(i), 'ITEM_') |
|
|
|
write_entry(temp_source, f'{base_name}_NAME', "default", name, False) |
|
if shortName: |
|
write_entry(temp_source, f'{base_name}_SHORT_NAME', "default", shortName, False) |
|
|
|
with open(paths["unique_itemdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
write_entry(temp_source, f'UNIQUE_ITEM_{i}_NAME', "default", row['name'], False) |
|
|
|
with open(paths["item_prefixes"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
write_entry(temp_source, f'ITEM_PREFIX_{i}_NAME', "default", row['name'], False) |
|
|
|
with open(paths["item_suffixes"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
write_entry(temp_source, f'ITEM_SUFFIX_{i}_NAME', "default", row['name'], False) |
|
|
|
# Quests |
|
if "questdat" in paths: |
|
with open(paths["questdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
var_name = create_identifier(row['qlstr'], 'QUEST_', '_NAME') |
|
write_entry(temp_source, var_name, "default", row['qlstr'], False) |
|
|
|
# Spells |
|
with open(paths["spelldat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
var_name = create_identifier(row['name'], 'SPELL_', '_NAME') |
|
write_entry(temp_source, var_name, "spell", row['name'], True) |
|
|
|
# Text/Speeches |
|
if "textdat" in paths: |
|
with open(paths["textdat"], 'r') as tsv: |
|
reader = csv.DictReader(tsv, delimiter='\t') |
|
for i, row in enumerate(reader): |
|
write_entry(temp_source, f'TEXT_{i}', "default", row['txtstr'], False) |
|
|
|
with open(translation_dummy_path, 'w') as temp_source: |
|
temp_source.write(f'/**\n') |
|
temp_source.write(f' * @file translation_dummy.cpp\n') |
|
temp_source.write(f' *\n') |
|
temp_source.write(f' * Do not edit this file manually, it is automatically generated\n') |
|
temp_source.write(f' * and updated by the extract_translation_data.py script.\n') |
|
temp_source.write(f' */\n') |
|
temp_source.write(f'#include "utils/language.h"\n\n') |
|
temp_source.write(f'namespace {{\n\n') |
|
|
|
process_files(base_paths, temp_source) |
|
process_files(hf_paths, temp_source) |
|
|
|
# Process SRT subtitle files |
|
srt_files = [ |
|
root.joinpath("assets/gendata/diabend.srt"), |
|
] |
|
for srt_file in srt_files: |
|
# Extract filename without extension and convert to uppercase for prefix |
|
filename = srt_file.stem.upper() |
|
prefix = f"SUBTITLE_{filename}" |
|
process_srt_file(srt_file, temp_source, prefix) |
|
|
|
temp_source.write(f'\n}} // namespace\n')
|
|
|