Initial commit: RE tooling and docs for The I of the Dragon localization/HD
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import os, re, struct
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
GAME = r"F:\steam\steamapps\common\The I of the Dragon"
|
||||
TBL = r"E:\DragonHD\patch\tables"
|
||||
KEY = b"GBDFYTNE"
|
||||
os.makedirs(TBL, exist_ok=True)
|
||||
|
||||
CODE = 0xC1FA
|
||||
RED = (122, 0, 138, 16)
|
||||
GREEN = (140, 0, 156, 16)
|
||||
ATLAS = "Shrift_gb_Germany.TGA"
|
||||
|
||||
# 1. paint red + green solid rects into atlas
|
||||
def paint(raw, x1, y1, x2, y2, b, g, r):
|
||||
w = struct.unpack_from("<H", raw, 12)[0]
|
||||
h = struct.unpack_from("<H", raw, 14)[0]
|
||||
desc = raw[17]; off = 18 + raw[0]
|
||||
row = (lambda y: y) if (desc & 0x20) else (lambda y: h - 1 - y)
|
||||
for y in range(y1, y2):
|
||||
for x in range(x1, x2):
|
||||
i = off + (row(y) * w + x) * 4
|
||||
raw[i] = b; raw[i+1] = g; raw[i+2] = r; raw[i+3] = 255
|
||||
|
||||
loose = os.path.join(GAME, "Data", "Textures", "Ui", "Overhead")
|
||||
for p in [os.path.join(loose, "Shrift_gb.tga")]:
|
||||
if os.path.exists(p):
|
||||
d = bytearray(open(p, "rb").read()); paint(d, *RED, 0, 0, 255); paint(d, *GREEN, 0, 255, 0)
|
||||
open(p, "wb").write(d); print("loose painted", p)
|
||||
res = os.path.join(GAME, "Data", "Textures.res")
|
||||
data = bytearray(open(res, "rb").read())
|
||||
cnt = struct.unpack_from("<I", data, 0)[0]; p = 4; ent = {}
|
||||
for i in range(cnt):
|
||||
ln = struct.unpack_from("<I", data, p)[0]; p += 4
|
||||
nm = data[p:p+ln].decode("latin1"); p += ln
|
||||
o, s = struct.unpack_from("<II", data, p); p += 8; ent[nm] = (o, s)
|
||||
for nm in ["UI\\Overhead\\Shrift_gb_Germany.TGA", "UI\\Overhead\\Shrift_gb.tga"]:
|
||||
o, s = ent[nm]; b = bytearray(data[o:o+s]); paint(b, *RED, 0, 0, 255); paint(b, *GREEN, 0, 255, 0)
|
||||
data[o:o+s] = b; print("res painted", nm)
|
||||
open(res, "wb").write(data)
|
||||
|
||||
# 2. Fonts.dat: point UI English 'e' at RED
|
||||
fp = os.path.join(GAME, "Data", "Misc", "Fonts.dat")
|
||||
d = open(fp, "rb").read()
|
||||
dec = bytes(d[2:][i] ^ KEY[i % len(KEY)] for i in range(len(d) - 2)).decode("latin1")
|
||||
first = dec.index("Font"); second = dec.index("Font", first + 4)
|
||||
block0 = dec[first:second]
|
||||
pat = re.compile(r'(Code\s*=\s*"e"\s*X1\s*=\s*)(-?\d+)(\s*Y1\s*=\s*)(-?\d+)(\s*X2\s*=\s*)(-?\d+)(\s*Y2\s*=\s*)(-?\d+)')
|
||||
block0b, n = pat.subn(lambda m: "%s%d%s%d%s%d%s%d" % (m.group(1), RED[0], m.group(3), RED[1], m.group(5), RED[2], m.group(7), RED[3]), block0, count=1)
|
||||
dec2 = dec[:first] + block0b + dec[second:]
|
||||
enc = b"==" + bytes(dec2.encode("latin1")[i] ^ KEY[i % len(KEY)] for i in range(len(dec2)))
|
||||
open(fp, "wb").write(enc)
|
||||
print("Fonts.dat 'e' -> red", n)
|
||||
|
||||
# 3. cjk tables: code -> GREEN
|
||||
gt = bytearray(65536 * 16); mt = bytearray(65536 * 8)
|
||||
u1 = GREEN[0] / 256.0; v1 = GREEN[1] / 128.0; u2 = GREEN[2] / 256.0; v2 = GREEN[3] / 128.0
|
||||
struct.pack_into("<4f", gt, CODE * 16, u1, v1, u2, v2)
|
||||
struct.pack_into("<2i", mt, CODE * 8, 17, 16)
|
||||
open(os.path.join(TBL, "cjk_glyph.bin"), "wb").write(gt)
|
||||
open(os.path.join(TBL, "cjk_metric.bin"), "wb").write(mt)
|
||||
print("cjk 0x%04X -> green" % CODE)
|
||||
|
||||
# 4. strings: set menu ids to CODE
|
||||
sp = os.path.join(GAME, "Data", "Misc", "Strings.dat")
|
||||
encs = open(sp, "rb").read()
|
||||
test = bytes([CODE >> 8, CODE & 0xFF])
|
||||
pat2 = re.compile(rb'(Id\s*=\s*"([^"]*)"\s*[\r\n]+\s*English\s*=\s*")[^"]*(")')
|
||||
def repl(m):
|
||||
sid = m.group(2)
|
||||
if sid.startswith((b"MainMenu", b"OptionsMenu", b"SaveLoadMenu", b"VideoOptions", b"AudioOptions")):
|
||||
return m.group(1) + test + m.group(3)
|
||||
return m.group(0)
|
||||
encs2, n2 = pat2.subn(repl, encs)
|
||||
open(sp, "wb").write(encs2)
|
||||
print("menu strings set:", n2)
|
||||
Reference in New Issue
Block a user