I came across a deflate stream I needed to inflate from C# and figured I would need to compile in zlib since there didn't appear to be any API for using raw deflate with a preset dictionary. As it turns out, ICSharpCode.SharpZipLib can do this, you just have to write a fake zlib header to make its Inflater ask for a dictionary.
Last active
December 18, 2017 06:37
-
-
Save robert-nix/240c06600728d6b726a5c89cb4370c5e to your computer and use it in GitHub Desktop.
DEFLATE using a preset dictionary with SharpZipLib (and the equivalent zlib code)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using ICSharpCode.SharpZipLib.Zip.Compression; | |
using ICSharpCode.SharpZipLib.Checksums; | |
public class DictionaryExample | |
{ | |
Inflater m_inflater; | |
void InitInflater() | |
{ | |
if (m_inflater != null) | |
{ | |
return; | |
} | |
m_inflater = new Inflater(false); | |
// Read dictionary from embedded resource | |
byte[] dict; | |
using (var dictStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("packet.dict")) | |
using (var ms = new MemoryStream()) | |
{ | |
dictStream.CopyTo(ms); | |
dict = ms.GetBuffer(); | |
} | |
// Calculate checksum of dictionary | |
var adler = new Adler32(); | |
adler.Update(dict); | |
long dictAdler = adler.Value; | |
// Write fake zlib header | |
byte[] header = new byte[6]; | |
// 7 = 32k Window, 8 = DEFLATE, b = FDICT | FLEVEL_DEFAULT, b = checksum | |
ushort hword = 0x78bb; | |
header[0] = (byte)(hword >> 8); | |
header[1] = (byte)(hword & 0xff); | |
header[2] = (byte)(dictAdler >> 24); | |
header[3] = (byte)(dictAdler >> 16); | |
header[4] = (byte)(dictAdler >> 8); | |
header[5] = (byte)(dictAdler); | |
// Let inflater read our header | |
m_inflater.SetInput(header); | |
byte[] dummy = new byte[1]; | |
int hdWritten = m_inflater.Inflate(dummy); | |
if (hdWritten != 0) | |
{ | |
throw new Exception("Expected inflating header to decode 0 bytes"); | |
} | |
// Now inflater can read the dictionary | |
m_inflater.SetDictionary(dict); | |
} | |
public void Inflate(byte[] dst, byte[] src, int srcIndex, int srcCount) | |
{ | |
InitInflater(); | |
m_inflater.SetInput(src, srcIndex, srcCount); | |
int dstWritten = m_inflater.Inflate(dst); | |
if (dstWritten != dst.Length) | |
{ | |
throw new Exception("inflate didn't decode enough bytes"); | |
} | |
// To discard any SYNC_FLUSH stuff, we have to read another block | |
byte[] dummy = new byte[1]; | |
while (!m_inflater.IsNeedingInput) | |
{ | |
m_inflater.Inflate(dummy); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <zlib.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
int main() { | |
int ret, dictLen, len, flush; | |
FILE *dictFile, *packetFile; | |
char *dict, *packet; | |
z_stream strm; | |
char dst[0x8000]; | |
dictFile = fopen("packet.dict", "rb"); | |
fseek(dictFile, 0, SEEK_END); | |
dictLen = ftell(dictFile); | |
dict = (char *)malloc(dictLen); | |
fseek(dictFile, 0, SEEK_SET); | |
fread(dict, 1, dictLen, dictFile); | |
packetFile = fopen("packet.zlib", "rb"); | |
fseek(packetFile, 0, SEEK_END); | |
len = ftell(packetFile); | |
packet = (char *)malloc(len); | |
fseek(packetFile, 0, SEEK_SET); | |
fread(packet, 1, len, packetFile); | |
strm.zalloc = Z_NULL; | |
strm.zfree = Z_NULL; | |
strm.opaque = Z_NULL; | |
ret = inflateInit2(&strm, -15); | |
if (ret != Z_OK) { | |
printf("inflateInit2 failed: %d\n", ret); | |
return ret; | |
} | |
ret = inflateSetDictionary(&strm, dict, dictLen); | |
if (ret != Z_OK) { | |
printf("inflateSetDictionary failed: %d\n", ret); | |
return ret; | |
} | |
strm.next_in = packet; | |
strm.avail_in = len; | |
strm.next_out = dst; | |
strm.avail_out = 0x8000; | |
ret = inflate(&strm, 1); | |
if (ret != Z_OK) { | |
printf("inflate failed: %d\n", ret); | |
return ret; | |
} | |
fwrite(dst, 1, strm.total_out, stdout); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment