WZX与WZL是盛大传奇后期客户端采用的图形资源封装格式,WZX承担索引职责,记录每一张图片在WZL中的起始偏移;WZL承担数据存储职责,按顺序存放文件头、图片元信息与像素流。二者同名成对出现,例如hum.wzx+hum.wzl、Prguse.wzl+Prguse.wzx。解析逻辑本质是“先读WZX拿偏移数组→按偏移seek到WZL→读图片头→读像素流→结合调色板还原RGB”。
一、WZX索引文件结构
偏移布局如下:
0x00–0x2B:44字节标题头(Title数组,内容无解析价值,直接跳过)
0x2C–0x2F:4字节兴int,图片总数ImageCount
0x30起:ImageCount×4字节,每张图在对应WZL中的绝对偏移量(兴uint32)
空图处理:ImageCount只是数组容量,实际有效图可能少于该值,偏移为0或指向空数据块时需跳过。
Python读取WZX偏移表:
importstruct
defparse_wzx(path):
withopen(path'rb')asf:
f.seek(44)
image_count=struct.unpack('<I'f.read(4))[0]
offsets=[]
foriinrange(image_count):
f.seek(48+i*4)
off=struct.unpack('<I'f.read(4))[0]
offsets.append(off)
returnimage_countoffsets
cntoffs=parse_wzx('hum.wzx')
print('image_count='cnt)
print('first5offsets='offs[:5])
二、WZL数据文件结构
WZL文件头:
0x00–0x2B:44字节标题
0x2C–0x2F:4字节int,ImageCount
0x30起:5×4字节未知保留字段(Unknow[0..4]),共20字节
之后紧跟按偏移排列的图片块。
单张图片块结构(ImageInfo头,约16字节,实际按字段累加):
偏移类型含义
0bytePixelFormat像素位深(8=256色索引,16=RGB565等)
1byteCompressed是否压缩(1=zlib/gzip,0=裸数据)
2byteReserve保留
3byteCompressLevel压缩等级
4–5int16Width宽
6–7int16Height高
8–9int16OffsetX绘制偏移X
10–11int16OffsetY绘制偏移Y
12–15int32Length像素数据长度
ImageInfo头后紧跟Length字节像素流;若Compressed=1,先zlib.decompress再处理。
三、像素还原与调色板
传奇历史资源以8bit索引图为主,16bitRGB565次之。
8bit流程:像素流每个字节是调色板下标→查256色MirPalette(TRGBQuad数组,BGRA顺序)→写出RGB。
调色板来源:客户端自带palette数据,或首个WIL文件头内嵌1024字节(256×4)调色板;新版本UI资源也可能直接存RGB565/RGBA4444,此时不走调色板。
16bitRGB565转RGB公式:
r=((v>>11)&0x1F)<<3
g=((v>>5)&0x3F)<<2
b=(v&0x1F)<<3
四、完整Python解码单张图示例(8bit索引+zlib)
importstructzlib
fromPILimportImage
#256色传奇调色板占位(实际应载入客户端MirPalette.bin)
MIR_PALETTE=[(iii)foriinrange(256)]
defread_wzl_image(wzl_pathoffset):
withopen(wzl_path'rb')asf:
f.seek(offset)
pixel_fmtcompresclevel=f.read(4)
wh=struct.unpack('<hh'f.read(4))
oxoy=struct.unpack('<hh'f.read(4))
data_len=struct.unpack('<I'f.read(4))[0]
raw=f.read(data_len)
ifcomp==1:
raw=zlib.decompress(raw)
ifpixel_fmt==8:#8bit索引
img=Image.new('RGB'(wh))
px=img.load()
idx=0
foryinrange(h):
forxinrange(w):
pal=raw[idx];idx+=1
px[xy]=MIR_PALETTE[pal]
returnimg(oxoy)
elifpixel_fmt==16:#RGB565
img=Image.new('RGB'(wh))
px=img.load()
idx=0
foryinrange(h):
forxinrange(w):
v=struct.unpack('<H'raw[idx:idx+2])[0];idx+=2
r=((v>>11)&0x1F)<<3
g=((v>>5)&0x3F)<<2
b=(v&0x1F)<<3
px[xy]=(rgb)
returnimg(oxoy)
else:
raiseValueError(f'unsupportedpixel_fmt={pixel_fmt}')
#调用示例:取第0张图
cntoffs=parse_wzx('hum.wzx')
imgoff=read_wzl_image('hum.wzl'offs[0])
print('size='img.size'offset='off)
img.save('out_0.png')
五、批量抽取整个WZL资源包
defdump_wzl_all(wzx_pathwzl_pathout_dir):
importos
os.makedirs(out_direxist_ok=True)
cntoffs=parse_wzx(wzx_path)
forioffinenumerate(offs):
ifoff==0:
continue
try:
img_=read_wzl_image(wzl_pathoff)
img.save(f'{out_dir}/{i}.png')
exceptExceptionase:
print('skip'ie)
dump_wzl_all('Prguse.wzx''Prguse.wzl''prguse_out')
六、与老版WIL/WIX格式差异
老客户端用WIL(数据)+WIX(索引),WIX头44字节标题+int计数+偏移数组,WIL头后紧跟256色调色板1024字节,图片头含宽高/偏移/阴影/长度;WZL/WZX把调色板外置或改为高位深直存,索引与数据分离更干净,支持zlib压缩,文件体积更小,解析时不再强制依赖WIL内嵌调色板。
七、常见解析异常定位
•全图黑色:调色板未载入或PixelFormat判断错
•尺寸错位花屏:ImageInfo头字节数算错,常见漏掉CompressLevel或Reserve
•偏移越界:WZX与WZL不是同一版本补丁包,文件名同但内容不匹配
-decompress报错:Compressed标志误判,裸数据当zlib解
•透明区域丢失:传奇索引图0号调色板槽通常为透明色,写PNG时应置alpha=0而非黑色
按上述字段顺序与字节对齐方式处理,WZL/WZX全部图片块均可逐张还原为PNG位图。
一、WZX索引文件结构
偏移布局如下:
0x00–0x2B:44字节标题头(Title数组,内容无解析价值,直接跳过)
0x2C–0x2F:4字节兴int,图片总数ImageCount
0x30起:ImageCount×4字节,每张图在对应WZL中的绝对偏移量(兴uint32)
空图处理:ImageCount只是数组容量,实际有效图可能少于该值,偏移为0或指向空数据块时需跳过。
Python读取WZX偏移表:
importstruct
defparse_wzx(path):
withopen(path'rb')asf:
f.seek(44)
image_count=struct.unpack('<I'f.read(4))[0]
offsets=[]
foriinrange(image_count):
f.seek(48+i*4)
off=struct.unpack('<I'f.read(4))[0]
offsets.append(off)
returnimage_countoffsets
cntoffs=parse_wzx('hum.wzx')
print('image_count='cnt)
print('first5offsets='offs[:5])
二、WZL数据文件结构
WZL文件头:
0x00–0x2B:44字节标题
0x2C–0x2F:4字节int,ImageCount
0x30起:5×4字节未知保留字段(Unknow[0..4]),共20字节
之后紧跟按偏移排列的图片块。
单张图片块结构(ImageInfo头,约16字节,实际按字段累加):
偏移类型含义
0bytePixelFormat像素位深(8=256色索引,16=RGB565等)
1byteCompressed是否压缩(1=zlib/gzip,0=裸数据)
2byteReserve保留
3byteCompressLevel压缩等级
4–5int16Width宽
6–7int16Height高
8–9int16OffsetX绘制偏移X
10–11int16OffsetY绘制偏移Y
12–15int32Length像素数据长度
ImageInfo头后紧跟Length字节像素流;若Compressed=1,先zlib.decompress再处理。
三、像素还原与调色板
传奇历史资源以8bit索引图为主,16bitRGB565次之。
8bit流程:像素流每个字节是调色板下标→查256色MirPalette(TRGBQuad数组,BGRA顺序)→写出RGB。
调色板来源:客户端自带palette数据,或首个WIL文件头内嵌1024字节(256×4)调色板;新版本UI资源也可能直接存RGB565/RGBA4444,此时不走调色板。
16bitRGB565转RGB公式:
r=((v>>11)&0x1F)<<3
g=((v>>5)&0x3F)<<2
b=(v&0x1F)<<3
四、完整Python解码单张图示例(8bit索引+zlib)
importstructzlib
fromPILimportImage
#256色传奇调色板占位(实际应载入客户端MirPalette.bin)
MIR_PALETTE=[(iii)foriinrange(256)]
defread_wzl_image(wzl_pathoffset):
withopen(wzl_path'rb')asf:
f.seek(offset)
pixel_fmtcompresclevel=f.read(4)
wh=struct.unpack('<hh'f.read(4))
oxoy=struct.unpack('<hh'f.read(4))
data_len=struct.unpack('<I'f.read(4))[0]
raw=f.read(data_len)
ifcomp==1:
raw=zlib.decompress(raw)
ifpixel_fmt==8:#8bit索引
img=Image.new('RGB'(wh))
px=img.load()
idx=0
foryinrange(h):
forxinrange(w):
pal=raw[idx];idx+=1
px[xy]=MIR_PALETTE[pal]
returnimg(oxoy)
elifpixel_fmt==16:#RGB565
img=Image.new('RGB'(wh))
px=img.load()
idx=0
foryinrange(h):
forxinrange(w):
v=struct.unpack('<H'raw[idx:idx+2])[0];idx+=2
r=((v>>11)&0x1F)<<3
g=((v>>5)&0x3F)<<2
b=(v&0x1F)<<3
px[xy]=(rgb)
returnimg(oxoy)
else:
raiseValueError(f'unsupportedpixel_fmt={pixel_fmt}')
#调用示例:取第0张图
cntoffs=parse_wzx('hum.wzx')
imgoff=read_wzl_image('hum.wzl'offs[0])
print('size='img.size'offset='off)
img.save('out_0.png')
五、批量抽取整个WZL资源包
defdump_wzl_all(wzx_pathwzl_pathout_dir):
importos
os.makedirs(out_direxist_ok=True)
cntoffs=parse_wzx(wzx_path)
forioffinenumerate(offs):
ifoff==0:
continue
try:
img_=read_wzl_image(wzl_pathoff)
img.save(f'{out_dir}/{i}.png')
exceptExceptionase:
print('skip'ie)
dump_wzl_all('Prguse.wzx''Prguse.wzl''prguse_out')
六、与老版WIL/WIX格式差异
老客户端用WIL(数据)+WIX(索引),WIX头44字节标题+int计数+偏移数组,WIL头后紧跟256色调色板1024字节,图片头含宽高/偏移/阴影/长度;WZL/WZX把调色板外置或改为高位深直存,索引与数据分离更干净,支持zlib压缩,文件体积更小,解析时不再强制依赖WIL内嵌调色板。
七、常见解析异常定位
•全图黑色:调色板未载入或PixelFormat判断错
•尺寸错位花屏:ImageInfo头字节数算错,常见漏掉CompressLevel或Reserve
•偏移越界:WZX与WZL不是同一版本补丁包,文件名同但内容不匹配
-decompress报错:Compressed标志误判,裸数据当zlib解
•透明区域丢失:传奇索引图0号调色板槽通常为透明色,写PNG时应置alpha=0而非黑色
按上述字段顺序与字节对齐方式处理,WZL/WZX全部图片块均可逐张还原为PNG位图。

