c++ - 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 提供额外的参数才能正确解压缩。几天来我已经四处询问并尝试了很多事情,我真的需要有这方面知识的人在这里权衡。谢谢你的时间!
解决方案
提供的数据确实是无效的 deflate 流,两者距离太远,并且在 deflate 流结束后有 8 个字节的垃圾。您的代码没有明显的问题。
正如您所指出的,在偏移 396 处,十个距离中的第一个距离太远了。这就是通货膨胀停止的地方。在偏移量 3472 处,几乎在最后,有一个长度不检查其补码的存储块。
对于太远的距离,您可以尝试使用inflateSetDictionary()
right after设置一个 32K 零字节的字典inflateInit2()
。然后解压缩将继续,用零填充给定的位置。这可能是也可能不是游戏正在做的事情。存储块错误没有明显的补救措施。
事实上,游戏作者可能会故意与您或任何试图解压缩其内部数据的人混淆,他们修改了 zlib 以供自己使用。
推荐阅读
- r - Matlab 和 R 图像处理输出之间的差异
- kubernetes - 如何从 EKS 集群中删除 pod?
- angular - 在 Angular Ivy 中,为什么在 View Engine 中的用法之间没有缓存纯管道实例?
- modbus - 带有 RTU 成帧器的 Pymodbus 异步服务器不工作
- html - getbootstrap.com vs bootstrapdocs.com:这些应该是相同的东西吗?
- swift - 如果有非英文字符SwiftUI如何处理URL图像
- c# - .net 5.0 AssemblyLoadContext 中的插件
- android - 如何访问可组合内的@Composable 属性?
- ios - 在 tableView 末尾添加一个按钮
- google-apps-script - 如何根据以下条件在 google sheet 中发送电子邮件?