diff --git a/game/engines/default/engine/PlayerProfile.lua b/game/engines/default/engine/PlayerProfile.lua
index 36f9a40fa3b3674234ac32b3ea1d9ed0d40cb71e..e03e900b4bc609b43accfe34555bf5df787acdf2 100644
--- a/game/engines/default/engine/PlayerProfile.lua
+++ b/game/engines/default/engine/PlayerProfile.lua
@@ -145,7 +145,6 @@ function _M:loadGenericProfile()
 				local field = file:gsub(".profile$", "")
 				self.generic[field] = self.generic[field] or {}
 				self:loadData(f, self.generic[field])
-				if not self.generic[field].__uuid then self.generic[field].__uuid = util.uuid() end
 			end
 		end
 	end
@@ -223,7 +222,6 @@ function _M:loadModuleProfile(short_name)
 					else
 						self.modules[short_name][field] = self.modules[short_name][field] or {}
 						self:loadData(f, self.modules[short_name][field])
-						if not self.modules[short_name][field].__uuid then self.modules[short_name][field].__uuid = util.uuid() end
 					end
 				end
 			end
@@ -379,7 +377,7 @@ function _M:eventGetNews(e)
 end
 
 function _M:eventGetConfigs(e)
-	local data = e.data:unserialize()
+	local data = zlib.decompress(e.data):unserialize()
 	local module = e.module
 	if not data then print("[ONLINE PROFILE] get configs") return end
 	for name, val in pairs(data) do
@@ -464,7 +462,7 @@ function _M:setConfigs(module, name, val)
 	if not self.auth then return end
 	if name == "online" then return end
 	if type(val) ~= "string" then val = serialize(val) end
-	core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, data=table.serialize{[name] = val}})
+	core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, data=zlib.compress(table.serialize{[name] = val})})
 end
 
 function _M:syncOnline(module)
@@ -477,7 +475,7 @@ function _M:syncOnline(module)
 	local data = {}
 	for k, v in pairs(sync) do if k ~= "online" then data[k] = serialize(v) end end
 
-	core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, data=table.serialize(data)})
+	core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, data=zlib.compress(table.serialize(data))})
 end
 
 function _M:checkModuleHash(module, md5)
diff --git a/game/loader/pre-init.lua b/game/loader/pre-init.lua
index 7b78c5f9a44916c7cb4849822c633fb0b4052e2b..c5c759fc5f2eb06f74e5cb109244f48cc424dff0 100644
--- a/game/loader/pre-init.lua
+++ b/game/loader/pre-init.lua
@@ -82,11 +82,13 @@ function table.serialize(src, sub)
 		local nk, ne = k, e
 		local tk, te = type(k), type(e)
 
-		if tk == "table" then nk = table.serialize(nk, true)
-		elseif tk == "string" then -- nothing, we are good
+		if tk == "table" then nk = "["..table.serialize(nk, true).."]"
+		elseif tk == "string" then nk = string.format("[%q]", nk)
 		else nk = "["..nk.."]"
 		end
 
+		if not sub then nk = "_G"..nk end
+
 		if te == "table" then
 			str = str..string.format("%s=%s ", nk, table.serialize(ne, true))
 		elseif te == "number" then
@@ -106,7 +108,7 @@ function string.unserialize(str)
 	local f, err = loadstring(str)
 	if not f then print("[UNSERIALIZE] error", err, str) return nil end
 	local t = {}
-	setfenv(f, t)
+	setfenv(f, setmetatable(t, {__index={_G=t}}))
 	local ok, err = pcall(f)
-	if ok then return t else print("[UNSERIALIZE] error", err, str) return nil end
+	if ok then return setmetatable(t, nil) else print("[UNSERIALIZE] error", err, str) return nil end
 end
diff --git a/game/profile-thread/Client.lua b/game/profile-thread/Client.lua
index 04de4ca246d76674b7e8d879b9809d903f37241e..57fef177f8fa645df3c765fbcad697ee447a1db3 100644
--- a/game/profile-thread/Client.lua
+++ b/game/profile-thread/Client.lua
@@ -29,7 +29,7 @@ end
 
 function _M:connected()
 	if self.sock then return true end
-	self.sock = socket.connect("te4.org", 2257)
+	self.sock = socket.connect("te4.org", 2259)
 	if not self.sock then return false end
 --	self.sock:settimeout(10)
 	print("[PROFILE] Thread connected to te4.org")
@@ -40,8 +40,9 @@ end
 --- Connects the second tcp channel to receive data
 function _M:connectedPull()
 	if self.psock then return true end
-	self.psock = socket.connect("te4.org", 2258)
+	self.psock = socket.connect("te4.org", 2260)
 	if not self.psock then return false end
+--	self.psock:settimeout(10)
 	print("[PROFILE] Pull socket connected to te4.org")
 	self.psock:send(self.auth.push_id.."\n") -- Identify ourself
 	return true
@@ -57,8 +58,30 @@ function _M:disconnect()
 	core.game.sleep(5000) -- Wait 5 secs
 end
 
+function _M:receive(size)
+	local try = 0
+	local l, err = nil, "timeout"
+	while not l and err == "timeout" and try < 10 do
+		l, err = self.sock:receive(size)
+		try = try + 1
+	end
+	if not l then
+		if err == "closed" then
+			print("[PROFILE] connection disrupted, trying to reconnect", err)
+			self:disconnect()
+		end
+		return nil
+	end
+	return l
+end
+
 function _M:read(ncode)
-	local l, err = self.sock:receive("*l")
+	local try = 0
+	local l, err = nil, "timeout"
+	while not l and err == "timeout" and try < 10 do
+		l, err = self.sock:receive("*l")
+		try = try + 1
+	end
 	if not l then
 		if err == "closed" then
 			print("[PROFILE] connection disrupted, trying to reconnect", err)
@@ -74,7 +97,12 @@ function _M:read(ncode)
 end
 
 function _M:pread(ncode)
-	local l, err = self.psock:receive("*l")
+	local try = 0
+	local l, err = nil, "timeout"
+	while not l and err == "timeout" and try < 10 do
+		l, err = self.psock:receive("*l")
+		try = try + 1
+	end
 	if not l then
 		if err == "closed" then
 			print("[PROFILE] push connection disrupted, trying to reconnect", err)
@@ -201,7 +229,7 @@ function _M:orderGetNews(o)
 		size = tonumber(size)
 		if not size or size < 1 or not title then cprofile.pushEvent("e='News' news=false") return end
 
-		local body = self.sock:receive(size)
+		local body = self:receive(size)
 		cprofile.pushEvent(string.format("e='GetNews' news=%q", table.serialize{title=title, body=body}))
 	else
 		cprofile.pushEvent("e='GetNews' news=false")
@@ -215,7 +243,7 @@ function _M:orderGetConfigs(o)
 		local _, _, size = self.last_line:find("^([0-9]+)")
 		size = tonumber(size)
 		if not size or size < 1 then return end
-		local body = self.sock:receive(size)
+		local body = self:receive(size)
 		cprofile.pushEvent(string.format("e='GetConfigs' module=%q data=%q", o.module, body))
 	end
 end
@@ -279,7 +307,7 @@ function _M:orderChatUserInfo(o)
 		local _, _, size = self.last_line:find("^([0-9]+)")
 		size = tonumber(size)
 		if not size or size < 1 then return end
-		local body = self.sock:receive(size)
+		local body = self:receive(size)
 		cprofile.pushEvent(string.format("e='Chat' se='UserInfo' user=%q data=%q", o.user, body))
 	end
 end
diff --git a/premake4.lua b/premake4.lua
index 01f89b90c9387b6066ec116b729c5b2f1109a1ac..13dc940720745d8ed8274e9c1be9e6d14d59e75d 100644
--- a/premake4.lua
+++ b/premake4.lua
@@ -89,7 +89,7 @@ project "TEngine"
 	language "C"
 	targetname "t-engine"
 	files { "src/*.c", }
-	links { "physfs", "lua".._OPTIONS.lua, "fov", "luasocket", "luaprofiler", "lualanes", "lpeg", "tcodimport", "lxp", "expatstatic", "luamd5" }
+	links { "physfs", "lua".._OPTIONS.lua, "fov", "luasocket", "luaprofiler", "lualanes", "lpeg", "tcodimport", "lxp", "expatstatic", "luamd5", "luazlib" }
 	defines { "_DEFAULT_VIDEOMODE_FLAGS_='SDL_HWSURFACE|SDL_DOUBLEBUF'" }
 	defines { [[TENGINE_HOME_PATH='".t-engine"']] }
 
@@ -114,7 +114,7 @@ configuration "windows"
 	defines { [[TENGINE_HOME_PATH='"T-Engine"']], 'SELFEXE_WINDOWS' }
 
 configuration "linux"
-	links { "SDL", "SDL_ttf", "SDL_image", "SDL_mixer", "GL", "GLU", "m", "pthread", "uuid" }
+	links { "SDL", "SDL_ttf", "SDL_image", "SDL_mixer", "GL", "GLU", "m", "pthread" }
 	defines { [[TENGINE_HOME_PATH='".t-engine"']], 'SELFEXE_LINUX' }
 
 configuration {"linux", "Debug"}
@@ -266,3 +266,10 @@ project "luamd5"
 	targetname "luamd5"
 
 	files { "src/luamd5/*.c", }
