封包的结构比较清晰,只是其中的图像是经过压缩编码的。
图像分为两种,一种是文件标识为 “GE” 的单图文件,另一种则是文件标识为 “PGD3” 的差分文件。差分文件中含有一个单图文件的文件名,当引擎读取差分文件时会同时读取单图文件,解码后将两者进行混合。代码里还备有 “PGD2” 的分支,是 “PGD3” 的一个子集。不过这个游戏里似乎并没有该类型的文件,所以先不管了。

两种图像格式的结构

struct GEHeader {
    char     magic[2];  // "GE"
    uint16_t size;
    uint32_t x;
    uint32_t y;
    uint32_t width;
    uint32_t height;
    uint32_t display_width;
    uint32_t display_height;
    uint8_t  comparess;
    uint8_t  zero[3];
};
struct PGD3Header {
    char     magic[4];  // "PGD3"
    uint16_t offset_x;
    uint16_t offset_y;
    uint16_t width;
    uint16_t height;
    uint16_t component;
    char     name[34];
}




根据 GE 文件头中的压缩指示字段的不同有 3 种不同的压缩方式,该游戏中的图像对应压缩的字段都是 3,而后根据解码出来图像的颜色深度不同,也对应着不同的处理方式。当然在内存中两者最终都会解码为 32 位 BGRA 的格式,而且会将图像的宽度向上对齐到 2^n。以上就是这个解码过程的概况了,步骤还是比较多的,而且代码也散落在好几个子函数里。虽然我觉得应该把算法全逆出来才算完整,不过我还是没法理解算法本身的内容。。。就算是逆估计也就是对着代码翻译或是直接 F5,这就是纯体力活了,所以还是算了(((((((・・;)

于是愉快地决定直接去内存里 dump 现成数据。而引擎解码对应的图片是调用了导出的 PalSpriteLoad,传递的关键参数就是资源文件名了,这使得处理变的更加简单。根据前面的分析,在解封包的时候就可以同时判断图片的类型(是不是差分),以及 GE 文件的压缩方式等,这些数据可以记录下来,随后传递给调试器即可根据文件类型自动收取对应文件的数据了。

另外脚本文件还有一小段的解密过程

以上就是 PAL 引擎(?)封包的大致情况了。