]> git.lizzy.rs Git - Crafter.git/commitdiff
Push extreme prototype of automatically downloaded skins
authoroilboi <47129783+oilboi@users.noreply.github.com>
Wed, 27 May 2020 00:14:17 +0000 (20:14 -0400)
committeroilboi <47129783+oilboi@users.noreply.github.com>
Wed, 27 May 2020 00:14:17 +0000 (20:14 -0400)
minetest.conf
mods/skins/init.lua [new file with mode: 0644]
mods/skins/png_lua/.DS_Store [new file with mode: 0644]
mods/skins/png_lua/README.md [new file with mode: 0644]
mods/skins/png_lua/deflatelua.lua [new file with mode: 0644]
mods/skins/png_lua/png.lua [new file with mode: 0644]
mods/skins/textures/blank_skin.png [new file with mode: 0644]
mods/skins/textures/p.png [new file with mode: 0644]

index 1e6b677f1b39fad97fce15b39d5ca6b3e9b35c8d..e4874740b3aa425698d68e002e6525bbbb9bf974 100644 (file)
@@ -5,4 +5,6 @@ show_entity_selectionbox = true
 enable_client_modding = true
 console_alpha = 157
 csm_restriction_flags = 0
-enable_mod_channels = true
\ No newline at end of file
+enable_mod_channels = true
+secure.http_mods = skins
+secure.trusted_mods = skins
\ No newline at end of file
diff --git a/mods/skins/init.lua b/mods/skins/init.lua
new file mode 100644 (file)
index 0000000..2ae8c72
--- /dev/null
@@ -0,0 +1,184 @@
+local security = minetest.request_insecure_environment()
+
+local path = minetest.get_modpath(minetest.get_current_modname())
+
+local pngimage = dofile(path.."/png_lua/png.lua")
+
+--run through all the skins on the skindex and index them all locally
+--only try to index further than the point in the current list max
+
+local http = minetest.request_http_api()
+local id = "Lua Skins Updater"
+-- Binary downloads are required
+if not core.features.httpfetch_binary_data then
+       print("outdated version of MINETEST detected!")
+end
+
+if not http or not security then
+    for i = 1,5 do
+        print("!WARNING!")
+    end
+    print("---------------------------------------------------------------")
+    print("HTTP access is required. Please add this to your minetest.conf:")
+    print("secure.http_mods = skins")
+    print("secure.trusted_mods = skins")
+    print("!!Skins will not work without this!!")
+    print("---------------------------------------------------------------")
+    return(nil)
+end
+
+
+-- http://minetest.fensta.bplaced.net/api/apidoku.md
+local root_url = "http://minetest.fensta.bplaced.net"
+local page_url = root_url .. "/api/v2/get.json.php?getlist&page=%i&outformat=base64" -- [1] = Page#
+local preview_url = root_url .. "/skins/1/%i.png" -- [1] = ID
+
+-- Fancy debug wrapper to download an URL
+local function fetch_url(url, callback)
+       http.fetch({
+        url = url,
+        timeout = 3,
+    }, function(result)
+        print(dump(result))
+        if result.succeeded then
+            
+                       --if result.code ~= 200 then
+                               --core.log("warning", ("%s: STATUS=%i URL=%s"):format(
+                               --      _ID_, result.code, url))
+                       --end
+                       return callback(result.data)
+               end
+               core.log("warning", ("%s: Failed to download URL=%s"):format(
+                       id, url))
+       end)
+end
+
+local function unsafe_file_write(path, contents)
+       local f = security.io.open(path, "wb")
+       f:write(contents)
+       f:close()
+end
+
+--https://gist.github.com/marceloCodget/3862929 rgb to hex
+
+local function rgbToHex(rgb)
+       local hexadecimal = '0X'
+
+       for key, value in pairs(rgb) do
+               local hex = ''
+
+               while(value > 0)do
+                       local index = math.fmod(value, 16) + 1
+                       value = math.floor(value / 16)
+                       hex = string.sub('0123456789ABCDEF', index, index) .. hex                       
+               end
+
+               if(string.len(hex) == 0)then
+                       hex = '00'
+
+               elseif(string.len(hex) == 1)then
+                       hex = '0' .. hex
+               end
+
+               hexadecimal = hexadecimal .. hex
+       end
+
+       return hexadecimal
+end
+
+local xmax = 64
+local ymax = 32
+local function file_to_texture(image)
+    local x = 1
+    local y = 1
+    --local base_texture = "[combine:"..xmax.."x"..ymax
+    local base_texture = "[combine:" .. xmax .. "x" .. ymax
+    --local base_texture2 = "[combine:"..xmax.."x"..ymax
+    for _,line in pairs(image.pixels) do
+        for _,data in pairs(line) do
+            if x <= 32 or y > 16 then
+                local hex = rgbToHex({data.R,data.G,data.B}):sub(3)
+                base_texture = base_texture .. (":%s,%s=%s"):format(x - 1, y - 1, "(p.png\\^[colorize\\:#" .. hex .. ")")
+            --else
+            --    print(dump(data))
+            end
+            x = x + 1
+            if x > xmax then
+                x = 1
+                y = y + 1
+            end
+            if y > ymax then
+                break
+            end
+        end
+    end
+    return(base_texture)
+end
+
+-- Function to fetch a range of pages
+fetch_function = function()
+    fetch_url("https://raw.githubusercontent.com/oilboi/crafter_skindex/master/skin.png", function(data)
+        if data then
+            unsafe_file_write(path.."/skin_temp/temp.png", data)
+
+            local img = pngimage(minetest.get_modpath("skins").."/skin_temp/temp.png", nil, false, false)
+            if img then
+                local stored_texture = file_to_texture(img)
+
+                --print("===============================================================")
+                --print(stored_texture)
+                if stored_texture then
+                    --print(dump(stored_texture))
+                    minetest.get_player_by_name("singleplayer"):set_properties({textures = {stored_texture}})
+                    --[[
+                    minetest.get_player_by_name("singleplayer"):hud_add(
+                        {
+                            hud_elem_type = "image",  -- See HUD element types
+                            -- Type of element, can be "image", "text", "statbar", or "inventory"
+                    
+                            position = {x=0.5, y=0.5},
+                            -- Left corner position of element
+                    
+                            name = "<name>",
+                    
+                            scale = {x = 2, y = 2},
+                    
+                            text = stored_texture,
+                    
+                            text2 = "<text>",
+                    
+                            number = 2,
+                    
+                            item = 3,
+                            -- Selected item in inventory. 0 for no item selected.
+                    
+                            direction = 0,
+                            -- Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
+                    
+                            alignment = {x=0, y=0},
+                    
+                            offset = {x=0, y=0},
+                    
+                            size = { x=100, y=100 },
+                            -- Size of element in pixels
+                    
+                            z_index = 0,
+                            -- Z index : lower z-index HUDs are displayed behind higher z-index HUDs
+                        }
+                    )
+                    ]]--
+                end
+            end
+
+        end
+    end)
+end
+
+--local img = pngimage(minetest.get_modpath("skins").."/skin_temp/temp.png", nil, false, false)
+--print(dump(img))
+
+minetest.register_on_joinplayer(function(player)
+    minetest.after(4,function()
+        fetch_function()
+    end)
+end)
\ No newline at end of file
diff --git a/mods/skins/png_lua/.DS_Store b/mods/skins/png_lua/.DS_Store
new file mode 100644 (file)
index 0000000..6a90dba
Binary files /dev/null and b/mods/skins/png_lua/.DS_Store differ
diff --git a/mods/skins/png_lua/README.md b/mods/skins/png_lua/README.md
new file mode 100644 (file)
index 0000000..0c6c0b9
--- /dev/null
@@ -0,0 +1,61 @@
+PNGLua
+======
+
+A pure lua implementation of a PNG decoder
+
+Usage
+-----
+
+To initialize a new png image:
+
+    img = pngImage(<path to image>, newRowCallback, verbose, memSave)
+    
+The argument "verbose" should be a boolean. If true, it will print messages while decoding. The argument "memSave" should also be a boolean. If true, it will not save pixel data after it has been decoded (you must use the pixel data passed to the newRowCallback to deal with image data).The available data from the image is as follows:
+
+```
+img.width = 0
+img.height = 0
+img.depth = 0
+img.colorType = 0
+img.pixels = { 
+                1: { 
+                    1: { R: ..., G: ..., B: ..., A: ...}, 
+                    2: { R: ..., G: ..., B: ..., A: ...}, 
+                    ...
+                },
+                2: { 
+                    1: { R: ..., G: ..., B: ..., A: ...}, 
+                    2: { R: ..., G: ..., B: ..., A: ...}, 
+                    ...
+                }    
+                ...            
+            }
+
+```
+
+The newRowCallback argument should have the following structure:
+
+    newRowCallback(rowNum, rowTotal, rowPixels)
+
+"rowNum" refers to the current row, "rowTotal" refers to the total number of rows in the image, and "rowPixels" refers to the table of pixels in the current row.
+
+Support
+-------
+
+The supported colortypes are as follows:
+
+-    Grayscale
+-    Truecolor
+-    Indexed
+-    Greyscale/alpha
+-    Truecolor/alpha
+
+So far the module only supports 256 Colors in png-8, png-24 as well as png-32 files. and no ancillary chunks.
+
+More than 256 colors might be supported (Bit-depths over 8) as long as they align with whole bytes. These have not been tested.
+
+Multiple IDAT chunks of arbitrary lengths are supported. Filter type 0 is currently the only supported filter type.
+
+Errors
+-------
+So far no error-checking has been implemented. No crc32 checks are done.
diff --git a/mods/skins/png_lua/deflatelua.lua b/mods/skins/png_lua/deflatelua.lua
new file mode 100644 (file)
index 0000000..5261bc5
--- /dev/null
@@ -0,0 +1,857 @@
+--[[
+
+LUA MODULE
+
+compress.deflatelua - deflate (and gunzip/zlib) implemented in Lua.
+
+SYNOPSIS
+local DEFLATE = require 'compress.deflatelua'
+-- uncompress gzip file
+local fh = assert(io.open'foo.txt.gz', 'rb')
+local ofh = assert(io.open'foo.txt', 'wb')
+DEFLATE.gunzip {input=fh, output=ofh}
+fh:close(); ofh:close()
+-- can also uncompress from string including zlib and raw DEFLATE formats.
+DESCRIPTION
+This is a pure Lua implementation of decompressing the DEFLATE format,
+including the related zlib and gzip formats.
+Note: This library only supports decompression.
+Compression is not currently implemented.
+
+API
+
+Note: in the following functions, input stream `fh` may be
+a file handle, string, or an iterator function that returns strings.
+Output stream `ofh` may be a file handle or a function that
+consumes one byte (number 0..255) per call.
+
+DEFLATE.inflate {input=fh, output=ofh}
+
+Decompresses input stream `fh` in the DEFLATE format
+while writing to output stream `ofh`.
+DEFLATE is detailed in http://tools.ietf.org/html/rfc1951 .
+DEFLATE.gunzip {input=fh, output=ofh, disable_crc=disable_crc}
+Decompresses input stream `fh` with the gzip format
+while writing to output stream `ofh`.
+`disable_crc` (defaults to `false`) will disable CRC-32 checking
+to increase speed.
+gzip is detailed in http://tools.ietf.org/html/rfc1952 .
+
+DEFLATE.inflate_zlib {input=fh, output=ofh, disable_crc=disable_crc}
+Decompresses input stream `fh` with the zlib format
+while writing to output stream `ofh`.
+`disable_crc` (defaults to `false`) will disable CRC-32 checking
+to increase speed.
+zlib is detailed in http://tools.ietf.org/html/rfc1950 .
+
+DEFLATE.adler32(byte, crc) --> rcrc
+Returns adler32 checksum of byte `byte` (number 0..255) appended
+to string with adler32 checksum `crc`. This is internally used by
+`inflate_zlib`.
+ADLER32 in detailed in http://tools.ietf.org/html/rfc1950 .
+
+COMMAND LINE UTILITY
+
+A `gunziplua` command line utility (in folder `bin`) is also provided.
+This mimicks the *nix `gunzip` utility but is a pure Lua implementation
+that invokes this library. For help do
+gunziplua -h
+DEPENDENCIES
+
+Requires 'digest.crc32lua' (used for optional CRC-32 checksum checks).
+https://github.com/davidm/lua-digest-crc32lua
+
+Will use a bit library ('bit', 'bit32', 'bit.numberlua') if available. This
+is not that critical for this library but is required by digest.crc32lua.
+
+'pythonic.optparse' is only required by the optional `gunziplua`
+command-line utilty for command line parsing.
+https://github.com/davidm/lua-pythonic-optparse
+
+INSTALLATION
+
+Copy the `compress` directory into your LUA_PATH.
+REFERENCES
+
+[1] DEFLATE Compressed Data Format Specification version 1.3
+http://tools.ietf.org/html/rfc1951
+[2] GZIP file format specification version 4.3
+http://tools.ietf.org/html/rfc1952
+[3] http://en.wikipedia.org/wiki/DEFLATE
+[4] pyflate, by Paul Sladen
+http://www.paul.sladen.org/projects/pyflate/
+[5] Compress::Zlib::Perl - partial pure Perl implementation of
+Compress::Zlib
+http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm
+
+LICENSE
+
+(c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
+
+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.
+(end license)
+--]]
+
+
+local M = {_TYPE='module', _NAME='compress.deflatelua', _VERSION='0.3.20111128'}
+
+local assert = assert
+local error = error
+local ipairs = ipairs
+local pairs = pairs
+local print = print
+local require = require
+local tostring = tostring
+local type = type
+local setmetatable = setmetatable
+local io = io
+local math = math
+local table_sort = table.sort
+local math_max = math.max
+local string_char = string.char
+
+--[[
+Requires the first module listed that exists, else raises like `require`.
+If a non-string is encountered, it is returned.
+Second return value is module name loaded (or '').
+--]]
+local function requireany(...)
+  local errs = {}
+  for i = 1, select('#', ...) do local name = select(i, ...)
+    if type(name) ~= 'string' then return name, '' end
+    local ok, mod = pcall(require, name)
+    if ok then return mod, name end
+    errs[#errs+1] = mod
+  end
+  error(table.concat(errs, '\n'), 2)
+end
+
+
+--local crc32 = require "digest.crc32lua" . crc32_byte
+local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua', nil)
+
+local DEBUG = false
+
+-- Whether to use `bit` library functions in current module.
+-- Unlike the crc32 library, it doesn't make much difference in this module.
+local NATIVE_BITOPS = (bit ~= nil)
+
+local band, lshift, rshift
+if NATIVE_BITOPS then
+  band = bit.band
+  lshift = bit.lshift
+  rshift = bit.rshift
+end
+
+
+local function warn(s)
+  io.stderr:write(s, '\n')
+end
+
+
+local function debug(...)
+  print('DEBUG', ...)
+end
+
+
+local function runtime_error(s, level)
+  level = level or 1
+  error({s}, level+1)
+end
+
+
+local function make_outstate(outbs)
+  local outstate = {}
+  outstate.outbs = outbs
+  outstate.window = {}
+  outstate.window_pos = 1
+  return outstate
+end
+
+
+local function output(outstate, byte)
+  -- debug('OUTPUT:', s)
+  local window_pos = outstate.window_pos
+  outstate.outbs(byte)
+  outstate.window[window_pos] = byte
+  outstate.window_pos = window_pos % 32768 + 1 -- 32K
+end
+
+
+local function noeof(val)
+  return assert(val, 'unexpected end of file')
+end
+
+
+local function hasbit(bits, bit)
+  return bits % (bit + bit) >= bit
+end
+
+
+local function memoize(f)
+  local mt = {}
+  local t = setmetatable({}, mt)
+  function mt:__index(k)
+    local v = f(k)
+    t[k] = v
+    return v
+  end
+  return t
+end
+
+
+-- small optimization (lookup table for powers of 2)
+local pow2 = memoize(function(n) return 2^n end)
+
+--local tbits = memoize(
+-- function(bits)
+-- return memoize( function(bit) return getbit(bits, bit) end )
+-- end )
+
+
+-- weak metatable marking objects as bitstream type
+local is_bitstream = setmetatable({}, {__mode='k'})
+
+
+-- DEBUG
+-- prints LSB first
+--[[
+local function bits_tostring(bits, nbits)
+local s = ''
+local tmp = bits
+local function f()
+local b = tmp % 2 == 1 and 1 or 0
+s = s .. b
+tmp = (tmp - b) / 2
+end
+if nbits then
+for i=1,nbits do f() end
+else
+while tmp ~= 0 do f() end
+end
+
+return s
+end
+--]]
+
+local function bytestream_from_file(fh)
+  local o = {}
+  function o:read()
+    local sb = fh:read(1)
+    if sb then return sb:byte() end
+  end
+  return o
+end
+
+
+local function bytestream_from_string(s)
+  local i = 1
+  local o = {}
+  function o:read()
+    local by
+    if i <= #s then
+      by = s:byte(i)
+      i = i + 1
+    end
+    return by
+  end
+  return o
+end
+
+
+local function bytestream_from_function(f)
+  local i = 0
+  local buffer = ''
+  local o = {}
+  function o:read()
+    i = i + 1
+    if i > #buffer then
+      buffer = f()
+      if not buffer then return end
+      i = 1
+    end
+    return buffer:byte(i,i)
+  end
+  return o
+end
+
+
+local function bitstream_from_bytestream(bys)
+  local buf_byte = 0
+  local buf_nbit = 0
+  local o = {}
+
+  function o:nbits_left_in_byte()
+    return buf_nbit
+  end
+
+  if NATIVE_BITOPS then
+    function o:read(nbits)
+      nbits = nbits or 1
+      while buf_nbit < nbits do
+        local byte = bys:read()
+        if not byte then return end -- note: more calls also return nil
+        buf_byte = buf_byte + lshift(byte, buf_nbit)
+        buf_nbit = buf_nbit + 8
+      end
+      local bits
+      if nbits == 0 then
+        bits = 0
+      elseif nbits == 32 then
+        bits = buf_byte
+        buf_byte = 0
+      else
+        bits = band(buf_byte, rshift(0xffffffff, 32 - nbits))
+        buf_byte = rshift(buf_byte, nbits)
+      end
+      buf_nbit = buf_nbit - nbits
+      return bits
+    end
+  else
+    function o:read(nbits)
+      nbits = nbits or 1
+      while buf_nbit < nbits do
+        local byte = bys:read()
+        if not byte then return end -- note: more calls also return nil
+        buf_byte = buf_byte + pow2[buf_nbit] * byte
+        buf_nbit = buf_nbit + 8
+      end
+      local m = pow2[nbits]
+      local bits = buf_byte % m
+      buf_byte = (buf_byte - bits) / m
+      buf_nbit = buf_nbit - nbits
+      return bits
+    end
+  end
+  
+  is_bitstream[o] = true
+
+  return o
+end
+
+
+local function get_bitstream(o)
+  local bs
+  if is_bitstream[o] then
+    return o
+  elseif io.type(o) == 'file' then
+    bs = bitstream_from_bytestream(bytestream_from_file(o))
+  elseif type(o) == 'string' then
+    bs = bitstream_from_bytestream(bytestream_from_string(o))
+  elseif type(o) == 'function' then
+    bs = bitstream_from_bytestream(bytestream_from_function(o))
+  else
+    runtime_error 'unrecognized type'
+  end
+  return bs
+end
+
+
+local function get_obytestream(o)
+  local bs
+  if io.type(o) == 'file' then
+    bs = function(sbyte) o:write(string_char(sbyte)) end
+  elseif type(o) == 'function' then
+    bs = o
+  else
+    runtime_error('unrecognized type: ' .. tostring(o))
+  end
+  return bs
+end
+
+
+local function HuffmanTable(init, is_full)
+  local t = {}
+  if is_full then
+    for val,nbits in pairs(init) do
+      if nbits ~= 0 then
+        t[#t+1] = {val=val, nbits=nbits}
+        --debug('*',val,nbits)
+      end
+    end
+  else
+    for i=1,#init-2,2 do
+      local firstval, nbits, nextval = init[i], init[i+1], init[i+2]
+      --debug(val, nextval, nbits)
+      if nbits ~= 0 then
+        for val=firstval,nextval-1 do
+          t[#t+1] = {val=val, nbits=nbits}
+        end
+      end
+    end
+  end
+  table_sort(t, function(a,b)
+    return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits
+  end)
+
+  -- assign codes
+  local code = 1 -- leading 1 marker
+  local nbits = 0
+  for i,s in ipairs(t) do
+    if s.nbits ~= nbits then
+      code = code * pow2[s.nbits - nbits]
+      nbits = s.nbits
+    end
+    s.code = code
+    --debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(code))
+    code = code + 1
+  end
+
+  local minbits = math.huge
+  local look = {}
+  for i,s in ipairs(t) do
+    minbits = math.min(minbits, s.nbits)
+    look[s.code] = s.val
+  end
+
+  --for _,o in ipairs(t) do
+  -- debug(':', o.nbits, o.val)
+  --end
+
+  -- function t:lookup(bits) return look[bits] end
+
+  local msb = NATIVE_BITOPS and function(bits, nbits)
+    local res = 0
+    for i=1,nbits do
+      res = lshift(res, 1) + band(bits, 1)
+      bits = rshift(bits, 1)
+    end
+    return res
+  end or function(bits, nbits)
+    local res = 0
+    for i=1,nbits do
+      local b = bits % 2
+      bits = (bits - b) / 2
+      res = res * 2 + b
+    end
+    return res
+  end
+  
+  local tfirstcode = memoize(
+    function(bits) return pow2[minbits] + msb(bits, minbits) end)
+
+  function t:read(bs)
+    local code = 1 -- leading 1 marker
+    local nbits = 0
+    while 1 do
+      if nbits == 0 then -- small optimization (optional)
+        code = tfirstcode[noeof(bs:read(minbits))]
+        nbits = nbits + minbits
+      else
+        local b = noeof(bs:read())
+        nbits = nbits + 1
+        code = code * 2 + b -- MSB first
+        --[[NATIVE_BITOPS
+code = lshift(code, 1) + b -- MSB first
+--]]
+      end
+      --debug('code?', code, bits_tostring(code))
+      local val = look[code]
+      if val then
+        --debug('FOUND', val)
+        return val
+      end
+    end
+  end
+
+  return t
+end
+
+
+local function parse_gzip_header(bs)
+  -- local FLG_FTEXT = 2^0
+  local FLG_FHCRC = 2^1
+  local FLG_FEXTRA = 2^2
+  local FLG_FNAME = 2^3
+  local FLG_FCOMMENT = 2^4
+
+  local id1 = bs:read(8)
+  local id2 = bs:read(8)
+  if id1 ~= 31 or id2 ~= 139 then
+    runtime_error 'not in gzip format'
+  end
+  local cm = bs:read(8) -- compression method
+  local flg = bs:read(8) -- FLaGs
+  local mtime = bs:read(32) -- Modification TIME
+  local xfl = bs:read(8) -- eXtra FLags
+  local os = bs:read(8) -- Operating System
+
+  if DEBUG then
+    debug("CM=", cm)
+    debug("FLG=", flg)
+    debug("MTIME=", mtime)
+    -- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable
+    debug("XFL=", xfl)
+    debug("OS=", os)
+  end
+
+  if not os then runtime_error 'invalid header' end
+
+  if hasbit(flg, FLG_FEXTRA) then
+    local xlen = bs:read(16)
+    local extra = 0
+    for i=1,xlen do
+      extra = bs:read(8)
+    end
+    if not extra then runtime_error 'invalid header' end
+  end
+
+  local function parse_zstring(bs)
+    repeat
+      local by = bs:read(8)
+      if not by then runtime_error 'invalid header' end
+    until by == 0
+  end
+
+  if hasbit(flg, FLG_FNAME) then
+    parse_zstring(bs)
+  end
+
+  if hasbit(flg, FLG_FCOMMENT) then
+    parse_zstring(bs)
+  end
+
+  if hasbit(flg, FLG_FHCRC) then
+    local crc16 = bs:read(16)
+    if not crc16 then runtime_error 'invalid header' end
+    -- IMPROVE: check CRC. where is an example .gz file that
+    -- has this set?
+    if DEBUG then
+      debug("CRC16=", crc16)
+    end
+  end
+end
+
+local function parse_zlib_header(bs)
+  local cm = bs:read(4) -- Compression Method
+  local cinfo = bs:read(4) -- Compression info
+  local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG)
+  local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary)
+  local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level)
+  local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags)
+  local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs
+  
+  if cm ~= 8 then -- not "deflate"
+    runtime_error("unrecognized zlib compression method: " .. cm)
+  end
+  if cinfo > 7 then
+    runtime_error("invalid zlib window size: cinfo=" .. cinfo)
+  end
+  local window_size = 2^(cinfo + 8)
+  
+  if (cmf*256 + flg) % 31 ~= 0 then
+    runtime_error("invalid zlib header (bad fcheck sum)")
+  end
+  
+  if fdict == 1 then
+    runtime_error("FIX:TODO - FDICT not currently implemented")
+    local dictid_ = bs:read(32)
+  end
+  
+  return window_size
+end
+
+local function parse_huffmantables(bs)
+    local hlit = bs:read(5) -- # of literal/length codes - 257
+    local hdist = bs:read(5) -- # of distance codes - 1
+    local hclen = noeof(bs:read(4)) -- # of code length codes - 4
+
+    local ncodelen_codes = hclen + 4
+    local codelen_init = {}
+    local codelen_vals = {
+      16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
+    for i=1,ncodelen_codes do
+      local nbits = bs:read(3)
+      local val = codelen_vals[i]
+      codelen_init[val] = nbits
+    end
+    local codelentable = HuffmanTable(codelen_init, true)
+
+    local function decode(ncodes)
+      local init = {}
+      local nbits
+      local val = 0
+      while val < ncodes do
+        local codelen = codelentable:read(bs)
+        --FIX:check nil?
+        local nrepeat
+        if codelen <= 15 then
+          nrepeat = 1
+          nbits = codelen
+          --debug('w', nbits)
+        elseif codelen == 16 then
+          nrepeat = 3 + noeof(bs:read(2))
+          -- nbits unchanged
+        elseif codelen == 17 then
+          nrepeat = 3 + noeof(bs:read(3))
+          nbits = 0
+        elseif codelen == 18 then
+          nrepeat = 11 + noeof(bs:read(7))
+          nbits = 0
+        else
+          error 'ASSERT'
+        end
+        for i=1,nrepeat do
+          init[val] = nbits
+          val = val + 1
+        end
+      end
+      local huffmantable = HuffmanTable(init, true)
+      return huffmantable
+    end
+
+    local nlit_codes = hlit + 257
+    local ndist_codes = hdist + 1
+
+    local littable = decode(nlit_codes)
+    local disttable = decode(ndist_codes)
+
+    return littable, disttable
+end
+
+
+local tdecode_len_base
+local tdecode_len_nextrabits
+local tdecode_dist_base
+local tdecode_dist_nextrabits
+local function parse_compressed_item(bs, outstate, littable, disttable)
+  local val = littable:read(bs)
+  --debug(val, val < 256 and string_char(val))
+  if val < 256 then -- literal
+    output(outstate, val)
+  elseif val == 256 then -- end of block
+    return true
+  else
+    if not tdecode_len_base then
+      local t = {[257]=3}
+      local skip = 1
+      for i=258,285,4 do
+        for j=i,i+3 do t[j] = t[j-1] + skip end
+        if i ~= 258 then skip = skip * 2 end
+      end
+      t[285] = 258
+      tdecode_len_base = t
+      --for i=257,285 do debug('T1',i,t[i]) end
+    end
+    if not tdecode_len_nextrabits then
+      local t = {}
+      if NATIVE_BITOPS then
+        for i=257,285 do
+          local j = math_max(i - 261, 0)
+          t[i] = rshift(j, 2)
+        end
+      else
+        for i=257,285 do
+          local j = math_max(i - 261, 0)
+          t[i] = (j - (j % 4)) / 4
+        end
+      end
+      t[285] = 0
+      tdecode_len_nextrabits = t
+      --for i=257,285 do debug('T2',i,t[i]) end
+    end
+    local len_base = tdecode_len_base[val]
+    local nextrabits = tdecode_len_nextrabits[val]
+    local extrabits = bs:read(nextrabits)
+    local len = len_base + extrabits
+
+    if not tdecode_dist_base then
+      local t = {[0]=1}
+      local skip = 1
+      for i=1,29,2 do
+        for j=i,i+1 do t[j] = t[j-1] + skip end
+        if i ~= 1 then skip = skip * 2 end
+      end
+      tdecode_dist_base = t
+      --for i=0,29 do debug('T3',i,t[i]) end
+    end
+    if not tdecode_dist_nextrabits then
+      local t = {}
+      if NATIVE_BITOPS then
+        for i=0,29 do
+          local j = math_max(i - 2, 0)
+          t[i] = rshift(j, 1)
+        end
+      else
+        for i=0,29 do
+          local j = math_max(i - 2, 0)
+          t[i] = (j - (j % 2)) / 2
+        end
+      end
+      tdecode_dist_nextrabits = t
+      --for i=0,29 do debug('T4',i,t[i]) end
+    end
+    local dist_val = disttable:read(bs)
+    local dist_base = tdecode_dist_base[dist_val]
+    local dist_nextrabits = tdecode_dist_nextrabits[dist_val]
+    local dist_extrabits = bs:read(dist_nextrabits)
+    local dist = dist_base + dist_extrabits
+
+    --debug('BACK', len, dist)
+    for i=1,len do
+      local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K
+      output(outstate, assert(outstate.window[pos], 'invalid distance'))
+    end
+  end
+  return false
+end
+
+
+local function parse_block(bs, outstate)
+  local bfinal = bs:read(1)
+  local btype = bs:read(2)
+
+  local BTYPE_NO_COMPRESSION = 0
+  local BTYPE_FIXED_HUFFMAN = 1
+  local BTYPE_DYNAMIC_HUFFMAN = 2
+  local BTYPE_RESERVED_ = 3
+
+  if DEBUG then
+    debug('bfinal=', bfinal)
+    debug('btype=', btype)
+  end
+
+  if btype == BTYPE_NO_COMPRESSION then
+    bs:read(bs:nbits_left_in_byte())
+    local len = bs:read(16)
+    local nlen_ = noeof(bs:read(16))
+
+    for i=1,len do
+      local by = noeof(bs:read(8))
+      output(outstate, by)
+    end
+  elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then
+    local littable, disttable
+    if btype == BTYPE_DYNAMIC_HUFFMAN then
+      littable, disttable = parse_huffmantables(bs)
+    else
+      littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil}
+      disttable = HuffmanTable {0,5, 32,nil}
+    end
+
+    repeat
+      local is_done = parse_compressed_item(
+        bs, outstate, littable, disttable)
+    until is_done
+  else
+    runtime_error 'unrecognized compression type'
+  end
+
+  return bfinal ~= 0
+end
+
+
+function M.inflate(t)
+  local bs = get_bitstream(t.input)
+  local outbs = get_obytestream(t.output)
+  local outstate = make_outstate(outbs)
+
+  repeat
+    local is_final = parse_block(bs, outstate)
+  until is_final
+end
+local inflate = M.inflate
+
+
+function M.gunzip(t)
+  local bs = get_bitstream(t.input)
+  local outbs = get_obytestream(t.output)
+  local disable_crc = t.disable_crc
+  if disable_crc == nil then disable_crc = false end
+
+  parse_gzip_header(bs)
+
+  local data_crc32 = 0
+
+  inflate{input=bs, output=
+    disable_crc and outbs or
+      function(byte)
+        data_crc32 = crc32(byte, data_crc32)
+        outbs(byte)
+      end
+  }
+
+  bs:read(bs:nbits_left_in_byte())
+
+  local expected_crc32 = bs:read(32)
+  local isize = bs:read(32) -- ignored
+  if DEBUG then
+    debug('crc32=', expected_crc32)
+    debug('isize=', isize)
+  end
+  if not disable_crc and data_crc32 then
+    if data_crc32 ~= expected_crc32 then
+      runtime_error('invalid compressed data--crc error')
+    end
+  end
+  if bs:read() then
+    warn 'trailing garbage ignored'
+  end
+end
+
+
+function M.adler32(byte, crc)
+  local s1 = crc % 65536
+  local s2 = (crc - s1) / 65536
+  s1 = (s1 + byte) % 65521
+  s2 = (s2 + s1) % 65521
+  return s2*65536 + s1
+end -- 65521 is the largest prime smaller than 2^16
+
+
+function M.inflate_zlib(t)
+  local bs = get_bitstream(t.input)
+  local outbs = get_obytestream(t.output)
+  local disable_crc = t.disable_crc
+  if disable_crc == nil then disable_crc = false end
+  
+  local window_size_ = parse_zlib_header(bs)
+  
+  local data_adler32 = 1
+  
+  inflate{input=bs, output=
+    disable_crc and outbs or
+      function(byte)
+        data_adler32 = M.adler32(byte, data_adler32)
+        outbs(byte)
+      end
+  }
+
+  bs:read(bs:nbits_left_in_byte())
+  
+  local b3 = bs:read(8)
+  local b2 = bs:read(8)
+  local b1 = bs:read(8)
+  local b0 = bs:read(8)
+  local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0
+  if DEBUG then
+    debug('alder32=', expected_adler32)
+  end
+  if not disable_crc then
+    if data_adler32 ~= expected_adler32 then
+      runtime_error('invalid compressed data--crc error')
+    end
+  end
+  if bs:read() then
+    warn 'trailing garbage ignored'
+  end
+end
+
+
+return M
\ No newline at end of file
diff --git a/mods/skins/png_lua/png.lua b/mods/skins/png_lua/png.lua
new file mode 100644 (file)
index 0000000..963e086
--- /dev/null
@@ -0,0 +1,308 @@
+-- The MIT License (MIT)
+
+-- Copyright (c) 2013 DelusionalLogic
+
+-- 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.
+local path = minetest.get_modpath(minetest.get_current_modname())
+
+local deflate = dofile(path.."/png_lua/deflatelua.lua")
+
+local requiredDeflateVersion = "0.3.20111128"
+
+if (deflate._VERSION ~= requiredDeflateVersion) then
+    error("Incorrect deflate version: must be "..requiredDeflateVersion..", not "..deflate._VERSION)
+end
+
+local function bsRight(num, pow)
+    return math.floor(num / 2^pow)
+end
+
+local function bsLeft(num, pow)
+    return math.floor(num * 2^pow)
+end
+
+local function bytesToNum(bytes)
+    local n = 0
+    for k,v in ipairs(bytes) do
+        n = bsLeft(n, 8) + v
+    end
+    if (n > 2147483647) then
+        return (n - 4294967296)
+    else
+        return n
+    end
+    n = (n > 2147483647) and (n - 4294967296) or n
+    return n
+end
+
+local function readInt(stream, bps)
+    local bytes = {}
+    bps = bps or 4
+    for i=1,bps do
+        bytes[i] = stream:read(1):byte()
+    end
+    return bytesToNum(bytes)
+end
+
+local function readChar(stream, num)
+    num = num or 1
+    return stream:read(num)
+end
+
+local function readByte(stream)
+    return stream:read(1):byte()
+end
+
+local function getDataIHDR(stream, length)
+    local data = {}
+    data["width"] = readInt(stream)
+    data["height"] = readInt(stream)
+    data["bitDepth"] = readByte(stream)
+    data["colorType"] = readByte(stream)
+    data["compression"] = readByte(stream)
+    data["filter"] = readByte(stream)
+    data["interlace"] = readByte(stream)
+    return data
+end
+
+local function getDataIDAT(stream, length, oldData)
+    local data = {}
+    if (oldData == nil) then
+        data.data = readChar(stream, length)
+    else
+        data.data = oldData.data .. readChar(stream, length)
+    end
+    return data
+end
+
+local function getDataPLTE(stream, length)
+    local data = {}
+    data["numColors"] = math.floor(length/3)
+    data["colors"] = {}
+    for i = 1, data["numColors"] do
+        data.colors[i] = {
+            R = readByte(stream),
+            G = readByte(stream),
+            B = readByte(stream)
+        }
+    end
+    return data
+end
+
+local function extractChunkData(stream)
+    local chunkData = {}
+    local length
+    local type
+    local crc
+
+    while type ~= "IEND" do
+        length = readInt(stream)
+        type = readChar(stream, 4)
+        if (type == "IHDR") then
+            chunkData[type] = getDataIHDR(stream, length)
+        elseif (type == "IDAT") then
+            chunkData[type] = getDataIDAT(stream, length, chunkData[type])
+        elseif (type == "PLTE") then
+            chunkData[type] = getDataPLTE(stream, length)
+        else
+            readChar(stream, length)
+        end
+        crc = readChar(stream, 4)
+    end
+
+    return chunkData
+end
+
+local function makePixel(stream, depth, colorType, palette)
+    local bps = math.floor(depth/8) --bits per sample
+    local pixelData = { R = 0, G = 0, B = 0, A = 0 }
+    local grey
+    local index
+    local color 
+
+    if colorType == 0 then
+        grey = readInt(stream, bps)
+        pixelData.R = grey
+        pixelData.G = grey
+        pixelData.B = grey
+        pixelData.A = 255
+    elseif colorType == 2 then
+        pixelData.R = readInt(stream, bps)
+        pixelData.G = readInt(stream, bps)
+        pixelData.B = readInt(stream, bps)
+        pixelData.A = 255
+    elseif colorType == 3 then
+        index = readInt(stream, bps)+1
+        color = palette.colors[index]
+        pixelData.R = color.R
+        pixelData.G = color.G
+        pixelData.B = color.B
+        pixelData.A = 255
+    elseif colorType == 4 then
+        grey = readInt(stream, bps)
+        pixelData.R = grey
+        pixelData.G = grey
+        pixelData.B = grey
+        pixelData.A = readInt(stream, bps)
+    elseif colorType == 6 then
+        pixelData.R = readInt(stream, bps)
+        pixelData.G = readInt(stream, bps)
+        pixelData.B = readInt(stream, bps)
+        pixelData.A = readInt(stream, bps)
+    end
+
+    return pixelData
+end
+
+local function bitFromColorType(colorType)
+    if colorType == 0 then return 1 end
+    if colorType == 2 then return 3 end
+    if colorType == 3 then return 1 end
+    if colorType == 4 then return 2 end
+    if colorType == 6 then return 4 end
+    error 'Invalid colortype'
+end
+
+local function paethPredict(a, b, c)
+    local p = a + b - c
+    local varA = math.abs(p - a)
+    local varB = math.abs(p - b)
+    local varC = math.abs(p - c)
+
+    if varA <= varB and varA <= varC then 
+        return a 
+    elseif varB <= varC then 
+        return b 
+    else
+        return c
+    end
+end
+
+local function filterType1(curPixel, lastPixel)
+    local lastByte
+    local newPixel = {}
+    for fieldName, curByte in pairs(curPixel) do
+        lastByte = lastPixel and lastPixel[fieldName] or 0
+        newPixel[fieldName] = (curByte + lastByte) % 256
+    end
+    return newPixel
+end
+
+local prevPixelRow = {}
+local function getPixelRow(stream, depth, colorType, palette, length)
+    local pixelRow = {}
+    local bpp = math.floor(depth/8) * bitFromColorType(colorType)
+    local bpl = bpp*length
+    local filterType = readByte(stream)
+
+    if filterType == 0 then
+        for x = 1, length do
+            pixelRow[x] = makePixel(stream, depth, colorType, palette)
+        end
+    elseif filterType == 1 then
+        local curPixel
+        local lastPixel
+        local newPixel
+        local lastByte
+        for x = 1, length do
+            curPixel = makePixel(stream, depth, colorType, palette)
+            lastPixel = prevPixelRow[pixelNum]
+            newPixel = {}
+            for fieldName, curByte in pairs(curPixel) do
+                lastByte = lastPixel and lastPixel[fieldName] or 0
+                newPixel[fieldName] = (curByte + lastByte) % 256
+            end
+            pixelRow[x] = newPixel
+        end
+    else
+        error("Unsupported filter type: " .. tostring(filterType))
+    end
+    prevPixelRow = pixelRow
+
+    return pixelRow
+end
+
+
+local function pngImage(path, progCallback, verbose, memSave)
+    local stream = io.open(path, "rb")
+    local chunkData
+    local imStr
+    local width = 0
+    local height = 0
+    local depth = 0
+    local colorType = 0
+    local output = {}
+    local pixels = {}
+    local StringStream
+    local function printV(msg)
+        if (verbose) then
+            print(msg)
+        end
+    end
+
+    if readChar(stream, 8) ~= "\137\080\078\071\013\010\026\010" then 
+        error "Not a png"
+    end
+
+    printV("Parsing Chunks...")
+    chunkData = extractChunkData(stream)
+
+    width = chunkData.IHDR.width
+    height = chunkData.IHDR.height
+    depth = chunkData.IHDR.bitDepth
+    colorType = chunkData.IHDR.colorType
+
+    printV("Deflating...")
+    deflate.inflate_zlib {
+        input = chunkData.IDAT.data, 
+        output = function(byte) 
+            output[#output+1] = string.char(byte) 
+        end, 
+        disable_crc = true
+    }
+    StringStream = {
+        str = table.concat(output),
+        read = function(self, num)
+            local toreturn = self.str:sub(1, num)
+            self.str = self.str:sub(num + 1, self.str:len())
+            return toreturn
+        end  
+    }
+
+    printV("Creating pixelmap...")
+    for i = 1, height do
+        local pixelRow = getPixelRow(StringStream, depth, colorType, chunkData.PLTE, width)
+        if progCallback ~= nil then 
+            progCallback(i, height, pixelRow)
+        end
+        if not memSave then
+            pixels[i] = pixelRow
+        end
+    end
+
+    printV("Done.")
+    return {
+        width = width,
+        height = height,
+        depth = depth,
+        colorType = colorType,
+        pixels = pixels
+    }
+end
+
+return pngImage
\ No newline at end of file
diff --git a/mods/skins/textures/blank_skin.png b/mods/skins/textures/blank_skin.png
new file mode 100644 (file)
index 0000000..8edb0ff
Binary files /dev/null and b/mods/skins/textures/blank_skin.png differ
diff --git a/mods/skins/textures/p.png b/mods/skins/textures/p.png
new file mode 100644 (file)
index 0000000..fe53713
Binary files /dev/null and b/mods/skins/textures/p.png differ