【发布时间】:2018-10-23 02:18:56
【问题描述】:
我需要解压缩在游戏保存数据中找到的一些 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;
}
这里是示例文件数据 compressed data 的粘贴箱,其中 0x789C 和尾随 CRC 已删除。我可以提供无穷无尽的示例文件。他们都有同样的问题。
通过上述函数运行该数据将正确解压缩流的开头,但在输入字节0x18C 上失败。当文件的开头以0x000B 开头并且解压后的数据比输入数据长时,您可以判断它已正确解压。
我希望我能更多地了解放气压缩以自己解决这个问题。我最初的想法是游戏决定使用自定义版本的 zlib 或者需要为 zlib 提供额外的参数才能正确解压缩。几天来我已经四处询问并尝试了很多事情,我真的需要有这方面知识的人在这里权衡。感谢您的宝贵时间!
【问题讨论】:
-
如果您真的想深入了解这一点,提供更多的存档游戏样本和/或识别游戏以便其他人可以制作自己的游戏可能会有所帮助。
-
@mwfearnley 在这个单一的存档游戏中有数千个这样的压缩文件
-
哦,好吧..但这只是您粘贴的一个示例流,对吗?可能查看多个流可以找到数据被破坏的一致方式......另外,我想知道您如何能够验证部分数据的正确性?
-
我能够验证正确性,因为我知道输出中会发生什么。我对这个特定游戏的保存格式非常有经验,但是它只是这个游戏的一个版本具有不同的压缩。我试图批量解压缩所有流。他们中的一小部分确实解压缩而没有错误,但数据仍然部分错误。如果有帮助,我可能会发布所有的信息流
标签: c++ zlib compression deflate inflate