WIX 是 WEMADE Image Index,纯索引文件;WIL 是 WEMADE Image Library,存调色板与全部 8bit 索引像素。一组资源必然是 xxx.wix + xxx.wil 成对出现,单独读 WIL 也能抠图,但定位第 N 张图的正确起点必须靠 WIX 的偏移数组。
一、WIX 文件结构(原版 Mir2)
• 0x00–0x2B(44 字节):标题 #INDX v1.0-WEMADE Entertainment inc.(实际常见为 40 字节标题 + 4 字节填充,社区实现里按 44 字节处理最稳)
- 0x2C–0x2F(4 字节 int32 LE):图片数量 imageCount
• 0x30–0x33(4 字节 int32 LE):首图数据偏移,固定 0x438 = 1080
• 接着 imageCount × 4 字节 int32 LE 数组:第 i 张图在 WIL 中的绝对字节偏移 offset[i]
Mir3/新格式 WIX 标题区缩成 20 字节,首偏移常在 0x1C,解析前先读标题串判断分支。
二、WIL 文件结构(原版 256 色版)
• 0x00–0x2B(44 字节):标题 #ILIB v1.0-WEMADE Entertainment inc.
- 0x2C–0x2F:图片数量(与 WIX 一致)
• 0x30–0x33:色深度(256)
• 0x34–0x37:调色板字节数(1024)
• 0x38–0x437(共 1024 字节):256 色调色板,每色 4 字节 BGRA(A 常置 0xFF)
- 从 0x438 开始:按 WIX 偏移数组切分每张图:
• 2 字节 short LE:width
• 2 字节 short LE:height
• 2 字节 short LE:offsetX
- 2 字节 short LE:offsetY
• 4 字节固定标志 07 00 D4 FF
• 紧跟 width × height 字节:8bit 索引像素(每字节是调色板下标,未压缩)
真彩版(16bit / 32bit / 带 WZL 压缩)头部换成 56 字节 Title(44)+ImageCount+ColorCount+PaletteSize,像素改 RGB565 或 RGBA,部分还套 LZH 变种压缩,需先解压再解码。
三、Java 基础工具类
public final class LeBuf {
public static int readIntLE(byte[] b, int off) {
return (b[off] & 0xFF)
| ((b[off+1] & 0xFF) << 8)
| ((b[off+2] & 0xFF) << 16)
| ((b[off+3] & 0xFF) << 24);
}
public static short readShortLE(byte[] b, int off) {
return (short)((b[off] & 0xFF) | ((b[off+1] & 0xFF) << 8));
}
}
四、WIX 解析
public class WixIndex {
public String title;
public int imageCount;
public int baseOffset; // 通常 0x438
public int[] offsets; // 每张图在 wil 中的绝对偏移
public static WixIndex parse(byte[] wix) {
WixIndex idx = new WixIndex();
idx.title = new String(wix, 0, 44).trim();
idx.imageCount = LeBuf.readIntLE(wix, 0x2C);
idx.baseOffset = LeBuf.readIntLE(wix, 0x30);
idx.offsets = new int[idx.imageCount];
for (int i = 0; i < idx.imageCount; i++) {
idx.offsets[i] = LeBuf.readIntLE(wix, 0x34 + i * 4);
}
return idx;
}
}
五、WIL 解析与转 BufferedImage
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class WilArchive {
private final byte[] wil;
private int[] palette = new int[256]; // 解好的 ARGB
private WixIndex index;
public WilArchive(Path wixPath, Path wilPath) throws IOException {
this.index = WixIndex.parse(Files.readAllBytes(wixPath));
this.wil = Files.readAllBytes(wilPath);
parsePalette();
}
private void parsePalette() {
// 调色板从 0x38 开始,256 × 4 字节 BGRA
int p = 0x38;
for (int i = 0; i < 256; i++) {
int b = wil[p] & 0xFF;
int g = wil[p+1] & 0xFF;
int r = wil[p+2] & 0xFF;
int a = wil[p+3] & 0xFF;
palette[i] = (a << 24) | (r << 16) | (g << 8) | b;
p += 4;
}
}
public BufferedImage getImage(int n) {
if (n < 0 || n >= index.imageCount) return null;
int pos = index.offsets[n];
int w = LeBuf.readShortLE(wil, pos) & 0xFFFF;
int h = LeBuf.readShortLE(wil, pos + 2) & 0xFFFF;
// pos+4 offsetX, pos+6 offsetY, pos+8~11 固定标志 07 00 D4 FF
int pxStart = pos + 12;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int palIdx = wil[pxStart + y * w + x] & 0xFF;
img.setRGB(x, y, palette[palIdx]);
}
}
return img;
}
public int imageCount() { return index.imageCount; }
}
六、调用示例
WilArchive arch = new WilArchive(
Path.of("Data/Objects1.wix"),
Path.of("Data/Objects1.wil"));
BufferedImage frame0 = arch.getImage(0);
System.out.println(arch.imageCount() + " frames loaded");
// 交给 Swing/JavaFX 画,或 ImageIO.write 导出 png
七、踩坑点
• 标题长度别死磕 40,原版 WIX 实际头到 0x2B 共 44 字节,图片数在 0x2C;写错偏移全图错位。
• 像素是 8bit 调色板索引,不是直接 RGB;真彩 WIL 需按 ColorCount 分支处理 RGB565/RGBA。
- 新格式 / Mir3 / GEE 的 WZL 封装是在 WIL 外再套 LZH 变种压缩,要先整包解压再按上面逻辑走。
• 偏移数组里值为 0 或指向 0x438 的是空帧(透明占位),直接返回 null 或透明图。
- 大库(Objects1.wil 几十 MB)别整库解所有帧,按需要懒加载 getImage(n) 即可。
• JMir 等开源实现里 LibHeader / ImageInfo 实体就是上述结构的对象化,可直接参照复用。
热血传奇WIX与WIL文件Java解析实现:文件头偏移表调色板转BufferedImage
来源:
作者:
点击:

