"""Compile navigation labels from the existing local Chinese FRES, offline.
Run Extract-GameUiLocalization.py first. Only the small navigation label subset
is embedded; the native package, loader and dialogue table remain local.
"""
import argparse, hashlib, json, re, struct, zlib
from pathlib import Path
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--resource', type=Path, required=True)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
data = args.resource.read_bytes()
digest = hashlib.sha256(data).hexdigest()
if digest != '07eaea408329cf9b4db9767b2e5498402d28ac0d6126739a997507c31f785362':
raise ValueError('The local Chinese text resource version is not recognized')
body = zlib.decompress(data[10193412:])
names = {}
for index in range(179147):
name, key, kind = struct.unpack_from('<III', data, 0x7abc2c + index * 12)
if kind != 65537:
raise ValueError('Unexpected text resource type')
names[key] = data[144 + name:data.index(b'\0', 144 + name)].decode('ascii')
texts = {}
position = 0x597c
while True:
key, kind, buffer, offset = struct.unpack_from('<IIII', body, position)
if kind != 0x0b88b045:
break
position += 16
# Navigation labels live in the shared UTF-8 pool. Other pools are dialogue.
if buffer != 0:
continue
start = 0x2c16c0 + offset
length = struct.unpack_from('<I', body, start - 4)[0]
if length > 100000 or start + length >= len(body) or body[start + length] != 0:
raise ValueError('Invalid original string bounds')
texts[names[key]] = body[start:start + length].decode('utf-8')
prefixes = ('uss_type_', 'ext_panel_filter_', 'fss_signal_type_', 'fss_station', 'fixed_event_')
tokens = {key: value for key, value in texts.items()
if len(value) <= 160 and (key.startswith(prefixes)
or re.fullmatch(r'multiplayer_scenario\d+_title', key)
or key in ('uss', 'unknown', 'navbeacon_name', 'navbeacon_colonisation_name', 'ext_panel_location_not_scanned')
or key.startswith('uss_') and value == '$USS;')}
assert tokens['uss_type_salvage'] == '衰变信号'
assert tokens['uss_type_veryvaluablesalvage'] == '高阶材料信号'
assert tokens['multiplayer_scenario42_title'] == '导航信标'
document = dict(sourcePackageSha256='6d8256802b37041a9c819655f1cecd9432b6452171e794dba2d5a08313f1e898',
sourceResourceSha256=digest, source='Existing built-in Chinese translation resource',
generator='research/Compile-GameNavigationLocalization.py', tokens=dict(sorted(tokens.items())))
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(document, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
print(json.dumps(dict(tokens=len(tokens), output=str(args.output), sha256=hashlib.sha256(args.output.read_bytes()).hexdigest(),
gameProcessAccess=False, gameCallsInvoked=0)))
"""Compile navigation labels from the existing local Chinese FRES, offline.
Run Extract-GameUiLocalization.py first. Only the small navigation label subset
is embedded; the native package, loader and dialogue table remain local.
"""
import argparse, hashlib, json, re, struct, zlib
from pathlib import Path
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--resource', type=Path, required=True)
parser.add_argument('--output', type=Path, required=True)
args = parser.parse_args()
data = args.resource.read_bytes()
digest = hashlib.sha256(data).hexdigest()
if digest != '07eaea408329cf9b4db9767b2e5498402d28ac0d6126739a997507c31f785362':
raise ValueError('The local Chinese text resource version is not recognized')
body = zlib.decompress(data[10193412:])
names = {}
for index in range(179147):
name, key, kind = struct.unpack_from('<III', data, 0x7abc2c + index * 12)
if kind != 65537:
raise ValueError('Unexpected text resource type')
names[key] = data[144 + name:data.index(b'\0', 144 + name)].decode('ascii')
texts = {}
position = 0x597c
while True:
key, kind, buffer, offset = struct.unpack_from('<IIII', body, position)
if kind != 0x0b88b045:
break
position += 16
# Navigation labels live in the shared UTF-8 pool. Other pools are dialogue.
if buffer != 0:
continue
start = 0x2c16c0 + offset
length = struct.unpack_from('<I', body, start - 4)[0]
if length > 100000 or start + length >= len(body) or body[start + length] != 0:
raise ValueError('Invalid original string bounds')
texts[names[key]] = body[start:start + length].decode('utf-8')
prefixes = ('uss_type_', 'ext_panel_filter_', 'fss_signal_type_', 'fss_station', 'fixed_event_')
tokens = {key: value for key, value in texts.items()
if len(value) <= 160 and (key.startswith(prefixes)
or re.fullmatch(r'multiplayer_scenario\d+_title', key)
or key in ('uss', 'unknown', 'navbeacon_name', 'navbeacon_colonisation_name', 'ext_panel_location_not_scanned')
or key.startswith('uss_') and value == '$USS;')}
assert tokens['uss_type_salvage'] == '衰变信号'
assert tokens['uss_type_veryvaluablesalvage'] == '高阶材料信号'
assert tokens['multiplayer_scenario42_title'] == '导航信标'
document = dict(sourcePackageSha256='6d8256802b37041a9c819655f1cecd9432b6452171e794dba2d5a08313f1e898',
sourceResourceSha256=digest, source='Existing built-in Chinese translation resource',
generator='research/Compile-GameNavigationLocalization.py', tokens=dict(sorted(tokens.items())))
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(document, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
print(json.dumps(dict(tokens=len(tokens), output=str(args.output), sha256=hashlib.sha256(args.output.read_bytes()).hexdigest(),
gameProcessAccess=False, gameCallsInvoked=0)))