Guest User

velx loader

a guest
May 14th, 2020
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.09 KB | None | 0 0
  1. local magic = 0x5f7d
  2. local magic_rev = 0x7d5f
  3. local header_fmt = "I2I2I2I2I2I6I6"
  4. local en = string.unpack("=I2", string.char(0x7d, 0x5f)) == magic -- true = LE, false = BE
  5. local function get_end(e)
  6.     return (e and "<") or ">"
  7. end
  8. local function read_header(dat)
  9.     local e = get_end(en)
  10.     local m = string.unpack(e.."I2", dat)
  11.     if m ~= magic and m ~= magic_rev then return nil, "bad magic" end
  12.     if m ~= magic then
  13.         e = get_end(not en)
  14.     end
  15.     local ent = {}
  16.     ent.magic, ent.namesize, ent.mode, ent.uid, ent.gid, ent.filesize, ent.mtime = string.unpack(e..header_fmt, dat)
  17.     return ent
  18. end
  19.  
  20. local arc = {}
  21.  
  22. function arc:get(path)
  23.     if path:sub(1,1) ~= "/" then path = "/"..path end
  24.     for i=1, #self.tbl do
  25.         if (self.tbl[i].name == path and self.tbl[i].mode & 32768 > 0) then
  26.             self.seek(self.tbl[i].pos-self.seek(0))
  27.             return self.read(self.tbl[i].filesize), self.tbl[i]
  28.         end
  29.     end
  30.     return nil, "file not found"
  31. end
  32.  
  33. function arc:exists(path)
  34.     if path:sub(1,1) ~= "/" then path = "/"..path end
  35.     for i=1, #self.tbl do
  36.         if (self.tbl[i].name == path) then
  37.             return true
  38.         end
  39.     end
  40.     return false
  41. end
  42.  
  43. function arc:list(path)
  44.     if path:sub(#path) ~= "/" then path = path .. "/" end
  45.     if path:sub(1,1) ~= "/" then path = "/"..path end
  46.     local ent = {}
  47.     for i=1, #self.tbl do
  48.         if (self.tbl[i].name:sub(1, #path) == path and not self.tbl[i].name:find("/", #path+1, false)) then
  49.             ent[#ent+1] = self.tbl[i].name:gsub("^(.*/)", "")
  50.         end
  51.     end
  52.     return ent
  53. end
  54.  
  55. function arc:close()
  56.     self.close()
  57. end
  58.  
  59. local tsar_read = function(read, seek, close)
  60.         local tbl = {}
  61.         local lname = ""
  62.         while lname ~= "TRAILER!!!" do
  63.             local dat = read(22)
  64.             local et = read_header(dat)
  65.             et.name = read(et.namesize)
  66.             et.pos = seek(et.namesize & 1)
  67.             seek(et.filesize + (et.filesize & 1))
  68.             lname = et.name
  69.             if lname ~= "TRAILER!!!" then
  70.                 if (et.name:sub(1,1) ~= "/") then
  71.                     et.name = "/"..et.name
  72.                 end
  73.                 tbl[#tbl+1] = et
  74.             end
  75.         end
  76.         return setmetatable({tbl = tbl, read = read, seek = seek, close = close}, {__index=arc})
  77.     end
  78.  
  79. --[[----------------------------------------------------------------------------
  80.     LZSS - encoder / decoder
  81.     This is free and unencumbered software released into the public domain.
  82.     Anyone is free to copy, modify, publish, use, compile, sell, or
  83.     distribute this software, either in source code form or as a compiled
  84.     binary, for any purpose, commercial or non-commercial, and by any
  85.     means.
  86.     In jurisdictions that recognize copyright laws, the author or authors
  87.     of this software dedicate any and all copyright interest in the
  88.     software to the public domain. We make this dedication for the benefit
  89.     of the public at large and to the detriment of our heirs and
  90.     successors. We intend this dedication to be an overt act of
  91.     relinquishment in perpetuity of all present and future rights to this
  92.     software under copyright law.
  93.     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  94.     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  95.     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  96.     IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  97.     OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  98.     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  99.     OTHER DEALINGS IN THE SOFTWARE.
  100.     For more information, please refer to <http://unlicense.org/>
  101. --]]----------------------------------------------------------------------------
  102. --------------------------------------------------------------------------------
  103. local M = {}
  104. local string, table = string, table
  105.  
  106. --------------------------------------------------------------------------------
  107. local POS_BITS = 12
  108. local LEN_BITS = 16 - POS_BITS
  109. local POS_SIZE = 1 << POS_BITS
  110. local LEN_SIZE = 1 << LEN_BITS
  111. local LEN_MIN = 3
  112.  
  113. --------------------------------------------------------------------------------
  114. function M.compress(input)
  115.     local offset, output = 1, {}
  116.     local window = ''
  117.  
  118.     local function search()
  119.         for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do
  120.             local str = string.sub(input, offset, offset + i - 1)
  121.             local pos = string.find(window, str, 1, true)
  122.             if pos then
  123.                 return pos, str
  124.             end
  125.         end
  126.     end
  127.  
  128.     while offset <= #input do
  129.         local flags, buffer = 0, {}
  130.  
  131.         for i = 0, 7 do
  132.             if offset <= #input then
  133.                 local pos, str = search()
  134.                 if pos and #str >= LEN_MIN then
  135.                     local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN)
  136.                     buffer[#buffer + 1] = string.pack('>I2', tmp)
  137.                 else
  138.                     flags = flags | (1 << i)
  139.                     str = string.sub(input, offset, offset)
  140.                     buffer[#buffer + 1] = str
  141.                 end
  142.                 window = string.sub(window .. str, -POS_SIZE)
  143.                 offset = offset + #str
  144.             else
  145.                 break
  146.             end
  147.         end
  148.  
  149.         if #buffer > 0 then
  150.             output[#output + 1] = string.char(flags)
  151.             output[#output + 1] = table.concat(buffer)
  152.         end
  153.     end
  154.  
  155.     return table.concat(output)
  156. end
  157.  
  158. --------------------------------------------------------------------------------
  159. function M.decompress(input)
  160.     local offset, output = 1, {}
  161.     local window = ''
  162.  
  163.     while offset <= #input do
  164.         local flags = string.byte(input, offset)
  165.         offset = offset + 1
  166.  
  167.         for i = 1, 8 do
  168.             local str = nil
  169.             if (flags & 1) ~= 0 then
  170.                 if offset <= #input then
  171.                     str = string.sub(input, offset, offset)
  172.                     offset = offset + 1
  173.                 end
  174.             else
  175.                 if offset + 1 <= #input then
  176.                     local tmp = string.unpack('>I2', input, offset)
  177.                     offset = offset + 2
  178.                     local pos = (tmp >> LEN_BITS) + 1
  179.                     local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN
  180.                     str = string.sub(window, pos, pos + len - 1)
  181.                 end
  182.             end
  183.             flags = flags >> 1
  184.             if str then
  185.                 output[#output + 1] = str
  186.                 window = string.sub(window .. str, -POS_SIZE)
  187.             end
  188.         end
  189.     end
  190.  
  191.     return table.concat(output)
  192. end
  193.  
  194.  
  195. local function struct(tbl)
  196.     local pat = tbl.endian or "="
  197.     local args = {}
  198.     for i=1, #tbl do
  199.         local a, b = pairs(tbl[i])
  200.         local k, v = a(b)
  201.         args[i] = k
  202.         pat = pat .. v
  203.     end
  204.     return setmetatable({}, {__call=function(_, arg)
  205.         --checkArg(1, arg, "string", "table")
  206.         if (type(arg) == "string") then
  207.             local sval = {string.unpack(pat, arg)}
  208.             local rtn = {}
  209.             for i=1, #args do
  210.                 rtn[args[i]] = sval[i]
  211.             end
  212.             return rtn, sval[#sval]
  213.         elseif (type(arg) == "table") then
  214.             local sval = {}
  215.             for i=1, #args do
  216.                 sval[i] = arg[args[i]]
  217.             end
  218.             return string.pack(pat, unpack(sval))
  219.         end
  220.     end, __len=function()
  221.         return string.packsize(pat)
  222.     end})
  223. end
  224.  
  225. local velx_spec = struct {
  226.     endian = "<",
  227.     {magic="c5"},
  228.     {fver="B"},
  229.     {compression="B"},
  230.     {lver="B"},
  231.     {os="B"},
  232.     {arctype="c4"},
  233.     {psize="I3"},
  234.     {lsize="I3"},
  235.     {ssize="I3"},
  236.     {rsize="I4"}
  237. }
  238.  
  239. local function load_velx_part(hand, env, name, lib)
  240.     local venv = {_ENV=env}
  241.     setmetatable(venv, {__index=env})
  242.     venv._G = venv
  243.     local dat = hand:read(#velx_spec)
  244.     if (not dat or dat == "") then return nil end
  245.     local header = velx_spec(dat)
  246.     if (header.magic ~= "\27VelX") then
  247.         return nil, "Bad magic."
  248.     end
  249.     if (header.fver ~= 0) then
  250.         return nil, "Only version 0 is supported.", header
  251.     end
  252.     if (header.lver > 0x53) then
  253.         return nil, "Wrong Lua version.", header
  254.     end
  255.     if (header.os & 0x7F ~= 1) then
  256.         return nil, "Wrong OS.", header
  257.     end
  258.     if (header.os & 0x80 ~= ((lib and 0x80) or 0)) then
  259.         return nil, "Wrong binary type.", header
  260.     end
  261.     if (header.compression > 1) then
  262.         return nil, "Bad compression method."
  263.     end
  264.     if (header.arctype ~= "tsar" and header.arctype ~= "") then
  265.         return nil, "Invalid archive type.", header
  266.     end
  267.     -- Load code portion
  268.     local data = ""
  269.     while header.psize-#data > 0 do
  270.         data = data .. hand:read(header.psize-#data)
  271.     end
  272.     if (header.compression > 0) then
  273.         data = M.decompress(data)
  274.     end
  275.     hand:seek("cur", header.lsize)
  276.     hand:seek("cur", header.ssize)
  277.     local cpos = hand:seek("cur", 0)
  278.     if (header.arctype == "tsar") then
  279.         venv._ARCHIVE = tsar_read(function(amt)
  280.             local data = ""
  281.             while amt-#data > 0 do
  282.                 data = data .. hand:read(amt-#data)
  283.             end
  284.             return data
  285.         end, function(amt)
  286.             return hand:seek("cur", amt)-cpos+1
  287.         end, function()
  288.             hand:close()
  289.         end)
  290.     end
  291.     return load(data, "="..name, "t", venv)
  292. end
  293.  
  294. local function load_velx(hand, env, name, lib)
  295.     local func, err, head
  296.     local lasterr
  297.     while true do
  298.         lasterr = err
  299.         func, err, head = load_velx_part(hand, env, name, lib)
  300.         if (func) then return func end
  301.         if not head then return nil, err or lasterr end
  302.         hand:seek("cur", head.psize+head.lsize+head.ssize+head.rsize)
  303.     end
  304. end
  305.  
  306. local velx = {}
  307.  
  308. function velx.load(hand, env)
  309.     return load_velx(hand, env, os.getenv("_"), false)
  310. end
  311.  
  312. function velx.loadfile(path, env)
  313.     return load_velx(package.loaded.filesystem.open(path, "rb"), env, path, true)
  314. end
  315.  
  316. velx.mode = "rb"
  317. velx.version = "1.0"
  318. function velx.info()
  319.     print("    A loader for VELX format executables.")
  320.     print("    VELX version: 1")
  321.     print("    Supported compression:")
  322.     print("        - None")
  323.     print("        - LZSS")
  324.     print("    Supported archives:")
  325.     print("        - None")
  326.     print("        - tsar")
  327.     print("    VELX OSID: 1")
  328.     print("    Fat binaries: Supported")
  329. end
  330.  
  331. return velx
Add Comment
Please, Sign In to add comment