oleg-st / ZstdSharp

Port of zstd compression library to c#
MIT License
200 stars 29 forks source link

"Premature end of stream" exception when decompressing re-compressed output #19

Closed PiMoNFeeD closed 1 year ago

PiMoNFeeD commented 1 year ago

I was working on a modding tool for a game that uses ZStandard, the tool is capable of decompressing (exporting) and recompressing (importing) files compressed with zstd, it turns out that every version of ZstdSharp after 0.6.1 is unable to decompress back the buffer it has just compressed, throwing the aforementioned exception.

Example of decompressing Example of compressing

0.6.2 introduced a new argument to DecompressionStream called checkEndOfStream, and setting that to false doesn't give an exception, but the files end up being corrupted (in my case that is textures, they appear cut off at the bottom), 0.6.1 does not give any exception and does not corrupt files, either

What I do is, I open the original file from the game, it decompresses fine, then I re-import the same file, it compresses with no errors, but gives the exception when trying to decompress the same compressed buffer it just produced, and loading it in-game looks broken by the end of the file

oleg-st commented 1 year ago

You can try to add Dispose / Close to finalize the stream data.

CompressionStream adds an "end of stream" marker on Dispose (end directive). DecompressionStream expects it on Dispose to make sure all frames are decompressed.

using (var bufferWriter = new ArrayPoolBufferWriter<byte>())
{
    using (var zss = new CompressionStream(bufferWriter.AsStream(), ZstdSharp.Unsafe.Methods.ZSTD_defaultCLevel()))
    {
        zss.Write(data, 0, data.Length);
        zss.Flush();
    }
    // compression stream is finalized here (after Dispose)
    _data = bufferWriter.WrittenSpan.ToArray();
}
PiMoNFeeD commented 1 year ago

That appears to have fixed the issue, thanks!