首页 > 解决方案 > Z_DATA_ERROR 中途膨胀

问题描述

我需要解压缩在游戏保存数据中找到的一些 zlib 压缩文件。我无法访问游戏的源代码。每个文件都以它开头0x789C,告诉我它们确实是用 zlib 压缩的。但是,所有对这些文件进行 inflate 的调用都无法完全解压缩并返回Z_DATA_ERROR. 使用 zlib 版本 1.2.5、1.2.8 和 1.2.11,结果相同。

尽管 zlib 告诉我输入数据已损坏,但我确信这不是因为游戏能够毫无问题地解压缩这些文件,而且这不是孤立于单个数据流。我有数十万个独特的数据流以相同的方式压缩,它们都Z_DATA_ERROR在解压过程中抛出了某个地方。

此外,成功解压的部分解压数据是正确的。输出完全符合预期。

此外,大约 10% 的时间,zlib 将解压缩整个文件,但结果不正确。大块的解压缩数据包含一遍又一遍重复的相同字节,这告诉我这是误报。

这是我的解压代码:

//QByteArray is a Qt wrapper for a char *
QByteArray Compression::DecompressData(QByteArray data)
{
    QByteArray result;

    int ret;
    z_stream strm;
    static const int CHUNK_SIZE = 1;//set to 1 just for debugging
    char out[CHUNK_SIZE];

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = data.size();
    strm.next_in = (Bytef*)(data.data());

    ret = inflateInit2(&strm, -15);
    if (ret != Z_OK)
    {
        qDebug() << "init error" << ret;
        return QByteArray();
    }

    do
    {
        strm.avail_out = CHUNK_SIZE;
        strm.next_out = (Bytef*)(out);

        ret = inflate(&strm, Z_NO_FLUSH);
        qDebug() << "debugging output: " << ret << QString::number(strm.total_in, 16);//This tells me which input byte caused the failure
        Q_ASSERT(ret != Z_STREAM_ERROR);

        switch (ret)
        {
        case Z_NEED_DICT:
            ret = Z_DATA_ERROR;
        case Z_DATA_ERROR:
        case Z_MEM_ERROR:
            (void)inflateEnd(&strm);
            return result;
        }

        result.append(out, CHUNK_SIZE - strm.avail_out);
    } while (strm.avail_out == 0);

    inflateEnd(&strm);
    return result;
}

这是示例文件的数据压缩数据的粘贴箱,其中删除了0x789C尾随 CRC。我可以提供无穷无尽的示例文件。他们都有同样的问题。

通过上述函数运行该数据将正确解压缩流的开头,但在输入 byte 上失败0x18C。当文件的开头开始0x000B并且解压缩的数据比输入数据长时,您可以告诉它正确解压缩。

我希望我能更多地了解放气压缩来自己解决这个问题。我最初的想法是游戏决定使用自定义版本的 zlib 或者需要为 zlib 提供额外的参数才能正确解压缩。几天来我已经四处询问并尝试了很多事情,我真的需要有这方面知识的人在这里权衡。谢谢你的时间!

标签: c++zlibcompressiondeflateinflate

解决方案


提供的数据确实是无效的 deflate 流,两者距离太远,并且在 deflate 流结束后有 8 个字节的垃圾。您的代码没有明显的问题。

正如您所指出的,在偏移 396 处,十个距离中的第一个距离太远了。这就是通货膨胀停止的地方。在偏移量 3472 处,几乎在最后,有一个长度不检查其补码的存储块。

对于太远的距离,您可以尝试使用inflateSetDictionary()right after设置一个 32K 零字节的字典inflateInit2()。然后解压缩将继续,用零填充给定的位置。这可能是也可能不是游戏正在做的事情。存储块错误没有明显的补救措施。

事实上,游戏作者可能会故意与您或任何试图解压缩其内部数据的人混淆,他们修改了 zlib 以供自己使用。


推荐阅读