Beachhead 2000 纹理格式技术文档本文档

Beachhead 2000 纹理格式技术文档本文档 本文档记录 Beachhead 2000 游戏中所有纹理文件格式的二进制结构、解析方式和用途。 基于 Beachhead16.exe.lst 逆向分析和 TexParser 实现代码整理。格式总览扩展名格式名称色深是否有调色板用途.clutCLUT24-bit自身就是调色板256色 RGB 调色板.rgbRGB56516-bit否真彩色纹理.rmap8RMAP88-bit需要 .clut8位索引纹理地形/物体.tmap8TMAP88-bit需要 .clut8位索引动画纹理.tmap32TMAP3232-bit否带透明通道的真彩色纹理.hmapHMAP8-bit内置地形色高度图/地形数据.palPAL24-bit自身就是调色板组多组调色板.sprSPR8-bit需要 .pal精灵/按钮/字体纹理CLUT - 调色板文件结构---------------------------------------- | 偏移 | 内容 | ---------------------------------------- | 0 | R0 (红色分量第0色) | | 1 | G0 (绿色分量第0色) | | 2 | B0 (蓝色分量第0色) | | 3 | R1 | | ... | ... | | 765 | R255 | | 766 | G255 | | 767 | B255 | ----------------------------------------解析方式for (int i 0; i 256; i) { int r data[i * 3 0]; int g data[i * 3 1]; int b data[i * 3 2]; palette[i] RGB(r, g, b); }用途为.rmap8和.tmap8文件提供颜色查找表。一个纹理文件通常对应多个 CLUT 变体见 CLUT 调色板类型。RGB - RGB565 纹理文件结构------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | 8 | RAW RGB (ASCII 文件标识) | | 8 | 2 | Width (uint16, little-endian) | | 10 | 2 | Height (uint16, little-endian) | | 12 | 2 | Pixel[0] (RGB565) | | 14 | 2 | Pixel[1] | | ... | ... | ... | ------------------------------------------------解析方式// 读取 16-bit 像素 uint16_t pixel data[offset] | (data[offset 1] 8); // 解码 RGB565 int r5 (pixel 11) 0x1F; // 5-bit red int g6 (pixel 5) 0x3F; // 6-bit green int b5 pixel 0x1F; // 5-bit blue // 扩展到 8-bit int r (r5 3) | (r5 2); // 5-8 bit int g (g6 2) | (g6 4); // 6-8 bit int b (b5 3) | (b5 2); // 5-8 bit尺寸获取RMAP8 - 8位索引纹理文件结构------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | 128 | 文件头 (未知内容) | | 128 | 1 | Pixel[0] (8-bit 调色板索引) | | 129 | 1 | Pixel[1] | | ... | ... | ... | ------------------------------------------------解析方式// 跳过 128 字节头部 QByteArray pixelData data.mid(128); // 使用 CLUT 调色板渲染 QImage img(width, height, QImage::Format_Indexed8); img.setColorTable(clutPalette); memcpy(img.bits(), pixelData.constData(), width * height);常见尺寸TMAP8 - 8位索引动画纹理文件结构与 RMAP8 相同------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | 128 | 文件头 | | 128 | N | 像素数据 (8-bit 索引) | ------------------------------------------------动画检测int totalPixels pixelData.size(); int singleFramePixels width * height; int frameCount totalPixels / singleFramePixels; // 如果总像素是单帧的整数倍则为动画 if (totalPixels % singleFramePixels 0) { // 多帧动画 } // 或者高度远大于宽度时可能是竖直条带动画 if (height width * 2 height % width 0) { frameCount height / width; height width; // 每帧为正方形 }帧提取for (int f 0; f frameCount; f) { QByteArray frameData pixelData.mid(f * singleFramePixels, singleFramePixels); QImage frame indexedToImage(frameData, width, height); }TMAP32 - 32位 BGRA 纹理文件结构------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | 1 | B0 (蓝色第0像素) | | 1 | 1 | G0 (绿色) | | 2 | 1 | R0 (红色) | | 3 | 1 | A0 (Alpha透明度) | | 4 | 1 | B1 | | ... | ... | ... | ------------------------------------------------解析方式for (每个像素) { uint8_t b data[offset 0]; uint8_t g data[offset 1]; uint8_t r data[offset 2]; uint8_t a data[offset 3]; pixel RGBA(r, g, b, a); }HMAP - 高度图文件结构------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | H | 文件头 (可变大小) | | H | 1128251| 高度数据 (1501×751 字节) | ------------------------------------------------游戏引擎加载方式根据 Beachhead16.exe.lst 分析// sub_4259DA - 加载 HMap FILE* fp fopen(HMap1501x751C.hmap, rb); char buffer[1128251]; // 读取并丢弃头部通常为 128 字节 fread(buffer, 1, 128, fp); // 读取实际高度数据覆盖头部缓冲区 fread(buffer, 1, 1128251, fp); fclose(fp); // 获取高度值 uint8_t height buffer[pixelX pixelY * 1501];高度值映射高度值范围地形类型颜色0-51 (0-20%)深水深蓝52-89 (21-35%)浅水蓝色90-102 (36-40%)沙滩黄色103-178 (41-70%)草地/陆地绿色179-216 (71-85%)山丘棕色217-255 (86-100%)雪地白色游戏内地形类型值 (dword_460600)值地形0空1地面7水9深水0xA浅水0xB沙滩0xC雪地0xD天空PAL - 调色板组文件结构------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | 4 | Offset[0] (uint32, little-endian)| | 4 | 4 | Offset[1] | | ... | ... | ... | | 4N | 4 | Offset[N-1] | | Offset[0] | M0 | Palette[0] 数据 (RGB 三元组) | | Offset[1] | M1 | Palette[1] 数据 | | ... | ... | ... | ------------------------------------------------精灵头部解析struct SpriteHeader { uint8_t type; // 0x05 uint16_t width; // little-endian uint16_t height; // little-endian uint8_t palIndex; // PAL 调色板索引 uint8_t unknown[2]; // 未知 };渲染方式// 1. 加载同名 PAL 文件如 UNITS.SPR → UNITS.PAL // 2. 对每个精灵 // - 读取头部获取 width/height/palIndex // - 从 PAL 中选择对应调色板 // - 读取像素数据width × height 字节 // - 索引 0 为透明色 // - 其他索引通过调色板转换为 RGB已知 SPR 文件文件名精灵数用途MENUS.SPR28菜单元素INNAME.SPR4姓名输入界面UNITS.SPR2游戏单位坦克等CONTROLS.SPR2控件按钮LOGOS.SPR2LogoARIAL.SPR2字体CLUT 调色板类型根据 Beachhead16.exe.lst 逆向分析游戏引擎支持以下 CLUT 变体后缀完整示例说明(无)Ocean.clut基础调色板GenAlphaOceanGenAlpha.clut生成 Alpha 通道GenAlpha2OceanGenAlpha2.clut生成 Alpha 通道变体2ExpandOceanExpand.clut扩展颜色范围Expand555OceanExpand555.clut扩展到 RGB555ShadedOceanShaded.clut带阴影效果加载策略QStringList clutTypes { , GenAlpha, GenAlpha2, Expand, Expand555, Shaded }; for (const QString type : clutTypes) { QString clutPath baseName type .clut; // 尝试加载... }调色板匹配策略自动匹配规则手动指定如果自动匹配失败可通过文件对话框手动选择 CLUT/PAL 文件。文件关联关系.rmap8 / .tmap8 ──→ .clut (CLUT 调色板) .spr ──→ .pal (PAL 调色板组) .rgb ──→ (无自带 RGB565 数据) .tmap32 ──→ (无自带 BGRA 数据) .hmap ──→ (无使用内置地形色) .clut ──→ (自身就是调色板) .pal ──→ (自身就是调色板组)解析方式// 读取偏移表 while (offset fileSize) { uint32_t off read_uint32_le(); if (off fileSize) break; offsets.push_back(off); } // 读取每个调色板 for (每个偏移) { int paletteSize nextOffset - currentOffset; int colorCount paletteSize / 3; for (int c 0; c colorCount; c) { int r data[off c * 3 0]; int g data[off c * 3 1]; int b data[off c * 3 2]; } }已知 PAL 文件文件名调色板数每调色板大小用途MENUS.PAL15770 字节菜单界面INNAME.PAL4770 字节姓名输入UNITS.PAL2770 字节游戏单位CONTROLS.PAL2770 字节控件按钮LOGOS.PAL2770 字节LogoARIAL.PAL2~383 字节字体SPR - 精灵/纹理文件结构------------------------------------------------ | 偏移 | 大小 | 内容 | ------------------------------------------------ | 0 | 4 | NumSprites (uint32, little-endian)| | 4 | 4 | Offset[0] | | 8 | 4 | Offset[1] | | ... | ... | ... | | Offset[0] | 1 | Type (0x05) | | 1 | 2 | Width (uint16, little-endian) | | 3 | 2 | Height (uint16, little-endian) | | 5 | 1 | PaletteIndex (调色板索引) | | 6 | 1 | Unknown | | 7 | 1 | Unknown | | 8 | W×H | PixelData (8-bit 索引) | ------------------------------------------------本文档记录 Beachhead 2000 游戏中所有纹理文件格式的二进制结构、解析方式和用途。 基于 Beachhead16.exe.lst 逆向分析和 TexParser 实现代码整理。目录格式总览CLUT - 调色板RGB - RGB565 纹理RMAP8 - 8位索引纹理TMAP8 - 8位索引动画纹理TMAP32 - 32位 BGRA 纹理HMAP - 高度图PAL - 调色板组SPR - 精灵/纹理CLUT 调色板类型调色板匹配策略文件大小: 固定 768 字节颜色数: 256 色每色大小: 3 字节 (RGB)字节序: 无单字节分量无文件头文件头: 12 字节 (可选部分文件无头)像素格式: RGB565 (16-bit)R: 5 bits(bit 11-15)G: 6 bits(bit 5-10)B: 5 bits(bit 0-4)字节序: Little-endian优先从文件头读取 (offset 8-10)无头文件从文件名解析 (如Barge256x256.RGB)文件头: 128 字节像素格式: 8-bit 索引指向 CLUT 调色板尺寸: 从文件名解析如Ocean256x256.rmap8256×256、128×128、64×64、512×512256×128、128×256、64×128、1024×1024文件头: 128 字节像素格式: 8-bit 索引支持动画: 多帧存储在单个文件中无文件头像素格式: BGRA (32-bit)尺寸: 从文件名解析字节序: 单字节分量无字节序问题标准尺寸: 1501 × 751 像素期望数据大小: 1,128,251 字节像素格式: 8-bit 高度值 (0-255)文件头大小:fileSize - 1128251头部: N × 4 字节偏移表调色板数量: N由偏移表长度决定每调色板大小:Offset[i1] - Offset[i]字节颜色格式: RGB 三元组颜色数: 调色板大小 / 3头部: 4 NumSprites × 4 字节精灵头部: 8 字节像素格式: 8-bit 索引指向 PAL 调色板调色板索引: 精灵头部第 5 字节从 0 开始透明色: 调色板索引 0同名匹配:Texture.rmap8→Texture.clutCLUT 类型后缀: 尝试TextureGenAlpha.clut、TextureExpand.clut等尺寸剥离:Texture256x256.rmap8→Texture.clut通用调色板: 尝试Ocean.clut、Grass.clut、Sand.clut等