zzlib.lua 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. -- zzlib - zlib decompression in Lua - Implementation-independent code
  2. -- Copyright (c) 2016-2020 Francois Galea <fgalea at free.fr>
  3. -- This program is free software. It comes without any warranty, to
  4. -- the extent permitted by applicable law. You can redistribute it
  5. -- and/or modify it under the terms of the Do What The Fuck You Want
  6. -- To Public License, Version 2, as published by Sam Hocevar. See
  7. -- the COPYING file or http://www.wtfpl.net/ for more details.
  8. local unpack = table.unpack or unpack
  9. local infl
  10. --[[
  11. local lua_version = tonumber(_VERSION:match("^Lua (.*)"))
  12. if not lua_version or lua_version < 5.3 then
  13. -- older version of Lua or Luajit being used - use bit/bit32-based implementation
  14. infl = require("inflate-bit32")
  15. else
  16. -- From Lua 5.3, use implementation based on bitwise operators
  17. infl = require("inflate-bwo")
  18. end
  19. ]]
  20. infl = require("inflate-bit32")
  21. -- infl = require("inflate-bwo")
  22. local zzlib = {}
  23. local function arraytostr(array)
  24. local tmp = {}
  25. local size = #array
  26. local pos = 1
  27. local imax = 1
  28. while size > 0 do
  29. local bsize = size >= 2048 and 2048 or size
  30. local s = string.char(unpack(array, pos, pos + bsize - 1))
  31. pos = pos + bsize
  32. size = size - bsize
  33. local i = 1
  34. while tmp[i] do
  35. s = tmp[i] .. s
  36. tmp[i] = nil
  37. i = i + 1
  38. end
  39. if i > imax then imax = i end
  40. tmp[i] = s
  41. end
  42. local str = ""
  43. for i = 1, imax do if tmp[i] then str = tmp[i] .. str end end
  44. return str
  45. end
  46. local function inflate_gzip(bs)
  47. local id1, id2, cm, flg = bs.buf:byte(1, 4)
  48. if id1 ~= 31 or id2 ~= 139 then error("invalid gzip header") end
  49. if cm ~= 8 then error("only deflate format is supported") end
  50. bs.pos = 11
  51. if infl.band(flg, 4) ~= 0 then
  52. local xl1, xl2 = bs.buf.byte(bs.pos, bs.pos + 1)
  53. local xlen = xl2 * 256 + xl1
  54. bs.pos = bs.pos + xlen + 2
  55. end
  56. if infl.band(flg, 8) ~= 0 then
  57. local pos = bs.buf:find("\0", bs.pos)
  58. bs.pos = pos + 1
  59. end
  60. if infl.band(flg, 16) ~= 0 then
  61. local pos = bs.buf:find("\0", bs.pos)
  62. bs.pos = pos + 1
  63. end
  64. if infl.band(flg, 2) ~= 0 then
  65. -- TODO: check header CRC16
  66. bs.pos = bs.pos + 2
  67. end
  68. local result = arraytostr(infl.main(bs))
  69. local crc = bs:getb(8) + 256 *
  70. (bs:getb(8) + 256 * (bs:getb(8) + 256 * bs:getb(8)))
  71. bs:close()
  72. if crc ~= infl.crc32(result) then error("checksum verification failed") end
  73. return result
  74. end
  75. -- compute Adler-32 checksum
  76. local function adler32(s)
  77. local s1 = 1
  78. local s2 = 0
  79. for i = 1, #s do
  80. local c = s:byte(i)
  81. s1 = (s1 + c) % 65521
  82. s2 = (s2 + s1) % 65521
  83. end
  84. return s2 * 65536 + s1
  85. end
  86. local function inflate_zlib(bs)
  87. local cmf = bs.buf:byte(1)
  88. local flg = bs.buf:byte(2)
  89. if (cmf * 256 + flg) % 31 ~= 0 then
  90. error("zlib header check bits are incorrect")
  91. end
  92. if infl.band(cmf, 15) ~= 8 then error("only deflate format is supported") end
  93. if infl.rshift(cmf, 4) ~= 7 then error("unsupported window size") end
  94. if infl.band(flg, 32) ~= 0 then
  95. error("preset dictionary not implemented")
  96. end
  97. bs.pos = 3
  98. local result = arraytostr(infl.main(bs))
  99. local adler = ((bs:getb(8) * 256 + bs:getb(8)) * 256 + bs:getb(8)) * 256 +
  100. bs:getb(8)
  101. bs:close()
  102. if adler ~= adler32(result) then error("checksum verification failed") end
  103. return result
  104. end
  105. function zzlib.gunzipf(filename)
  106. local file, err = io.open(filename, "rb")
  107. if not file then return nil, err end
  108. return inflate_gzip(infl.bitstream_init(file))
  109. end
  110. function zzlib.gunzip(str) return inflate_gzip(infl.bitstream_init(str)) end
  111. function zzlib.inflate(str) return inflate_zlib(infl.bitstream_init(str)) end
  112. local function int2le(str, pos)
  113. local a, b = str:byte(pos, pos + 1)
  114. return b * 256 + a
  115. end
  116. local function int4le(str, pos)
  117. local a, b, c, d = str:byte(pos, pos + 3)
  118. return ((d * 256 + c) * 256 + b) * 256 + a
  119. end
  120. function zzlib.unzip(buf, filename)
  121. local p = #buf - 21
  122. local quit = false
  123. if int4le(buf, p) ~= 0x06054b50 then
  124. -- not sure there is a reliable way to locate the end of central directory record
  125. -- if it has a variable sized comment field
  126. error(".ZIP file comments not supported")
  127. end
  128. local cdoffset = int4le(buf, p + 16)
  129. local nfiles = int2le(buf, p + 10)
  130. p = cdoffset + 1
  131. for i = 1, nfiles do
  132. if int4le(buf, p) ~= 0x02014b50 then
  133. error("invalid central directory header signature")
  134. end
  135. local flag = int2le(buf, p + 8)
  136. local method = int2le(buf, p + 10)
  137. local crc = int4le(buf, p + 16)
  138. local namelen = int2le(buf, p + 28)
  139. local name = buf:sub(p + 46, p + 45 + namelen)
  140. if name == filename then
  141. local headoffset = int4le(buf, p + 42)
  142. p = 1 + headoffset
  143. if int4le(buf, p) ~= 0x04034b50 then
  144. error("invalid local header signature")
  145. end
  146. local csize = int4le(buf, p + 18)
  147. local extlen = int2le(buf, p + 28)
  148. p = p + 30 + namelen + extlen
  149. if method == 0 then
  150. -- no compression
  151. result = buf:sub(p, p + csize - 1)
  152. else
  153. -- DEFLATE compression
  154. local bs = infl.bitstream_init(buf)
  155. bs.pos = p
  156. result = arraytostr(infl.main(bs))
  157. end
  158. if crc ~= infl.crc32(result) then
  159. error("checksum verification failed")
  160. end
  161. return result
  162. end
  163. p = p + 46 + namelen + int2le(buf, p + 30) + int2le(buf, p + 32)
  164. end
  165. error("file '" .. filename .. "' not found in ZIP archive")
  166. end
  167. return zzlib