使用 ZLIB 库 C++ 膨胀(解压缩)PNG 文件
Inflate (decompress) PNG file using ZLIB library C++
我正在尝试使用 ZLIB 来扩充(解压缩).FLA 文件,从而提取其所有内容。由于 FLA 文件使用 ZIP 格式,我可以从中读取本地文件头 (https://en.wikipedia.org/wiki/Zip_(file_format)),并使用其中的信息来解压缩文件。
对于常规的基于文本的文件似乎工作正常,但是当涉及到二进制文件时(我只尝试过 PNG 和 DAT 文件),它无法解压缩它们,returning "Z_DATA_ERROR".
我无法在 ZLIB 中使用 minilib 库,因为 FLA 文件中的中央目录文件头与普通 zip 文件略有不同(这就是我手动读取本地文件头的原因)。
这是我用来解压一大块数据的代码:
void DecompressBuffer(char* compressedBuffer, unsigned int compressedSize, std::string& out_decompressedBuffer)
{
// init the decompression stream
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = 0;
stream.next_in = Z_NULL;
if (int err = inflateInit2(&stream, -MAX_WBITS) != Z_OK)
{
printf("Error: inflateInit %d\n", err);
return;
}
// Set the starting point and total data size to be read
stream.avail_in = compressedSize;
stream.next_in = (Bytef*)&compressedBuffer[0];
std::stringstream strStream;
// Start decompressing
while (stream.avail_in != 0)
{
unsigned char* readBuffer = (unsigned char*)malloc(MAX_READ_BUFFER_SIZE + 1);
readBuffer[MAX_READ_BUFFER_SIZE] = '[=10=]';
stream.next_out = readBuffer;
stream.avail_out = MAX_READ_BUFFER_SIZE;
int ret = inflate(&stream, Z_NO_FLUSH);
if (ret == Z_STREAM_END)
{
// only store the data we have left in the stream
size_t length = MAX_READ_BUFFER_SIZE - stream.avail_out;
std::string str((char*)readBuffer);
str = str.substr(0, length);
strStream << str;
break;
}
else
{
if (ret != Z_OK)
{
printf("Error: inflate %d\n", ret); // This is what it reaches when trying to inflate a PNG or DAT file
break;
}
// store the readbuffer in the stream
strStream << readBuffer;
}
free(readBuffer);
}
out_decompressedBuffer = strStream.str();
inflateEnd(&stream);
}
我试过压缩一个 PNG 文件并解压它。这不会 return 来自 Inflate() 的任何错误,但也不会正确地膨胀 PNG,并且唯一对应的值似乎是前几个。
原始文件(左)和解压后的代码文件(右):
Hex editor versions of both PNGs
你做的事情依赖于数据是文本和字符串,而不是二进制数据。
例如
std::string str((char*)readBuffer);
如果 readBuffer
的内容是原始二进制数据,那么它的中间可能包含一个或多个零字节。当您将它用作 C 风格字符串时,第一个零将充当字符串终止符。
建议你泛化一下,去掉字符串的依赖。相反,我建议您使用例如std::vector<int8_t>
.
同时,在过渡到更通用的方式期间,您可以执行例如
std::string str(readBuffer, length);
这将创建一个指定长度的字符串,并且不会检查内容是否有终止符。
我正在尝试使用 ZLIB 来扩充(解压缩).FLA 文件,从而提取其所有内容。由于 FLA 文件使用 ZIP 格式,我可以从中读取本地文件头 (https://en.wikipedia.org/wiki/Zip_(file_format)),并使用其中的信息来解压缩文件。
对于常规的基于文本的文件似乎工作正常,但是当涉及到二进制文件时(我只尝试过 PNG 和 DAT 文件),它无法解压缩它们,returning "Z_DATA_ERROR".
我无法在 ZLIB 中使用 minilib 库,因为 FLA 文件中的中央目录文件头与普通 zip 文件略有不同(这就是我手动读取本地文件头的原因)。
这是我用来解压一大块数据的代码:
void DecompressBuffer(char* compressedBuffer, unsigned int compressedSize, std::string& out_decompressedBuffer)
{
// init the decompression stream
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = 0;
stream.next_in = Z_NULL;
if (int err = inflateInit2(&stream, -MAX_WBITS) != Z_OK)
{
printf("Error: inflateInit %d\n", err);
return;
}
// Set the starting point and total data size to be read
stream.avail_in = compressedSize;
stream.next_in = (Bytef*)&compressedBuffer[0];
std::stringstream strStream;
// Start decompressing
while (stream.avail_in != 0)
{
unsigned char* readBuffer = (unsigned char*)malloc(MAX_READ_BUFFER_SIZE + 1);
readBuffer[MAX_READ_BUFFER_SIZE] = '[=10=]';
stream.next_out = readBuffer;
stream.avail_out = MAX_READ_BUFFER_SIZE;
int ret = inflate(&stream, Z_NO_FLUSH);
if (ret == Z_STREAM_END)
{
// only store the data we have left in the stream
size_t length = MAX_READ_BUFFER_SIZE - stream.avail_out;
std::string str((char*)readBuffer);
str = str.substr(0, length);
strStream << str;
break;
}
else
{
if (ret != Z_OK)
{
printf("Error: inflate %d\n", ret); // This is what it reaches when trying to inflate a PNG or DAT file
break;
}
// store the readbuffer in the stream
strStream << readBuffer;
}
free(readBuffer);
}
out_decompressedBuffer = strStream.str();
inflateEnd(&stream);
}
我试过压缩一个 PNG 文件并解压它。这不会 return 来自 Inflate() 的任何错误,但也不会正确地膨胀 PNG,并且唯一对应的值似乎是前几个。
原始文件(左)和解压后的代码文件(右):
Hex editor versions of both PNGs
你做的事情依赖于数据是文本和字符串,而不是二进制数据。
例如
std::string str((char*)readBuffer);
如果 readBuffer
的内容是原始二进制数据,那么它的中间可能包含一个或多个零字节。当您将它用作 C 风格字符串时,第一个零将充当字符串终止符。
建议你泛化一下,去掉字符串的依赖。相反,我建议您使用例如std::vector<int8_t>
.
同时,在过渡到更通用的方式期间,您可以执行例如
std::string str(readBuffer, length);
这将创建一个指定长度的字符串,并且不会检查内容是否有终止符。