这两天搞了一下 yu-ris 的 ypf 封包,这里做个简单的记录。

ypf 主要用了 zlib 进行压缩处理。包头里有文件数量和数据区起始地址等信息。文件索引表里包含了文件的偏移、压缩后长度和原始长度等基本信息,文件名则是经过取反后存储的。文件索引表非定长。

ypf 封包

封包的结构如下:

struct YpfHeader {
    uint32_t mMagic;
    uint32_t mVersionTmp;    // 应该是版本号之类的
    uint32_t mFileCount;
    uint32_t mDataStart;
    uint8_t  mZero[16];
};
struct YpfEntry {
    uint32_t unknown;
    uint8_t name_length;     // 要解码
    uint8_t name[1];         // 要解码
    uint8_t filetype;
    uint8_t zlib_compress_flag;
    uint32_t original_length;
    uint32_t compressd_length;
    uint32_t offset;
    uint32_t unknwon;      // 估计是 crc 之类的
};

目前手头上两款游戏的 YpfHeader::mVersionTmp 字段不同,并且 YpfEntry 中也有(微小的)差异,版号大的 YpfEntry 结构中多了一个全零的 uint32_t。

文件名前面有一个指示长度的字节,但是需要经过运算才能得到真实的文件名长度。为简便起见,可以在解码过程中识别文件扩展名之前的 ‘.’,然后加上最后 3 个字符的扩展名就得到完整文件名了。

根据索引表中的偏移长度提速数据并解压即可解出原始文件。

ycg 文件

ycg 是经 zlib 压缩后的图像。其中头 0x38 个字节记录了文件信息,结构如下:

struct YcgHeader {
    uint32_t mMagic;
    uint32_t mWidth;
    uint32_t mHeight;
    uint32_t mBpp;
    uint32_t mUnkonwn[4];
    uint32_t mPart1PlainLength;
    uint32_t mPart1PackedLength;
    uint32_t mMaybeImgPosition[2];
    uint32_t mPart2PlainLength;
    uint32_t mPart2PackedLength;
};

这两部分就对应着上下半张图像,对两部分分别解压后直接拼接,最后再补上 bmp 文件头即可。

ybn 文件

经过编译后的脚本文件,带有 YSTB 标志。使用异或解码,key 要在 exe 文件中找,搜索字符串 “ysbin\yst%05d.ybn” 即可快速空降到目标附近。每个游戏的 key 应该都不相同,毕竟可以自定义。

脚本截图