其他
Galgame汉化中的逆向(三):自定义字库分析
本文为看雪论坛优秀文章
看雪论坛作者ID:devseed
0x0 前言
0x1 观察
0x2 跟踪
可以从文本缓冲区入手,去跟踪d3d9了。
这部分说起来比较麻烦,因为是COM接口没有函数名称,非常需要耐心(对照d3d9.h来找)。
跟踪从d3d9.Direct3DCreate9开始,找到初始化ID3DXFont,然后再看调用栈找到字库读取函数。
0x3 字库分析
block_size = 48
intensity_map = (
0b00000000, 0b00010001, 0b00100010, 0b00110011,
0b01000100, 0b01010101, 0b01100110, 0b01110111,
0b10001000, 0b10011001, 0b10101010, 0b10111011,
0b11001100, 0b11011101, 0b11101110, 0b11111111)
def get_x(i, width, level):
# level means nesting times
v1 = (level >> 2) + (level >> 1 >> (level >> 2))
v2 = i << v1
# 8X8 3f,
v3 = (v2 & 0x3F) + ((v2 >> 2) & 0x1C0) + ((v2 >> 3) & 0x1FFFFE00)
return ((((level << 3) - 1) & ((v3 >> 1) ^ ((v3 ^ (v3 >> 1)) & 0xF))) >> v1)+ ((((((v2 >> 6) & 0xFF) + ((v3 >> (v1 + 5)) & 0xFE)) & 3)
+ (((v3 >> (v1 + 7)) % (((width + 31)) >> 5)) << 2)) << 3)
def get_y(i, width, level):
v1 = (level >> 2) + (level >> 1 >> (level >> 2))
v2 = i << v1
v3 = (v2 & 0x3F) + ((v2 >> 2) & 0x1C0) + ((v2 >> 3) & 0x1FFFFE00)
return ((v3 >> 4) & 1) + ((((v3 & ((level << 6) - 1) & -0x20)
+ ((((v2 & 0x3F)
+ ((v2 >> 2) & 0xC0)) & 0xF) << 1)) >> (v1 + 3)) & -2) + ((((v2 >> 10) & 2) + ((v3 >> (v1 + 6)) & 1)
+ (((v3 >> (v1 + 7)) // ((width + 31) >> 5)) << 2)) << 3)
def xtx_tex12gray(data, height, width, aligned_height, aligned_width):
gray = np.zeros([width*2, height*2], dtype=np.uint8) # exchange width height
print("%dX%d xtx, %d bytes-> %dX%d Gray"%(width, height, len(data), height*2, width*2 ))
for i in range(height*width):
abs_x = get_x(i, width, 2)
abs_y = get_y(i, width, 2)
if abs_y >= height or abs_x >= width:
continue
# each 2 byte containes 4 gray pixel
idx = i * 2
block_x = (abs_x // block_size) * block_size
block_y = (abs_y // block_size) * block_size
x = abs_x % block_size
y = abs_y % block_size
target_y = block_y + y
target_x1 = block_x * 4 + x; # each block(48X48) has 4 cordinate
target_x2 = block_x * 4 + x + block_size;
target_x3 = block_x * 4 + x + block_size * 2;
target_x4 = block_x * 4 + x + block_size * 3;
gray[target_y][target_x1] = intensity_map[data[idx] >> 4]
gray[target_y][target_x2] = intensity_map[data[idx] & 0xf]
gray[target_y][target_x3] = intensity_map[data[idx+1] >> 4]
gray[target_y][target_x4] = intensity_map[data[idx+1] & 0xf]
return gray
但是光看估计很多人会对求坐标值的一堆位运算劝退,那么这些匪夷所思的位运算是在干什么?
打好表后规律一目了然,这也是这个字库最有意思的地方。
def showtable():
width = 48
height = 96
lines = []
for i in range(height*2):
lines.append(width*2*[0])
for i in range(width * height):
x = get_x(i, width, 2)
y = get_y(i, width, 2)
lines[y][x] = str(i)
print(i, y ,x)
with open('position.csv', 'w', newline='') as fp:
f_csv = csv.writer(fp)
f_csv.writerows(lines)
图中元素每4X8个像素构成一个小chunk,然后4X4个小chunk构成了一个大chunk。
这是一种分形结构,level=2为分两次,也就是我们所说的套娃。
0x4 重建字库
(2)修改fontmap或者将gb2312编码映射到sjis上,生成映射后的码表(.tbl)。
(3)将png转换为该游戏字库格式。
最简单的方法是用PIL的ImageFont打印,其中大小px要转换为pt。
0x5 后记
看雪ID:devseed
https://bbs.pediy.com/user-617776.htm
*本文由看雪论坛 devseed 原创,转载请注明来自看雪社区。
推荐文章++++
好书推荐