Skip to content

Instantly share code, notes, and snippets.

@BSoD123456
Last active July 2, 2022 09:10
Show Gist options
  • Save BSoD123456/45d444fe9f9af287a3ec0ab7ab9aa25d to your computer and use it in GitHub Desktop.
Save BSoD123456/45d444fe9f9af287a3ec0ab7ab9aa25d to your computer and use it in GitHub Desktop.
A localization tool for TokyoRogue
#! python3
# coding: utf-8
import os, os.path
import json
TRANS_FILE = r'G:\ohters\TokyoRogue\TokyoRogue_trans.json'
SRC_FILE = r'G:\ohters\TokyoRogue\TokyoRogue.exe'
DST_FILE = r'G:\ohters\TokyoRogue\TokyoRogue_cn.exe'
RDATA_RANGE = (0x1d1400, 0x33400)
VALID_SECTS = [
(0x1dc3d4, 0x1dc3e4),
(0x1fc200, 0x202300),
]
FIND_MINLEN = 3
SRC_CODEC = 'shift-jis'
DST_CODEC = 'gbk'
class c_str_finder:
def __init__(self, raw, *sects):
self.raw = raw
self.sects = []
for sect in sects:
st, ed = sect
ed = min(len(self.raw), ed)
self.sects.append((st, ed - st))
self.scidx = 0
def find_str(self, sect, minlen, codec):
st = self.scidx
si = st
ofst, ln = sect
while si < ln and self.raw[si + ofst] != 0:
si += 1
r = self.raw[st + ofst: si + ofst]
while si < ln and self.raw[si + ofst] == 0:
si += 1
ed = si - 1
self.scidx = si
try:
s = r.decode(codec)
except:
return st, ed, None
if len(r) < 1:
return st, ed, None
elif s == 'rァ':
pass
elif len(r) < FIND_MINLEN and (
r[0] > 0x98 or r[0] < 0x80):
return st, ed, None
return st, ed, s
def scan(self, minlen, codec):
texts = []
for sect in self.sects:
ofst, ln = sect
self.scidx = 0
while self.scidx < ln:
st, ed, s = self.find_str(sect, minlen, codec)
if not s:
continue
gbk_s = s.replace('・', '·').replace('rァ', '->')
texts.append({
'text': s,
'info': [st + ofst, ed - st],
'tran': gbk_s,
})
print(f'{st + ofst:x}: {s}')
self.texts = texts
class c_txt_modifier:
def __init__(self, raw, texts):
self.data = bytearray(raw)
self.texts = texts
self.moded = False
def trans(self, itm, codec):
s = itm['tran']
if not s:
return False
try:
b = s.encode(codec)
except:
print(f'invalid codec for tran: "{s}"')
raise ValueError('invalid codec')
b = bytearray(b)
st, ln = itm['info']
if len(b) > ln:
print(f'tran: "{s}" is too long')
raise ValueError('trans too long')
while len(b) < ln:
b.append(0)
self.data[st:st + ln] = b
return True
def scan(self, codec):
moded = False
for itm in self.texts:
if self.trans(itm, codec):
moded = True
self.moded = moded
def main():
with open(SRC_FILE, 'rb') as fd:
raw = fd.read()
texts = None
if os.path.exists(TRANS_FILE):
try:
with open(TRANS_FILE, 'r', encoding='utf-8') as fd:
texts = json.load(fd)['texts']
except:
pass
if not texts:
print('parse source file')
sf = c_str_finder(raw, *VALID_SECTS)
sf.scan(FIND_MINLEN, SRC_CODEC)
texts = sf.texts
with open(TRANS_FILE, 'w', encoding='utf-8') as fd:
json.dump({
'texts': texts,
}, fd, ensure_ascii = False)
print('trans to dest lang')
tm = c_txt_modifier(raw, texts)
tm.scan(DST_CODEC)
if tm.moded:
print('write to dest file')
with open(DST_FILE, 'wb') as fd:
fd.write(tm.data)
print('done')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment