From 22bcc3e2d5614326f507b05f1d9c3a75b7a10a40 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Mon, 14 Sep 2015 22:17:30 +0200 Subject: [PATCH] =?UTF-8?q?Added=20the=20Tiago=20Dionizio's=20"lzlib",=20a?= =?UTF-8?q?nd=20modified=20it=20to=20be=20usable=20with=20ctr=C2=B5Lua.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accessible with require("ctr.fs.lzlib"). I just added the file "lzlib.c" in the source/ directory, because it's much simpler to build. https://github.com/LuaDist/lzlib for details and documentation. --- source/fs.c | 17 +- source/ir.c | 62 ++- source/lzlib.c | 997 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1072 insertions(+), 4 deletions(-) create mode 100644 source/lzlib.c diff --git a/source/fs.c b/source/fs.c index a568cb7..940ef58 100644 --- a/source/fs.c +++ b/source/fs.c @@ -10,6 +10,8 @@ Handle *fsuHandle; FS_archive sdmcArchive; +void load_lzlib(lua_State *L); + static int fs_list(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -99,9 +101,20 @@ static const struct luaL_Reg fs_lib[] = { { NULL, NULL } }; +// submodules +struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } fs_libs[] = { + {"zip", load_lzlib, NULL}, + {NULL, NULL} +}; + int luaopen_fs_lib(lua_State *L) { luaL_newlib(L, fs_lib); - + + for (int i = 0; fs_libs[i].name; i++) { + fs_libs[i].load(L); + lua_setfield(L, -2, fs_libs[i].name); + } + return 1; } @@ -121,4 +134,4 @@ void unload_fs_lib(lua_State *L) { FSUSER_CloseArchive(fsuHandle, &sdmcArchive); fsExit(); -} \ No newline at end of file +} diff --git a/source/ir.c b/source/ir.c index 14a1ec7..40cd490 100644 --- a/source/ir.c +++ b/source/ir.c @@ -1,3 +1,8 @@ +/*** +The `ir` module. +@module ctr.ir +@usage local ir = require("ctr.ir") +*/ #include <3ds/types.h> #include <3ds/services/ir.h> #include <3ds/linear.h> @@ -8,8 +13,35 @@ u32 bufferSize = 0; u32 *buffer; +/*** +Bitrate codes list (this is not a part of the module, just a reference) +@table bitrates +@field 3 115200 +@field 4 96000 +@field 5 72000 +@field 6 48000 (default) +@field 7 36000 +@field 8 24000 +@field 9 18000 +@field 10 12000 +@field 11 9600 +@field 12 6000 +@field 13 3000 +@field 14 57600 +@field 15 38400 +@field 16 19200 +@field 17 7200 +@field 18 4800 +*/ + +/*** +Initialize the IR module. +@function init +@tparam[opt=6] number bitrate bitrate of the IR module (more informations below) +@tparam[opt=2048] number buffer size of the buffer, in bytes (max 2048) +*/ static int ir_init(lua_State *L) { - u8 bitrate = luaL_checkinteger(L, 1); + u8 bitrate = luaL_optinteger(L, 1, 6); bufferSize = luaL_optinteger(L, 2, 2048); //default: 2Kio buffer = linearAlloc(bufferSize); @@ -25,12 +57,22 @@ static int ir_init(lua_State *L) { return 1; } +/*** +Disable the IR module. +@function shutdown +*/ static int ir_shutdown(lua_State *L) { IRU_Shutdown(); return 0; } +/*** +Send some data over the IR module. +@function send +@tparam string data just some data +@tparam[opt=false] boolean wait set to `true` to wait until the data is sent. +*/ static int ir_send(lua_State *L) { u8 *data = (u8*)luaL_checkstring(L, 1); u32 wait = lua_toboolean(L, 2); @@ -40,8 +82,14 @@ static int ir_send(lua_State *L) { return 0; } +/*** +Receive some data from the IR module. +@function receive +@tparam[opt=buffer size] number size bytes to receive +@tparam[opt=false] boolean wait wait until the data is received +*/ static int ir_receive(lua_State *L) { - u32 size = luaL_optinteger(L, 1, bufferSize); + u32 size = luaL_optinteger(L, 1, 0x800); u32 wait = lua_toboolean(L, 2); u8 *data = 0; u32 *transfercount = 0; @@ -53,6 +101,11 @@ static int ir_receive(lua_State *L) { return 1; } +/*** +Set the bitrate of the communication. +@function setBitRate +@tparam number bitrate new bitrate for the communication +*/ static int ir_setBitRate(lua_State *L) { u8 bitrate = luaL_checkinteger(L, 1); @@ -61,6 +114,11 @@ static int ir_setBitRate(lua_State *L) { return 0; } +/*** +Return the actual bitrate of the communication. +@function getBitRate +@treturn number actual bitrate +*/ static int ir_getBitRate(lua_State *L) { u8 bitrate = 0; diff --git a/source/lzlib.c b/source/lzlib.c new file mode 100644 index 0000000..b73dee1 --- /dev/null +++ b/source/lzlib.c @@ -0,0 +1,997 @@ +/************************************************************************ +* Author : Tiago Dionizio * +* 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. * +************************************************************************/ + +/* +Library modified in order to work with ctrµLua. +The outdated Lua API functions will be replaced if needed. +*/ + +#include +#include + +#include <3ds/types.h> + +#include "lua.h" +#include "lauxlib.h" + +#include "zlib.h" + +/* +** ========================================================================= +** compile time options wich determine available functionality +** ========================================================================= +*/ + +/* TODO + +- also call flush on table/userdata when flush function is detected +- remove io_cb check inflate_block if condition +- only set eos when ZSTREAM_END is reached +- check for stream errors to close stream when really needed + +*/ + + +/* +** ========================================================================= +** zlib stream metamethods +** ========================================================================= +*/ +#define ZSTREAMMETA "zlib:zstream" + +#define LZ_ANY -1 +#define LZ_NONE 0 +#define LZ_DEFLATE 1 +#define LZ_INFLATE 2 + +#if 0 + #define LZ_BUFFER_SIZE LUAL_BUFFERSIZE +#else + #define LZ_BUFFER_SIZE 8192 +#endif + +typedef struct { + /* zlib structures */ + z_stream zstream; + /* stream state. LZ_DEFLATE | LZ_INFLATE */ + int state; + int error; + int peek; + int eos; + /* user callback source for reading/writing */ + int io_cb; + /* input buffer */ + int i_buffer_ref; + size_t i_buffer_pos; + size_t i_buffer_len; + const char *i_buffer; + /* output buffer */ + size_t o_buffer_len; + size_t o_buffer_max; + char o_buffer[LZ_BUFFER_SIZE]; + /* dictionary */ + const Bytef *dictionary; + size_t dictionary_len; +} lz_stream; + + +/* forward declarations */ +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush); + + +static lz_stream *lzstream_new(lua_State *L, int src) { + lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream)); + + luaL_getmetatable(L, ZSTREAMMETA); + lua_setmetatable(L, -2); /* set metatable */ + + s->state = LZ_NONE; + s->error = Z_OK; + s->eos = 0; + s->io_cb = LUA_REFNIL; + + s->i_buffer = NULL; + s->i_buffer_ref = LUA_REFNIL; + s->i_buffer_pos = 0; + s->i_buffer_len = 0; + + s->peek = 0; + s->o_buffer_len = 0; + s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]); + + s->zstream.zalloc = Z_NULL; + s->zstream.zfree = Z_NULL; + + /* prepare source */ + if (lua_isstring(L, src)) { + lua_pushvalue(L, src); + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len); + } else { + /* table | function | userdata */ + lua_pushvalue(L, src); + s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX); + } + return s; +} + +static void lzstream_cleanup(lua_State *L, lz_stream *s) { + if (s && s->state != LZ_NONE) { + if (s->state == LZ_INFLATE) { + inflateEnd(&s->zstream); + } + if (s->state == LZ_DEFLATE) { + deflateEnd(&s->zstream); + } + + luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb); + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->state = LZ_NONE; + } +} + +/* ====================================================================== */ + +static lz_stream *lzstream_get(lua_State *L, int index) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, index, "bad zlib stream"); + return s; +} + +static lz_stream *lzstream_check(lua_State *L, int index, int state) { + lz_stream *s = lzstream_get(L, index); + if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) { + luaL_argerror(L, index, "attempt to use invalid zlib stream"); + } + return s; +} + +/* ====================================================================== */ + +static int lzstream_tostring(lua_State *L) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, 1, "bad zlib stream"); + + if (s->state == LZ_NONE) { + lua_pushstring(L, "zlib stream (closed)"); + } else if (s->state == LZ_DEFLATE) { + lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s); + } else if (s->state == LZ_INFLATE) { + lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s); + } else { + lua_pushfstring(L, "%p", (void*)s); + } + + return 1; +} + +/* ====================================================================== */ + +static int lzstream_gc(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + lzstream_cleanup(L, s); + return 0; +} + +/* ====================================================================== */ + +static int lzstream_close(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + + if (s->state == LZ_DEFLATE) { + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, Z_FINISH); + } + + lzstream_cleanup(L, s); + lua_pushboolean(L, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_adler(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_ANY); + lua_pushnumber(L, s->zstream.adler); + return 1; +} + +/* ====================================================================== */ + +/* + zlib.deflate( + sink: function | { write: function [, close: function, flush: function] }, + compression level, [Z_DEFAILT_COMPRESSION] + method, [Z_DEFLATED] + windowBits, [15] + memLevel, [8] + strategy, [Z_DEFAULT_STRATEGY] + dictionary: [""] + ) +*/ +static int lzlib_deflate(lua_State *L) { + int level, method, windowBits, memLevel, strategy; + lz_stream *s; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :write function? */ + lua_getfield(L, 1, "write"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "output parameter does not provide :write function"); + } + lua_pop(L, 1); + } + else if (!lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "output parameter must be a function, table or userdata value"); + } + + level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + windowBits = (int) luaL_optinteger(L, 4, 15); + memLevel = (int) luaL_optinteger(L, 5, 8); + strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) { + lua_pushliteral(L, "call to deflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to deflateSetDictionnary failed"); + lua_error(L); + } + } + + s->state = LZ_DEFLATE; + return 1; +} + +/* + zlib.inflate( + source: string | function | { read: function, close: function }, + windowBits: number, [15] + dictionary: [""] + ) +*/ +static int lzlib_inflate(lua_State *L) +{ + int windowBits; + lz_stream *s; + int have_peek = 0; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :read function? */ + lua_getfield(L, 1, "read"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "input parameter does not provide :read function"); + } + lua_pop(L, 1); + /* check for peek function */ + lua_getfield(L, 1, "peek"); + have_peek = lua_isfunction(L, -1); + lua_pop(L, 1); + } + else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value"); + } + + windowBits = (int) luaL_optinteger(L, 2, 15); + dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (windowBits > 0 && windowBits < 16) { + windowBits |= 32; + } + + if (inflateInit2(&s->zstream, windowBits) != Z_OK) { + lua_pushliteral(L, "call to inflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + s->dictionary = (const Bytef *) dictionary; + s->dictionary_len = dictionary_len; + } + + s->peek = have_peek; + s->state = LZ_INFLATE; + return 1; +} + +/* ====================================================================== */ + +static int lz_pushresult (lua_State *L, lz_stream *s) { + if (s->error == Z_OK) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, zError(s->error)); + lua_pushinteger(L, s->error); + return 3; + } +} + +/* + Get block to process: + - top of stack gets +*/ +static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) { + if (s->i_buffer_pos >= s->i_buffer_len) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isnil(L, -1)) { + if (lua_isfunction(L, -1)) { + lua_pushinteger(L, hint); + lua_call(L, 1, 1); + } else { + lua_getfield(L, -1, (s->peek ? "peek" : "read")); + lua_insert(L, -2); + lua_pushinteger(L, hint); + lua_call(L, 2, 1); + } + + if (lua_isstring(L, -1)) { + s->i_buffer_pos = 0; + s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len); + if (s->i_buffer_len > 0) { + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } + } else if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } else { + lua_pushliteral(L, "deflate callback must return string or nil"); + lua_error(L); + } + } else { + lua_pop(L, 1); + } + } + + return s->i_buffer; +} + +static int lzstream_inflate_block(lua_State *L, lz_stream *s) { + if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) { + int r; + + if (s->i_buffer_len == s->i_buffer_pos) { + s->zstream.next_in = NULL; + s->zstream.avail_in = 0; + } else { + s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos); + s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos; + } + + s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len; + s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len; + + /* munch some more */ + r = inflate(&s->zstream, Z_SYNC_FLUSH); + + if (r == Z_NEED_DICT) { + if (s->dictionary == NULL) { + lua_pushliteral(L, "no inflate dictionary provided"); + lua_error(L); + } + + if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to inflateSetDictionnary failed"); + lua_error(L); + } + + r = inflate(&s->zstream, Z_SYNC_FLUSH); + } + + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + s->error = r; + #if 1 + lua_pushfstring(L, "failed to decompress [%d]", r); + lua_error(L); + #endif + } + + if (r == Z_STREAM_END) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + s->eos = 1; + } + + /* number of processed bytes */ + if (s->peek) { + size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + lua_getfield(L, -1, "read"); + lua_insert(L, -2); + lua_pushinteger(L, processed); + lua_call(L, 2, 0); + } + + s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in; + s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out; + } + + return s->o_buffer_len; +} + +/* +** Remove n bytes from the output buffer. +*/ +static void lzstream_remove(lz_stream *s, size_t n) { + memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n); + s->o_buffer_len -= n; +} + +/* +** Copy at most n bytes to buffer b and remove them from the +** output stream buffer. +*/ +static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) { + /* check output */ + if (n > s->o_buffer_len) { + n = s->o_buffer_len; + } + + if (n > 0) { + lua_pushlstring(L, s->o_buffer, n); + luaL_addvalue(b); + + lzstream_remove(s, n); + } + + return n; +} + +/* + z:read( + {number | '*l' | '*a'}* + ) +*/ +static int lz_test_eof(lua_State *L, lz_stream *s) { + lua_pushlstring(L, NULL, 0); + if (s->o_buffer_len > 0) { + return 1; + } else if (s->eos) { + return 0; + } else { + return lzstream_inflate_block(L, s); + } +} + +static int lz_read_line(lua_State *L, lz_stream *s) { + luaL_Buffer b; + size_t l = 0, n; + + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + char *p = s->o_buffer; + size_t len = s->o_buffer_len; + + /* find newline in output buffer */ + for (n = 0; n < len; ++n, ++p) { + if (*p == '\n' || *p == '\r') { + int eat_nl = *p == '\r'; + luaL_addlstring(&b, s->o_buffer, n); + lzstream_remove(s, n+1); + l += n; + + if (eat_nl && lzstream_inflate_block(L, s)) { + if (s->o_buffer_len > 0 && *s->o_buffer == '\n') { + lzstream_remove(s, 1); + } + } + + luaL_pushresult(&b); + return 1; + } + } + + if (len > 0) { + luaL_addlstring(&b, s->o_buffer, len); + lzstream_remove(s, len); + l += len; + } + } while (lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + return l > 0 || !s->eos || s->o_buffer_len > 0; +} + + +static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) { + size_t len; + luaL_Buffer b; + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + size_t rlen = lzstream_flush_buffer(L, s, n, &b); + n -= rlen; + } while (n > 0 && lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + lua_tolstring(L, -1, &len); + return n == 0 || len > 0; +} + +static int lzstream_decompress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_INFLATE); + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = lz_read_line(L, s); + n = 3; /* 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 = 2; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, 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 = lz_read_line(L, s); + break; + case 'a': /* file */ + lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - 2; +} + + +static int lzstream_readline(lua_State *L) { + lz_stream *s; + int sucess; + + s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE); + sucess = lz_read_line(L, s); + + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + + if (sucess) { + return 1; + } else { + /* EOF */ + return 0; + } +} + +static int lzstream_lines(lua_State *L) { + lzstream_check(L, 1, LZ_INFLATE); + lua_settop(L, 1); + lua_pushcclosure(L, lzstream_readline, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) { + int r, arg; + int self = 0; + size_t b_size = s->o_buffer_max; + unsigned char *b = (unsigned char *)s->o_buffer; + + /* number of processed bytes */ + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isfunction(L, -1)) { + self = 1; + lua_getfield(L, -1, "write"); + } + + for (arg = from; arg <= to; arg++) { + s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in); + + do { + s->zstream.next_out = b; + s->zstream.avail_out = b_size; + + /* bake some more */ + r = deflate(&s->zstream, flush); + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + lua_pushboolean(L, 0); + lua_pushfstring(L, "failed to compress [%d]", r); + return 2; + } + + if (s->zstream.avail_out != b_size) { + /* write output */ + lua_pushvalue(L, -1); /* function */ + if (self) lua_pushvalue(L, -3); /* self */ + lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */ + lua_call(L, (self ? 2 : 1), 0); + } + + if (r == Z_STREAM_END) { + lzstream_cleanup(L, s); + break; + } + + /* process all input */ + } while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0); + } + + lua_pushboolean(L, 1); + return 1; +} + +static int lzstream_compress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH); +} + + +/* ====================================================================== */ + +static int lzstream_flush(lua_State *L) { + static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; + static const char *const flush_opts[] = { "sync", "full", "finish" }; + + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts); + + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, flush_values[flush]); +} + +/* +** ========================================================================= +** 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 */ + size_t len; + int adler = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + 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 */ + size_t len; + int crc = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, crc32(crc, buf, len)); + } + return 1; +} + +/* ====================================================================== */ + + +static int lzlib_compress(lua_State *L) { + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + int method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + int windowBits = (int) luaL_optinteger(L, 4, 15); + int memLevel = (int) luaL_optinteger(L, 5, 8); + int strategy = (int) luaL_optinteger(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) +{ + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int windowBits = (int) luaL_optinteger(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_pushliteral(L, "failed to initialize zstream structures"); + lua_error(L); + } + + 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); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + /* cleanup */ + inflateEnd(&zs); + + lua_pushliteral(L, "failed to process zlib stream"); + lua_error(L); + } + } + + /* cleanup */ + inflateEnd(&zs); + + luaL_pushresult(&b); + return 1; +} + + +/* +** ========================================================================= +** Register functions +** ========================================================================= +*/ + +#if (LUA_VERSION_NUM >= 502) + +#define luaL_register(L,n,f) luaL_setfuncs(L,f,0) + +#endif + +LUALIB_API int luaopen_zlib(lua_State *L) +{ + const luaL_Reg lzstream_meta[] = + { + {"write", lzstream_compress }, + {"read", lzstream_decompress }, + {"lines", lzstream_lines }, + {"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 }, + + {"deflate", lzlib_deflate }, + {"inflate", lzlib_inflate }, + + {"compress", lzlib_compress }, + {"decompress", lzlib_decompress }, + + {NULL, NULL} + }; + + /* ====================================================================== */ + + /* 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, lzstream_meta); + + lua_pop(L, 1); /* remove metatable from stack */ + + /* + ** Stack: + */ + lua_newtable(L); + + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2010 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.4-work3"); + lua_settable (L, -3); + +#define PUSH_LITERAL(name) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, Z_##name); \ + lua_settable (L, -3); + +#define PUSH_NUMBER(name, value) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, value); \ + lua_settable (L, -3); + + PUSH_LITERAL(NO_COMPRESSION) + PUSH_LITERAL(BEST_SPEED) + PUSH_LITERAL(BEST_COMPRESSION) + PUSH_LITERAL(DEFAULT_COMPRESSION) + + PUSH_LITERAL(FILTERED) + PUSH_LITERAL(HUFFMAN_ONLY) + PUSH_LITERAL(RLE) + PUSH_LITERAL(FIXED) + PUSH_LITERAL(DEFAULT_STRATEGY) + + PUSH_NUMBER(MINIMUM_MEMLEVEL, 1) + PUSH_NUMBER(MAXIMUM_MEMLEVEL, 9) + PUSH_NUMBER(DEFAULT_MEMLEVEL, 8) + + PUSH_NUMBER(DEFAULT_WINDOWBITS, 15) + PUSH_NUMBER(MINIMUM_WINDOWBITS, 8) + PUSH_NUMBER(MAXIMUM_WINDOWBITS, 15) + + PUSH_NUMBER(GZIP_WINDOWBITS, 16) + PUSH_NUMBER(RAW_WINDOWBITS, -1) + + luaL_register(L, NULL, zlib); + + /* + ** Stack: zlib table + */ + return 1; +} + +void load_lzlib(lua_State *L) { + luaL_requiref(L, "ctr.fs.lzlib", luaopen_zlib, false); +}