+
+project "luazlib"
+	kind "StaticLib"
+	language "C"
+	targetname "luazlib"
+
+	files { "src/lzlib/*.c", }
diff --git a/src/lzlib/Makefile b/src/lzlib/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..8bbfef86528a8bee73af370bea55a2c5d1f377d7
--- /dev/null
+++ b/src/lzlib/Makefile
@@ -0,0 +1,57 @@
+# $Id: Makefile,v 1.8 2004/07/22 19:10:47 tngd Exp $
+# makefile for zlib library for Lua
+
+# dist location
+DISTDIR=$(HOME)/dist
+TMP=/tmp
+
+# change these to reflect your Lua installation
+LUA= $(HOME)/local/lua
+LUAINC= $(LUA)/include
+LUALIB= $(LUA)/lib
+LUABIN= $(LUA)/bin
+
+ZLIB=../zlib-1.2.3
+
+# no need to change anything below here
+CFLAGS= $(INCS) $(DEFS) $(WARN) -O2 -fPIC
+WARN= -g -Werror -Wall -pedantic #-ansi -pedantic -Wall
+INCS= -I$(LUAINC) -I$(ZLIB)
+LIBS= -L$(ZLIB) -lz -L$(LUALIB) -L$(LUABIN) # -llua51
+
+MYLIB=lzlib
+
+ZLIB_NAME = zlib
+GZIP_NAME = gzip
+
+T_ZLIB= $(ZLIB_NAME).so
+T_GZIP= $(GZIP_NAME).so
+
+VER=0.3
+TARFILE = $(DISTDIR)/$(MYLIB)-$(VER).tar.gz
+TARFILES = 	Makefile README README.lgzip \
+			lzlib.c lgzip.c \
+			test_zlib.lua test_gzip.lua
+
+all: $(T_ZLIB) $(T_GZIP)
+
+test: $(T_ZLIB) $(T_GZIP)
+	$(LUABIN)/lua -lluarc test_zlib.lua
+	$(LUABIN)/lua -lluarc test_gzip.lua
+
+$(T_ZLIB): lzlib.o
+	$(CC) -o $@ -shared $< $(LIBS)
+
+$(T_GZIP): lgzip.o
+	$(CC) -o $@ -shared $< $(LIBS)
+
+clean:
+	rm -f *.o *.so core core.* a.out
+
+dist: $(TARFILE)
+
+$(TARFILE): $(TARFILES)
+	@ln -sf `pwd` $(TMP)/$(MYLIB)-$(VER)
+	tar -zcvf $(TARFILE) -C $(TMP) $(addprefix $(MYLIB)-$(VER)/,$(TARFILES))
+	@rm -f $(TMP)/$(MYLIB)-$(VER)
+	@lsum $(TARFILE) $(DISTDIR)/md5sums.txt
diff --git a/src/lzlib/README b/src/lzlib/README
new file mode 100644
index 0000000000000000000000000000000000000000..a3204813cfe968cd54f288bf83de0c7f32dc037f
--- /dev/null
+++ b/src/lzlib/README
@@ -0,0 +1,122 @@
+*************************************************************************
+* Author    : Tiago Dionizio <tiago.dionizio@gmail.com>                 *
+* Library   : lzlib - Lua 5.1 interface to access zlib library functions*
+*                                                                       *
+* Permission is hereby granted, free of charge, to any person obtaining *
+* a copy of this software and associated documentation files (the       *
+* "Software"), to deal in the Software without restriction, including   *
+* without limitation the rights to use, copy, modify, merge, publish,   *
+* distribute, sublicense, and/or sell copies of the Software, and to    *
+* permit persons to whom the Software is furnished to do so, subject to *
+* the following conditions:                                             *
+*                                                                       *
+* The above copyright notice and this permission notice shall be        *
+* included in all copies or substantial portions of the Software.       *
+*                                                                       *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  *
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  *
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     *
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                *
+*************************************************************************
+
+To use this library you need zlib library.
+You can get it from http://www.gzip.org/zlib/
+
+
+Loading the library:
+
+    If you built the library as a loadable package
+        [local] zlib = require 'zlib'
+
+    If you compiled the package statically into your application, call
+    the function "luaopen_zlib(L)". It will create a table with the zlib
+    functions and leave it on the stack.
+
+Definitions:
+
+    ds  = deflate stream
+    is  = inflate stream
+    int = number (integer)
+    *   = (any type)
+
+    [...] represent optional parameters that may be omited or nil, in wich case
+    will be replaced by their default values
+
+-- zlib functions --
+
+string      zlib.version()
+int         zlib.compile_flags()
+int         zlib.adler32(int adler, string buf)
+int         zlib.crc32(int crc, string buf)
+
+string      zlib.compress(string buf [, int level] [, int method] [, int windowBits] [, int memLevel] [, int strategy])
+string      zlib.uncompress(string buf [, int windowBits])
+
+ds          zlib.deflate_init([int level] [, int method] [, int windowBits] [, int memLevel] [, int strategy])
+is          zlib.inflate_init([int windowBits])
+
+-- deflate stream methods --
+
+int         ds:adler()
+int         ds:data_type()
+int         ds:total_in()
+int         ds:total_out()
+int, int    df:process(string [, int flush])
+void        ds:done()
+void        ds:callback(function callback, * userdata)
+int         ds:set_dictionary(string dictionary)
+ds [,int]   ds:clone()
+int         ds:reset()
+int         ds:params(int level, int strategy)
+int         ds:prime(int bits, int value)
+
+-- inflate stream methods --
+
+int         is:adler()
+int         is:data_type()
+int         is:total_in()
+int         is:total_out()
+int, int    if:process(string [, int flush])
+void        if:done()
+void        is:callback([function callback] [, * userdata])
+void        is:dictionary([function callback] [, * userdata])
+int         is:set_dictionary(string dictionary)
+is [,int]   is:clone()
+int         is:reset()
+int, int    is:sync(string buf)
+
+-- callbacks --
+void        callback(string buf, * userdata)
+void        dictionary(* userdata)
+
+-- general description --
+most functions/methods correspond to the original function in the zlib
+library, the main differences are:
+
+when (de)compressing blocks, the generated output is sent to the callback
+function, this is done to prevent passing too much meaningful result values
+from the process method
+
+deflate/inflate zlib functions are interfaced through the process method
+
+ds:params may invoke the output callback
+
+process method returns the error value and the number of input bytes
+processed
+
+clone method returns a copy of the stream (also copies callbacks) and
+returns a new stream object or nil plus an error code
+
+dictionary callback is not strictly necessary, but may avoid extra process
+calls if used, only needed when compressed stream also used a custom
+dictionary. when using it you must call is:set_dictionary as needed, if not
+you will have to watch the return code for zlib.
+
+is:sync returns an error code and the number of input bytes processed
+
+
+** for more information please refer to zlib.h **
+
diff --git a/src/lzlib/README.lgzip b/src/lzlib/README.lgzip
new file mode 100644
index 0000000000000000000000000000000000000000..78390dde68ec9c2a36b8a14d4ccb5148417a43b5
--- /dev/null
+++ b/src/lzlib/README.lgzip
@@ -0,0 +1,136 @@
+*************************************************************************
+* Author    : Tiago Dionizio <tiago.dionizio@gmail.com>                 *
+* Library   : lgzip - a gzip file access binding for Lua 5              *
+*             based on liolib.c from Lua 5.0 library                    *
+*                                                                       *
+* Permission is hereby granted, free of charge, to any person obtaining *
+* a copy of this software and associated documentation files (the       *
+* "Software"), to deal in the Software without restriction, including   *
+* without limitation the rights to use, copy, modify, merge, publish,   *
+* distribute, sublicense, and/or sell copies of the Software, and to    *
+* permit persons to whom the Software is furnished to do so, subject to *
+* the following conditions:                                             *
+*                                                                       *
+* The above copyright notice and this permission notice shall be        *
+* included in all copies or substantial portions of the Software.       *
+*                                                                       *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  *
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  *
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     *
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                *
+*************************************************************************
+
+To use this library you need zlib library.
+You can get it from http://www.gzip.org/zlib/
+
+
+This shows up as a 'addon' to the zlib binding interface.
+
+This is a simple binding to work with gzip files. It is based on the file
+liolib.c included in the original Lua 5.0 distribuition and contains a similar
+interface, of course, duplicated and unrelated features were removed.
+
+
+
+Loading the library:
+
+    If you built the library as a loadable package
+        [local] gzip = require 'gzip'
+
+    If you compiled the package statically into your application, call
+    the function "luaopen_gzip(L)". It will create a table with the zlib
+    functions and leave it on the stack.
+
+
+gzip.open(filename [, mode])
+
+    Opens a file name using "gzopen". Behaviour is identical to the description
+    given in the zlib library. If mode is not given a default mode "rb" will be
+    used. Mode is the same as interpreted by gzopen function, ie, it can
+    include special modes such as characters 1 to 9 that will be treated as the
+    compression level when opening a file for writing.
+
+    It returns a new file handle, or, in case of errors, nil plus an error
+    message
+
+gzip.lines(filename)
+
+    Same behaviour as io.lines in the io standard library provided by lua
+    with the aditional feature of working with gzip files. If a normal text
+    file is read it will read it normaly (normal gzopen behaviour).
+
+gzip.close(file)
+
+    Same as file:close, use file:close instead.
+
+file:flush()
+
+    This function takes no parameters and flushes all output to working file.
+    The same as calling 'gzflush(file, Z_FINISH)' so writing to the file will
+    most likely not work as expected. This is subject to change in the future
+    if there is a strong reason for it to happen.
+
+file:read(format1, ...)
+    Reads the file file, according to the given formats, which specify what
+    to read. For each format, the function returns a string with the characters
+    read, or nil if it cannot read data with the specified format. When called
+    without formats, it uses a default format that reads the entire next line
+    (see below).
+
+    The available formats are
+
+        "*a"   reads the whole file, starting at the current position. On end of
+               file, it returns the empty string.
+        "*l"   reads the next line (skipping the end of line), returning nil on
+               end of file. This is the default format.
+        number reads a string with up to that number of characters, returning
+               nil on end of file. If number is zero, it reads nothing and
+               returns an empty string, or nil on end of file.
+
+    Unlike io.read, the "*n" format will not be available.
+
+
+file:lines()
+
+    Returns an iterator function that, each time it is called, returns a new
+    line from the file. Therefore, the construction
+       for line in file:lines() do ... end
+   will iterate over all lines of the file. (Unlike gzip.lines, this function
+   does not close the file when the loop ends.)
+
+file:seek([whence] [, offset])
+
+    Sets and gets the file position, measured from the beginning of the file,
+    to the position given by offset plus a base specified by the string whence,
+    as follows:
+
+        "set" base is position 0 (beginning of the file);
+        "cur" base is current position;
+
+    In case of success, function seek returns the final file position, measured
+    in bytes from the beginning of the file. If this function fails, it returns
+    nil, plus a string describing the error.
+
+    The default value for whence is "cur", and for offset is 0. Therefore, the
+    call file:seek() returns the current file position, without changing it;
+    the call file:seek("set") sets the position to the beginning of the file
+    (and returns 0); and the call file:seek("end") sets the position to the end
+    of the file, and returns its size.
+
+    This function is subject to limitations imposed by gzseek function from
+    zlib library, such as the inability to use "end" as the base for seeking
+    and the inability to seek backwards when writing.
+
+file:write(value1, ...)
+
+    Writes the value of each of its arguments to the filehandle file. The
+    arguments must be strings or numbers. To write other values, use tostring
+    or string.format before write
+
+file:close()
+
+    Closes the file.
+
diff --git a/src/lzlib/lgzip.c b/src/lzlib/lgzip.c
new file mode 100644
index 0000000000000000000000000000000000000000..e1bcc9bcffc103c2b42e3cb912110811ce8e9871
--- /dev/null
+++ b/src/lzlib/lgzip.c
@@ -0,0 +1,377 @@
+/************************************************************************
+* Author    : Tiago Dionizio <tiago.dionizio@gmail.com>                 *
+* Library   : lgzip - a gzip file access binding for Lua 5              *
+*             based on liolib.c from Lua 5.0 library                    *
+*                                                                       *
+* Permission is hereby granted, free of charge, to any person obtaining *
+* a copy of this software and associated documentation files (the       *
+* "Software"), to deal in the Software without restriction, including   *
+* without limitation the rights to use, copy, modify, merge, publish,   *
+* distribute, sublicense, and/or sell copies of the Software, and to    *
+* permit persons to whom the Software is furnished to do so, subject to *
+* the following conditions:                                             *
+*                                                                       *
+* The above copyright notice and this permission notice shall be        *
+* included in all copies or substantial portions of the Software.       *
+*                                                                       *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  *
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  *
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     *
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                *
+************************************************************************/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <zlib.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+
+
+/*
+** {======================================================
+** FILE Operations
+** =======================================================
+*/
+
+
+#define FILEHANDLE		"zlib:gzFile"
+
+
+static int pushresult (lua_State *L, int i, const char *filename) {
+  if (i) {
+    lua_pushboolean(L, 1);
+    return 1;
+  }
+  else {
+    lua_pushnil(L);
+    if (filename)
+      lua_pushfstring(L, "%s: %s", filename, strerror(errno));
+    else
+      lua_pushfstring(L, "%s", strerror(errno));
+    lua_pushnumber(L, errno);
+    return 3;
+  }
+}
+
+
+static gzFile *topfile (lua_State *L, int findex) {
+  gzFile*f = (gzFile*)luaL_checkudata(L, findex, FILEHANDLE);
+  if (f == NULL) luaL_argerror(L, findex, "bad file");
+  return f;
+}
+
+
+static gzFile tofile (lua_State *L, int findex) {
+  gzFile*f = topfile(L, findex);
+  if (*f == NULL)
+    luaL_error(L, "attempt to use a closed file");
+  return *f;
+}
+
+
+
+/*
+** When creating file handles, always creates a `closed' file handle
+** before opening the actual file; so, if there is a memory error, the
+** file is not left opened.
+*/
+static gzFile *newfile (lua_State *L) {
+  gzFile *pf = (gzFile*)lua_newuserdata(L, sizeof(gzFile));
+  *pf = NULL;  /* file handle is currently `closed' */
+  luaL_getmetatable(L, FILEHANDLE);
+  lua_setmetatable(L, -2);
+  return pf;
+}
+
+
+static int aux_close (lua_State *L) {
+  gzFile f = tofile(L, 1);
+  int ok = (gzclose(f) == Z_OK);
+  if (ok)
+    *(gzFile*)lua_touserdata(L, 1) = NULL;  /* mark file as closed */
+  return ok;
+}
+
+
+static int io_close (lua_State *L) {
+  return pushresult(L, aux_close(L), NULL);
+}
+
+
+static int io_gc (lua_State *L) {
+  gzFile *f = topfile(L, 1);
+  if (*f != NULL)  /* ignore closed files */
+    aux_close(L);
+  return 0;
+}
+
+
+static int io_tostring (lua_State *L) {
+  char buff[128];
+  gzFile *f = topfile(L, 1);
+  if (*f == NULL)
+    strcpy(buff, "closed");
+  else
+    sprintf(buff, "%p", lua_touserdata(L, 1));
+  lua_pushfstring(L, "gzip file (%s)", buff);
+  return 1;
+}
+
+
+static int io_open (lua_State *L) {
+  const char *filename = luaL_checkstring(L, 1);
+  const char *mode = luaL_optstring(L, 2, "rb");
+  gzFile *pf = newfile(L);
+  *pf = gzopen(filename, mode);
+  return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
+}
+
+
+static int io_readline (lua_State *L);
+
+static void aux_lines (lua_State *L, int idx, int close) {
+  lua_pushliteral(L, FILEHANDLE);
+  lua_rawget(L, LUA_REGISTRYINDEX);
+  lua_pushvalue(L, idx);
+  lua_pushboolean(L, close);  /* close/not close file when finished */
+  lua_pushcclosure(L, io_readline, 3);
+}
+
+
+static int f_lines (lua_State *L) {
+  tofile(L, 1);  /* check that it's a valid file handle */
+  aux_lines(L, 1, 0);
+  return 1;
+}
+
+
+static int io_lines (lua_State *L) {
+  const char *filename = luaL_checkstring(L, 1);
+  gzFile *pf = newfile(L);
+  *pf = gzopen(filename, "rb");
+  luaL_argcheck(L, *pf, 1,  strerror(errno));
+  aux_lines(L, lua_gettop(L), 1);
+  return 1;
+}
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+static int test_eof (lua_State *L, gzFile f) {
+  lua_pushlstring(L, NULL, 0);
+  return (gzeof(f) != 1);
+}
+
+
+static int read_line (lua_State *L, gzFile f) {
+  luaL_Buffer b;
+  luaL_buffinit(L, &b);
+  for (;;) {
+    size_t l;
+    char *p = luaL_prepbuffer(&b);
+    if (gzgets(f, p, LUAL_BUFFERSIZE) == NULL) {  /* eof? */
+      luaL_pushresult(&b);  /* close buffer */
+      return (lua_strlen(L, -1) > 0);  /* check whether read something */
+    }
+    l = strlen(p);
+    if (p[l-1] != '\n')
+      luaL_addsize(&b, l);
+    else {
+      luaL_addsize(&b, l - 1);  /* do not include `eol' */
+      luaL_pushresult(&b);  /* close buffer */
+      return 1;  /* read at least an `eol' */
+    }
+  }
+}
+
+
+static int read_chars (lua_State *L, gzFile f, size_t n) {
+  size_t rlen;  /* how much to read */
+  size_t nr;  /* number of chars actually read */
+  luaL_Buffer b;
+  luaL_buffinit(L, &b);
+  rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */
+  do {
+    char *p = luaL_prepbuffer(&b);
+    if (rlen > n) rlen = n;  /* cannot read more than asked */
+    nr = gzread(f, p, rlen);
+    luaL_addsize(&b, nr);
+    n -= nr;  /* still have to read `n' chars */
+  } while (n > 0 && nr == rlen);  /* until end of count or eof */
+  luaL_pushresult(&b);  /* close buffer */
+  return (n == 0 || lua_strlen(L, -1) > 0);
+}
+
+
+static int g_read (lua_State *L, gzFile f, int first) {
+  int nargs = lua_gettop(L) - 1;
+  int success;
+  int n;
+  if (nargs == 0) {  /* no arguments? */
+    success = read_line(L, f);
+    n = first+1;  /* to return 1 result */
+  }
+  else {  /* ensure stack space for all results and for auxlib's buffer */
+    luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
+    success = 1;
+    for (n = first; nargs-- && success; n++) {
+      if (lua_type(L, n) == LUA_TNUMBER) {
+        size_t l = (size_t)lua_tonumber(L, n);
+        success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
+      }
+      else {
+        const char *p = lua_tostring(L, n);
+        luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
+        switch (p[1]) {
+          case 'l':  /* line */
+            success = read_line(L, f);
+            break;
+          case 'a':  /* file */
+            read_chars(L, f, ~((size_t)0));  /* read MAX_SIZE_T chars */
+            success = 1; /* always success */
+            break;
+          default:
+            return luaL_argerror(L, n, "invalid format");
+        }
+      }
+    }
+  }
+  if (!success) {
+    lua_pop(L, 1);  /* remove last result */
+    lua_pushnil(L);  /* push nil instead */
+  }
+  return n - first;
+}
+
+
+static int f_read (lua_State *L) {
+  return g_read(L, tofile(L, 1), 2);
+}
+
+
+static int io_readline (lua_State *L) {
+  gzFile f = *(gzFile*)lua_touserdata(L, lua_upvalueindex(2));
+  if (f == NULL)  /* file is already closed? */
+    luaL_error(L, "file is already closed");
+  if (read_line(L, f)) return 1;
+  else {  /* EOF */
+    if (lua_toboolean(L, lua_upvalueindex(3))) {  /* generator created file? */
+      lua_settop(L, 0);
+      lua_pushvalue(L, lua_upvalueindex(2));
+      aux_close(L);  /* close it */
+    }
+    return 0;
+  }
+}
+
+/* }====================================================== */
+
+
+static int g_write (lua_State *L, gzFile f, int arg) {
+  int nargs = lua_gettop(L) - 1;
+  int status = 1;
+  for (; nargs--; arg++) {
+    if (lua_type(L, arg) == LUA_TNUMBER) {
+      /* optimization: could be done exactly as for strings */
+      status = status &&
+          gzprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
+    }
+    else {
+      size_t l;
+      const char *s = luaL_checklstring(L, arg, &l);
+      status = status && (gzwrite(f, (char*)s, l) == l);
+    }
+  }
+  return pushresult(L, status, NULL);
+}
+
+
+static int f_write (lua_State *L) {
+  return g_write(L, tofile(L, 1), 2);
+}
+
+
+static int f_seek (lua_State *L) {
+  static const int mode[] = {SEEK_SET, SEEK_CUR};
+  static const char *const modenames[] = {"set", "cur", NULL};
+  gzFile f = tofile(L, 1);
+  int op = luaL_checkoption(L, 2, "cur", modenames);
+  long offset = luaL_optlong(L, 3, 0);
+  op = gzseek(f, offset, mode[op]);
+  if (op == -1)
+    return pushresult(L, 0, NULL);  /* error */
+  else {
+    lua_pushnumber(L, op);
+    return 1;
+  }
+}
+
+
+static int f_flush (lua_State *L) {
+  return pushresult(L, gzflush(tofile(L, 1), Z_FINISH) == Z_OK, NULL);
+}
+
+
+/* }====================================================== */
+
+
+LUALIB_API int luaopen_gzip (lua_State *L) {
+  const luaL_reg iolib[] = {
+    {"lines", io_lines},
+    {"close", io_close},
+    {"open", io_open},
+    {NULL, NULL}
+  };
+
+  const luaL_reg flib[] = {
+    {"flush", f_flush},
+    {"read", f_read},
+    {"lines", f_lines},
+    {"seek", f_seek},
+    {"write", f_write},
+    {"close", io_close},
+    {"__gc", io_gc},
+    {"__tostring", io_tostring},
+    {NULL, NULL}
+  };
+
+
+  luaL_newmetatable(L, FILEHANDLE);  /* create new metatable for file handles */
+  /* file methods */
+  lua_pushliteral(L, "__index");
+  lua_pushvalue(L, -2);  /* push metatable */
+  lua_rawset(L, -3);  /* metatable.__index = metatable */
+
+  luaL_register(L, NULL, flib);
+  lua_pop(L, 1);
+
+  lua_newtable(L);
+
+  lua_pushliteral (L, "_COPYRIGHT");
+  lua_pushliteral (L, "Copyright (C) 2003-2008 Tiago Dionizio");
+  lua_settable (L, -3);
+  lua_pushliteral (L, "_DESCRIPTION");
+  lua_pushliteral (L, "Lua 5 interface to access gzip files");
+  lua_settable (L, -3);
+  lua_pushliteral (L, "_VERSION");
+  lua_pushliteral (L, "lgzip 0.3");
+  lua_settable (L, -3);
+
+  luaL_register(L, NULL, iolib);
+
+  lua_pop(L, 1);
+  return 1;
+}
+
diff --git a/src/lzlib/lzlib.c b/src/lzlib/lzlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..20c978962870ba1dc7e93e0d3bd5c1e02948c863
--- /dev/null
+++ b/src/lzlib/lzlib.c
@@ -0,0 +1,592 @@
+/************************************************************************
+* Author    : Tiago Dionizio <tiago.dionizio@gmail.com>                 *
+* Library   : lzlib - Lua 5 interface to access zlib library functions  *
+*                                                                       *
+* Permission is hereby granted, free of charge, to any person obtaining *
+* a copy of this software and associated documentation files (the       *
+* "Software"), to deal in the Software without restriction, including   *
+* without limitation the rights to use, copy, modify, merge, publish,   *
+* distribute, sublicense, and/or sell copies of the Software, and to    *
+* permit persons to whom the Software is furnished to do so, subject to *
+* the following conditions:                                             *
+*                                                                       *
+* The above copyright notice and this permission notice shall be        *
+* included in all copies or substantial portions of the Software.       *
+*                                                                       *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  *
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  *
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     *
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                *
+************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "zlib.h"
+
+
+/*
+** =========================================================================
+** compile time options wich determine available functionality
+** =========================================================================
+*/
+
+
+/*
+** =========================================================================
+** zlib stream metamethods
+** =========================================================================
+*/
+#define ZSTREAMMETA "zlib:zstream"
+
+#define LZANY     (void*)-1
+#define LZNONE    (void*)0
+#define LZDEFLATE (void*)1
+#define LZINFLATE (void*)2
+
+static z_stream *lzstream_new(lua_State *L)
+{
+    z_stream *s = (z_stream*)lua_newuserdata(L, sizeof(z_stream));
+
+    luaL_getmetatable(L, ZSTREAMMETA);
+    lua_setmetatable(L, -2);        /* set metatable */
+
+    s->zalloc = Z_NULL;
+    s->zfree = Z_NULL;
+
+    s->next_out = Z_NULL;
+    s->avail_out = 0;
+    s->next_in = Z_NULL;
+    s->avail_in = 0;
+
+    s->opaque = LZNONE;
+
+    return s;
+}
+
+static void lzstream_cleanup(lua_State *L, z_stream *s)
+{
+    if (s && s->opaque != LZNONE)
+    {
+        if (s->opaque == LZINFLATE)
+            inflateEnd(s);
+        if (s->opaque == LZDEFLATE)
+            deflateEnd(s);
+        s->opaque = LZNONE;
+    }
+}
+
+/* ====================================================================== */
+
+static z_stream *lzstream_get(lua_State *L, int index)
+{
+    z_stream *s = (z_stream*)luaL_checkudata(L, index, ZSTREAMMETA);
+    if (s == NULL) luaL_argerror(L, index, "bad zlib stream");
+    return s;
+}
+
+static z_stream *lzstream_check(lua_State *L, int index, void *state)
+{
+    z_stream *s = lzstream_get(L, index);
+    if ((state != LZANY && s->opaque != state) || s->opaque == LZNONE)
+        luaL_argerror(L, index, "attempt to use invalid zlib stream");
+    return s;
+}
+
+/* ====================================================================== */
+
+static int lzstream_tostring(lua_State *L)
+{
+    char buf[100];
+    z_stream *s = (z_stream*)luaL_checkudata(L, 1, ZSTREAMMETA);
+    if (s == NULL) luaL_argerror(L, 1, "bad zlib stream");
+
+    if (s->opaque == LZNONE)
+        strncpy(buf, "zlib stream (closed)", 100);
+    else if (s->opaque == LZDEFLATE)
+        snprintf(buf, 100, "zlib deflate stream (%p)", (void*)s);
+    else if (s->opaque == LZINFLATE)
+        snprintf(buf, 100, "zlib inflate stream (%p)", (void*)s);
+    else
+        snprintf(buf, 100, "%p", (void*)s);
+
+    lua_pushstring(L, buf);
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzstream_gc(lua_State *L)
+{
+    z_stream *s = lzstream_get(L, 1);
+    lzstream_cleanup(L, s);
+    return 0;
+}
+
+/* ====================================================================== */
+
+static int lzstream_close(lua_State *L)
+{
+    z_stream *s = lzstream_check(L, 1, LZANY);
+    lzstream_cleanup(L, s);
+    return 0;
+}
+
+/* ====================================================================== */
+
+static int lzstream_adler(lua_State *L)
+{
+    z_stream *s = lzstream_check(L, 1, LZANY);
+    lua_pushnumber(L, s->adler);
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzlib_deflate(lua_State *L)
+{
+    int level = luaL_optint(L, 1, Z_DEFAULT_COMPRESSION);
+    int method = luaL_optint(L, 2, Z_DEFLATED);
+    int windowBits = luaL_optint(L, 3, 15);
+    int memLevel = luaL_optint(L, 4, 8);
+    int strategy = luaL_optint(L, 5, Z_DEFAULT_STRATEGY);
+
+    z_stream *s = lzstream_new(L);
+
+    if (deflateInit2(s, level, method, windowBits, memLevel, strategy) != Z_OK)
+    {
+        lua_pushliteral(L, "failed to start decompressing");
+        lua_error(L);
+    }
+    s->opaque = LZDEFLATE;
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzlib_inflate(lua_State *L)
+{
+    int windowBits = luaL_optint(L, 1, 15);
+
+    z_stream *s = lzstream_new(L);
+
+    if (inflateInit2(s, windowBits) != Z_OK)
+    {
+        lua_pushliteral(L, "failed to start compressing");
+        lua_error(L);
+    }
+
+    s->opaque = LZINFLATE;
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzstream_decompress(lua_State *L)
+{
+    int r;
+    luaL_Buffer b;
+
+    z_stream *s = lzstream_check(L, 1, LZINFLATE);
+    s->next_in = (unsigned char*)luaL_checkstring(L, 2);
+    s->avail_in = lua_strlen(L, 2);
+
+    luaL_buffinit(L, &b);
+
+    do {
+        s->next_out = (unsigned char*)luaL_prepbuffer(&b);
+        s->avail_out = LUAL_BUFFERSIZE;
+
+        /* munch some more */
+        r = inflate(s, Z_SYNC_FLUSH);
+        if (r != Z_OK && r != Z_STREAM_END) {
+            lua_pushfstring(L, "failed to decompress [%d]", r);
+            lua_error(L);
+        }
+
+        /* push gathered data */
+        luaL_addsize(&b, LUAL_BUFFERSIZE - s->avail_out);
+
+        /* until we have free space in the output buffer - meaning we are done */
+    } while (s->avail_in > 0 || s->avail_out == 0);
+
+    /* send gathered data if any */
+    luaL_pushresult(&b);
+
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzstream_compress(lua_State *L)
+{
+    int r;
+    luaL_Buffer b;
+
+    z_stream *s = lzstream_check(L, 1, LZDEFLATE);
+    s->next_in = (unsigned char*)luaL_checkstring(L, 2);
+    s->avail_in = lua_strlen(L, 2);
+
+    luaL_buffinit(L, &b);
+
+    do {
+        s->next_out = (unsigned char*)luaL_prepbuffer(&b);
+        s->avail_out = LUAL_BUFFERSIZE;
+
+        /* bake some more */
+        if ((r = deflate(s, Z_NO_FLUSH)) != Z_OK)
+        {
+            lua_pushfstring(L, "failed to compress [%d]", r);
+            lua_error(L);
+        }
+
+        /* push gathered data */
+        luaL_addsize(&b, LUAL_BUFFERSIZE - s->avail_out);
+
+        /* until we have free space in the output buffer - meaning we are done */
+    } while (s->avail_out == 0);
+
+    /* send gathered data if any */
+    luaL_pushresult(&b);
+
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzstream_flush(lua_State *L)
+{
+    z_stream *s = lzstream_check(L, 1, LZANY);
+
+    if (s->opaque == LZINFLATE) {
+        lua_pushliteral(L, "");
+        return 1;
+    }
+
+    s->next_in = (unsigned char *)"";
+    s->avail_in = 0;
+
+    {
+        int r = 0;
+        luaL_Buffer b;
+        luaL_buffinit(L, &b);
+
+        do {
+            s->next_out = (unsigned char *)luaL_prepbuffer(&b);
+            s->avail_out = LUAL_BUFFERSIZE;
+
+            r = deflate(s, Z_FINISH);
+
+            if (r != Z_OK && r != Z_STREAM_END) {
+                lua_pushfstring(L, "failed to flush [%d]", r);
+                lua_error(L);
+            }
+
+            /* push gathered data */
+            luaL_addsize(&b, LUAL_BUFFERSIZE - s->avail_out);
+        } while (r != Z_STREAM_END);
+
+        /* send gathered data if any */
+        luaL_pushresult(&b);
+    }
+    return 1;
+}
+
+/* ====================================================================== */
+
+static int lzstream_reset(lua_State *L)
+{
+    z_stream *s = lzstream_check(L, 1, LZANY);
+
+    if (s->state == LZDEFLATE)
+        lua_pushnumber(L, deflateReset(s));
+    else if (s->opaque == LZINFLATE)
+        lua_pushnumber(L, inflateReset(s));
+    else
+    {
+        lua_pushliteral(L, "invalid zlib stream state");
+        lua_error(L);
+    }
+
+    return 1;
+}
+
+
+/*
+** =========================================================================
+** zlib functions
+** =========================================================================
+*/
+
+static int lzlib_version(lua_State *L)
+{
+    lua_pushstring(L, zlibVersion());
+    return 1;
+}
+
+/* ====================================================================== */
+static int lzlib_adler32(lua_State *L)
+{
+    if (lua_gettop(L) == 0)
+    {
+        /* adler32 initial value */
+        lua_pushnumber(L, adler32(0L, Z_NULL, 0));
+    }
+    else
+    {
+        /* update adler32 checksum */
+        int adler = luaL_checkint(L, 1);
+        const unsigned char* buf = (unsigned char*)luaL_checkstring(L, 2);
+        int len = lua_strlen(L, 2);
+
+        lua_pushnumber(L, adler32(adler, buf, len));
+    }
+    return 1;
+}
+
+/* ====================================================================== */
+static int lzlib_crc32(lua_State *L)
+{
+    if (lua_gettop(L) == 0)
+    {
+        /* crc32 initial value */
+        lua_pushnumber(L, crc32(0L, Z_NULL, 0));
+    }
+    else
+    {
+        /* update crc32 checksum */
+        int crc = luaL_checkint(L, 1);
+        const unsigned char* buf = (unsigned char*)luaL_checkstring(L, 2);
+        int len = lua_strlen(L, 2);
+
+        lua_pushnumber(L, crc32(crc, buf, len));
+    }
+    return 1;
+}
+
+/* ====================================================================== */
+
+
+static int lzlib_compress(lua_State *L)
+{
+    const char *next_in = luaL_checkstring(L, 1);
+    int avail_in = lua_strlen(L, 1);
+    int level = luaL_optint(L, 2, Z_DEFAULT_COMPRESSION);
+    int method = luaL_optint(L, 3, Z_DEFLATED);
+    int windowBits = luaL_optint(L, 4, 15);
+    int memLevel = luaL_optint(L, 5, 8);
+    int strategy = luaL_optint(L, 6, Z_DEFAULT_STRATEGY);
+
+    int ret;
+    luaL_Buffer b;
+    z_stream zs;
+
+    luaL_buffinit(L, &b);
+
+    zs.zalloc = Z_NULL;
+    zs.zfree = Z_NULL;
+
+    zs.next_out = Z_NULL;
+    zs.avail_out = 0;
+    zs.next_in = Z_NULL;
+    zs.avail_in = 0;
+
+    ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy);
+
+    if (ret != Z_OK)
+    {
+        lua_pushnil(L);
+        lua_pushnumber(L, ret);
+        return 2;
+    }
+
+    zs.next_in = (unsigned char*)next_in;
+    zs.avail_in = avail_in;
+
+    for(;;)
+    {
+        zs.next_out = (unsigned char*)luaL_prepbuffer(&b);
+        zs.avail_out = LUAL_BUFFERSIZE;
+
+        /* munch some more */
+        ret = deflate(&zs, Z_FINISH);
+
+        /* push gathered data */
+        luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out);
+
+        /* done processing? */
+        if (ret == Z_STREAM_END)
+            break;
+
+        /* error condition? */
+        if (ret != Z_OK)
+            break;
+    }
+
+    /* cleanup */
+    deflateEnd(&zs);
+
+    luaL_pushresult(&b);
+    lua_pushnumber(L, ret);
+    return 2;
+}
+
+/* ====================================================================== */
+
+static int lzlib_decompress(lua_State *L)
+{
+    const char *next_in = luaL_checkstring(L, 1);
+    int avail_in = lua_strlen(L, 1);
+    int windowBits = luaL_optint(L, 2, 15);
+
+    int ret;
+    luaL_Buffer b;
+    z_stream zs;
+
+    luaL_buffinit(L, &b);
+
+    zs.zalloc = Z_NULL;
+    zs.zfree = Z_NULL;
+
+    zs.next_out = Z_NULL;
+    zs.avail_out = 0;
+    zs.next_in = Z_NULL;
+    zs.avail_in = 0;
+
+    ret = inflateInit2(&zs, windowBits);
+
+    if (ret != Z_OK)
+    {
+        lua_pushnil(L);
+        lua_pushnumber(L, ret);
+        return 2;
+    }
+
+    zs.next_in = (unsigned char*)next_in;
+    zs.avail_in = avail_in;
+
+    for(;;)
+    {
+        zs.next_out = (unsigned char*)luaL_prepbuffer(&b);
+        zs.avail_out = LUAL_BUFFERSIZE;
+
+        /* bake some more */
+        ret = inflate(&zs, Z_FINISH);
+
+        /* push gathered data */
+        luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out);
+
+        /* need dictionary? - no dictionary support here, so just quit */
+        if (ret == Z_NEED_DICT)
+            break;
+
+        /* done processing? */
+        if (ret == Z_STREAM_END)
+            break;
+
+        /* error condition? */
+        if (ret != Z_BUF_ERROR)
+            break;
+    }
+
+    /* cleanup */
+    inflateEnd(&zs);
+
+    luaL_pushresult(&b);
+    lua_pushnumber(L, ret);
+    return 2;
+}
+
+
+/*
+** =========================================================================
+** Register functions
+** =========================================================================
+*/
+
+
+LUALIB_API int luaopen_zlib(lua_State *L)
+{
+    const luaL_reg zstreamm[] =
+    {
+        {"reset",           lzstream_reset      },
+
+        {"compress",        lzstream_compress   },
+        {"decompress",      lzstream_decompress },
+        {"flush",           lzstream_flush      },
+        {"close",           lzstream_close      },
+
+        {"adler",           lzstream_adler      },
+
+        {"__tostring",      lzstream_tostring   },
+        {"__gc",            lzstream_gc         },
+        {NULL, NULL}
+    };
+
+    const luaL_reg zlib[] =
+    {
+        {"version",         lzlib_version       },
+        {"adler32",         lzlib_adler32       },
+        {"crc32",           lzlib_crc32         },
+
+        {"compressobj",     lzlib_deflate       },
+        {"decompressobj",   lzlib_inflate       },
+
+        {"compress",        lzlib_compress      },
+        {"decompress",      lzlib_decompress    },
+
+        {NULL, NULL}
+    };
+
+    /* ====================================================================== */
+
+    /* make sure header and library version are consistent */
+    const char* version = zlibVersion();
+    if (strcmp(version, ZLIB_VERSION))
+    {
+        lua_pushfstring(L, "zlib library version does not match - header: %s, library: %s", ZLIB_VERSION, version);
+        lua_error(L);
+    }
+
+    /* create new metatable for zlib compression structures */
+    luaL_newmetatable(L, ZSTREAMMETA);
+    lua_pushliteral(L, "__index");
+    lua_pushvalue(L, -2);               /* push metatable */
+    lua_rawset(L, -3);                  /* metatable.__index = metatable */
+
+    /*
+    ** Stack: metatable
+    */
+    luaL_register(L, NULL, zstreamm);
+
+    lua_pop(L, 1);                      /* remove metatable from stack */
+
+    /*
+    ** Stack:
+    */
+    lua_newtable(L);
+
+    lua_pushliteral (L, "_COPYRIGHT");
+    lua_pushliteral (L, "Copyright (C) 2003-2008 Tiago Dionizio");
+    lua_settable (L, -3);
+    lua_pushliteral (L, "_DESCRIPTION");
+    lua_pushliteral (L, "Lua 5 interface to access zlib library functions");
+    lua_settable (L, -3);
+    lua_pushliteral (L, "_VERSION");
+    lua_pushliteral (L, "lzlib 0.3");
+    lua_settable (L, -3);
+
+    luaL_register(L, "zlib", zlib);
+
+    lua_pop(L, 2);
+    /*
+    ** Stack: zlib table
+    */
+    return 1;
+}
diff --git a/src/lzlib/test_gzip.lua b/src/lzlib/test_gzip.lua
new file mode 100644
index 0000000000000000000000000000000000000000..bda6c5ca680db495c6486a1ec4c3e92c7452a497
--- /dev/null
+++ b/src/lzlib/test_gzip.lua
@@ -0,0 +1,97 @@
+
+local gzip = require 'gzip'
+
+local function line(header, c)
+    header = header or ''
+    c = c or '-'
+    print(string.rep(string.sub(c, 1, 1), 78 - string.len(header))..header)
+end
+
+
+line(' gzip', '=')
+
+line(' gzip writing')
+local loops = 1000
+local testfile = "test.gz"
+
+local of = gzip.open(testfile, "wb9")
+
+if (not of) then
+    error("Failed to open file test.gz for writing")
+end
+
+for i = 1, loops do
+    of:write(i, "\n")
+end
+
+of:close()
+
+local i = 0
+for l in gzip.lines(testfile) do
+    i = i + 1
+    if (tostring(i) ~= l) then
+        error(tostring(i))
+    end
+end
+
+assert(i == loops)
+print('Ok.')
+line(' gzip reading')
+
+local inf = gzip.open(testfile)
+
+if (not inf) then
+    error("Failed to open file test.gz for reading")
+end
+
+for i = 1, loops do
+    if (tostring(i) ~= inf:read("*l")) then
+        error(tostring(i))
+    end
+end
+
+inf:close()
+
+print('Ok.')
+line(' compress seek')
+
+of = gzip.open(testfile, "wb1")
+
+if (not of) then
+    error("Failed to open file test.gz for writing")
+end
+
+assert(of:seek("cur", 5) == 5)
+assert(of:seek("set", 10) == 10)
+
+of:write("1")
+
+of:close()
+
+print('Ok.')
+line(' uncompress seek')
+
+inf = gzip.open(testfile)
+
+if (not inf) then
+    error("Failed to open file test.gz for reading")
+end
+
+assert(inf:seek("set", 6) == 6)
+assert(inf:seek("set", 4) == 4)
+assert(inf:seek("cur", 1) == 5)
+assert(inf:seek("cur", -1) == 4)
+assert(inf:seek("cur", 1) == 5)
+assert(inf:seek("set", 6) == 6)
+
+inf:read(4)
+
+assert(inf:read(1) == "1")
+
+inf:close()
+
+os.remove(testfile)
+
+print('Ok.')
+
+line(' gzip', '=')
diff --git a/src/lzlib/test_zlib.lua b/src/lzlib/test_zlib.lua
new file mode 100644
index 0000000000000000000000000000000000000000..76a9fa868620ae9aabdb3c8bf3c89cf22eb017cd
--- /dev/null
+++ b/src/lzlib/test_zlib.lua
@@ -0,0 +1,105 @@
+
+local zlib = require 'zlib'
+
+local function line(header, c)
+    header = header or ''
+    c = c or '-'
+    print(string.rep(string.sub(c, 1, 1), 78 - string.len(header))..header)
+end
+
+local function ipart(value)
+    return value - math.mod(value, 1)
+end
+
+local function bitvalues(value, bstart, num)
+    value = ipart(value / 2^bstart)
+    return math.mod(value, 2^num)
+end
+
+line(' zlib '..zlib.version(), '=')
+
+line(' adler32')
+local adler = zlib.adler32()
+print('adler32 init : '..adler)
+adler = zlib.adler32(adler, 'some text')
+print('updated adler: '..adler)
+adler = zlib.adler32(adler, 'some text')
+print('updated adler: '..adler)
+adler = zlib.adler32(adler, 'some text')
+print('updated adler: '..adler)
+adler = zlib.adler32(adler, 'some text')
+print('updated adler: '..adler)
+adler = zlib.adler32(adler, 'some text')
+print('updated adler: '..adler)
+adler = zlib.adler32(adler, 'some textd')
+print('updated adler: '..adler)
+
+line(' crc32')
+local crc = zlib.crc32()
+print('crc32 init : '..crc)
+crc = zlib.crc32(crc, 'some text')
+print('updated crc: '..crc)
+crc = zlib.crc32(crc, 'some text')
+print('updated crc: '..crc)
+crc = zlib.crc32(crc, 'some text')
+print('updated crc: '..crc)
+crc = zlib.crc32(crc, 'some text')
+print('updated crc: '..crc)
+crc = zlib.crc32(crc, 'some text')
+print('updated crc: '..crc)
+crc = zlib.crc32(crc, 'some textd')
+print('updated crc: '..crc)
+
+
+line(' deflate/inflate')
+local us
+f = io.open('lzlib.c') -- f = io.open('../all.tar')
+us = f:read('*a')
+f:close()
+
+do -- local block
+
+local f, cs, zd, zi, aux_res, res, ret, count
+
+print('file length              : '..string.len(us))
+
+cs = ''
+zd = zlib.compressobj(1)
+print('deflate stream           : '..tostring(zd))
+
+cs = cs .. zd:compress(string.sub(us, 1, string.len(us)/2))
+cs = cs .. zd:compress(string.sub(us, string.len(us)/2+1))
+cs = cs .. zd:flush()
+
+print('compressed length        : '..string.len(cs))
+print('compressed adler         : '..tostring(zd:adler()))
+zd:close()
+
+zi = zlib.decompressobj()
+print('inflate stream           : '..tostring(zi))
+res = ''
+res = res .. zi:decompress(string.sub(cs, 1, 10))
+res = res .. zi:decompress(string.sub(cs, 11))
+res = res .. zi:flush()
+print('uncompressed length      : '..string.len(res))
+print('uncompressed adler       : '..tostring(zi:adler()))
+zi:close()
+print('result == uncompressed   : '..tostring(res == us))
+print('compression ratio        : '..tostring(string.len(us)/string.len(cs)))
+
+end -- local block
+collectgarbage()
+
+line(' compress/uncompress')
+do -- local block
+local cs, res
+print('file length              : '..string.len(us))
+cs = zlib.compress(us,1)
+print('compressed length        : '..string.len(cs))
+res = zlib.decompress(cs)
+print('uncompressed length      : '..string.len(res))
+print('result == uncompressed   : '..tostring(res == us))
+print('compression ratio        : '..tostring(string.len(us)/string.len(cs)))
+end -- local block
+
+line(' zlib '..zlib.version(), '=')
diff --git a/src/main.c b/src/main.c
index c4216b61e4d2784279d72602b84416b788570124..62940a0bcfd3dd04610ce26f07c8ae0797ea02ce 100644
--- a/src/main.c
+++ b/src/main.c
@@ -707,6 +707,7 @@ void boot_lua(int state, bool rebooting, int argc, char *argv[])
 		luaopen_shaders(L);
 		luaopen_serial(L);
 		luaopen_profile(L);
+		luaopen_zlib(L);
 
 		// Override "print" if requested
 		if (no_debug)