Advertisement
minizbot

CCA - archives for computercraft!

Mar 7th, 2016
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.27 KB | None | 0 0
  1. local bit = bit32
  2. --BEGIN LZW
  3. --BEGIN LZW PACKING FUNCTIONS
  4. local function pack(bn1, bn2)
  5.     bytes = {}
  6.     bytes[1] = bit.band(bn1, 0xFF)
  7.     bytes[2] = bit.rshift(bn1, 8) + bit.lshift(bit.band(bn2, 0xF), 4)
  8.     bytes[3] = bit.rshift(bn2, 4)
  9.     return bytes[1], bytes[2], bytes[3]
  10. end
  11. local function upack(b1, b2, b3)
  12.     bn1 = b1 + bit.lshift(bit.band(b2, 0xF), 8)
  13.     bn2 = bit.lshift(b3,4) + bit.band(bit.rshift(b2, 4), 0xF)
  14.     return bn1, bn2
  15. end
  16. --END LZW PACKING FUNCTIONS
  17. --BEGIN LZW FUNCTIONS
  18. local function createDict(bool)
  19.     local ret = {}
  20.     for i = 1, 255 do
  21.         if bool then
  22.             ret[string.char(i)] = i
  23.         else
  24.             ret[i] = string.char(i)
  25.         end
  26.     end
  27.     if not bool then ret[256] = 256 end
  28.     return ret
  29. end
  30. --BEGIN LZW COMPRESS
  31. local function cp(sInput)
  32.     local dic = createDict(true)
  33.     local s = ""
  34.     local ch
  35.     local dlen = 256
  36.     local len = string.len(sInput)
  37.     local result = {}
  38.     local temp
  39.     for i = 1, len do
  40.         if dlen == 4095 then
  41.             result[#result + 1] = dic[s]
  42.             result[#result + 1] = 256
  43.             dic = createDict(true)
  44.             dlen = 256
  45.             s = ""
  46.         end
  47.         ch = string.sub(sInput, i, i)
  48.         temp = s..ch
  49.         if dic[temp] then
  50.             s = temp
  51.         else
  52.             result[#result + 1] = dic[s]
  53.             dlen = dlen +1
  54.             dic[temp] = dlen       
  55.             s = ch
  56.         end
  57.     end
  58.     result[#result + 1] = dic[s]
  59.    
  60.     return result
  61. end
  62. --END LZW COMPRESS
  63. --BEGIN LZW DECOMPRESS
  64. local function dc(data)
  65.     local dic = createDict(false)  
  66.     local entry
  67.     local ch
  68.     local currCode
  69.     local result = {}
  70.  
  71.     result[#result + 1] = dic[data[1]]
  72.     prefix = dic[data[1]]
  73.     for i = 2, #data do
  74.         currCode = data[i]
  75.         if currCode == 256 then
  76.             dic = createDict(false)
  77.             prefix = ""
  78.         else
  79.             entry = dic[currCode]
  80.             if entry then--exists in dictionary
  81.                 ch = string.sub(entry, 1, 1)       
  82.                 result[#result + 1] = entry
  83.                 if prefix ~= "" then
  84.                     dic[#dic+1] = prefix .. ch
  85.                 end
  86.             else   
  87.                 ch = string.sub(prefix, 1, 1)
  88.                 result[#result + 1] = prefix..ch
  89.                 dic[#dic + 1] = prefix..ch
  90.             end
  91.        
  92.             prefix = dic[currCode]
  93.         end
  94.     end
  95.    
  96.     return table.concat(result)
  97. end
  98. --END LZW DECOMPRESS
  99. --END LZW FUNCTIONS
  100. --END LZW
  101. --BEGIN MISC
  102. local function trim(inp)
  103.     for i = 0,2 do
  104.         if inp[#inp] == 0 then
  105.             inp[#inp] = nil
  106.         end
  107.     end
  108. end
  109. --END MISC
  110. --BEGIN PUBLIC LZW API
  111. function decompress(input)
  112.     local rec = {}
  113.     for i = 1, #input, 3 do
  114.         rec[#rec+1], rec[#rec+2] = upack(input[i], input[i+1] or 0, input[i+2] or 0)       
  115.     end
  116.     trim(rec)
  117.     return dc(rec)
  118. end
  119. function compress(input)
  120.     local rec = {}
  121.     local data = cp(input)
  122.     for i=1, #data, 2 do
  123.         rec[#rec+1], rec[#rec+2], rec[#rec+3] = pack(data[i], data[i+1] or 0)
  124.     end
  125.     trim(rec)
  126.     return rec
  127. end
  128. --END PUBLIC LZW API
  129. --END LZW
  130. --BEGIN BINARY RW FUNCTIONS
  131. --Writer helper functions
  132. function writeByte(fh, byt)
  133.     fh.write(bit.band(byt, 0xFF))
  134. end
  135.  
  136. function writeShort(fh, short)
  137.     writeByte(fh, bit.band(short, 0xFF))
  138.     writeByte(fh, bit.rshift(short, 8))
  139. end
  140. function writeInt(fh, int)
  141.     writeShort(fh, bit.band(int, 0xFFFF))
  142.     writeShort(fh, bit.rshift(int, 16))
  143. end
  144. function writeChar(fh, char)
  145.     writeByte(fh, string.byte(char))
  146. end
  147. function writeString(fh, str)
  148.     local chars = math.min(65535, #str)
  149.     writeShort(fh, chars)
  150.     for i = 1, chars do
  151.         writeByte(fh, string.byte(str, i,i))
  152.     end
  153. end
  154. function writeBytes(fh, bytes)
  155.     writeInt(fh, #bytes)
  156.     for k,v in ipairs(bytes) do
  157.         writeByte(fh, v)
  158.     end
  159. end
  160.  
  161. --reader helper functions
  162. function readByte(fh)
  163.     return fh.read()
  164. end
  165. function readShort(fh)
  166.     return readByte(fh) + bit.lshift(readByte(fh), 8)
  167. end
  168. function readInt(fh)
  169.     return readShort(fh) + bit.lshift(readShort(fh), 16)
  170. end
  171. function readChar(fh)
  172.     return string.char(readByte(fh))
  173. end
  174. function readString(fh)
  175.     local chars = {}
  176.     local len = readShort(fh)
  177.     for i = 1, len do
  178.         chars[#chars+1] = string.char(readByte(fh))
  179.     end
  180.     return table.concat(chars)
  181. end
  182. function readBytes(fh)
  183.     local len = readInt(fh)
  184.     local ret = {}
  185.     for i = 1, len do
  186.         ret[i] = readByte(fh)
  187.     end
  188.     return ret
  189. end
  190. --END BINARY RW FUNCTIONS
  191. --BEGIN CCA FORMAT SPEC
  192. --[[
  193.     * CCAs are a computercraft archival format designed by me (Minizbot2012) and internally compress with lzw and has a clear code
  194.     * CCAs have "header" codes which determine what is the next set of data
  195.     * CCAs have a special headers for folders, this is the only unique header
  196.     * "header" spec [
  197.         * (byte) 1 = the file code [
  198.             (Short) String length
  199.             (String) fileName
  200.             (Int) Compressed file length
  201.             (Byte[]) Compressed file Bytes
  202.         ]
  203.         * (byte) 2 = folder code = [
  204.             * (Short) String len
  205.             * (String) filenName
  206.         ]
  207.         * (byte) 3 = end folder code = [
  208.             No Options!
  209.         ]
  210.  
  211.     ]
  212. ]]
  213. CCACodes = {
  214.     function(obj, data) --1 = File-Code
  215.         if obj.mode == 1 then -- if in decompress mode, then return the folder, fileName, and the data
  216.             local folder = table.concat(obj.fbuff, "/")
  217.             local fileName = readString(obj.fil)
  218.             local fileData = decompress(readBytes(obj.fil))
  219.             return folder, fileName, fileData
  220.         else -- else we are passed a filename, we also add the control code
  221.             writeByte(obj.fil, 0x01)
  222.             writeString(obj.fil, fs.getName(data))
  223.             local tmp = fs.open(data, 'r')
  224.             local fd = tmp.readAll()
  225.             tmp.close()
  226.             writeBytes(obj.fil, compress(fd))
  227.         end
  228.     end,
  229.     function(obj, data) --2 = Folder-Begin
  230.         if obj.mode == 1 then -- if in decompress mode, then read a string from the file and add the name to the stack
  231.             local folderName = readString(obj.fil)
  232.             table.insert(obj.fbuff, folderName)
  233.         else -- else we are passed a foldername for data, add add the folder code with the folder's name
  234.             writeByte(obj.fil, 0x02)
  235.             writeString(obj.fil, data)
  236.         end
  237.     end,
  238.     function(obj, data) -- 3 = folder end
  239.         if obj.mode == 1 then -- if in decompress mode, pop the folder form the stack
  240.             table.remove(obj.fbuff)
  241.         else -- else we write the code to the filestream
  242.             writeByte(obj.fil, 0x03)
  243.         end
  244.     end,
  245. }
  246. CCA = {}
  247. function CCA:init()
  248.     --Object oriented compressor / decompressor
  249.     local obj = {}
  250.     obj.fbuff = {}
  251.     obj.mode = 0
  252.     setmetatable(obj, self)
  253.     self.__index = self
  254.     return obj
  255. end
  256. function CCA:parseCode(code, ex)
  257.     --just runs a code from the table
  258.     return CCACodes[code](self, ex)
  259. end
  260. function CCA:decompress(fileName, folder)
  261.     table.insert(self.fbuff, folder) --insert the top folder
  262.     self.mode = 1 -- set the mode
  263.     self.fil = fs.open(fileName, 'rb') -- open the folder to decompress
  264.     local code = readByte(self.fil)
  265.     repeat
  266.         local flder, fileName, data = self:parseCode(code) -- parese code and if output
  267.         if not(folder and fileName and data) then -- its a folder (no output)
  268.         else -- its a file
  269.             if not fs.exists(flder) then --its folder doesn't exist yet...
  270.                 fs.makeDir(flder) --So Make it!
  271.             end
  272.             --open a file and write output
  273.             local output = fs.open(fs.combine(flder, fileName), "w")
  274.             output.write(data)
  275.             output.close()
  276.         end
  277.         code = readByte(self.fil)
  278.     until code == nil
  279.     --close the input file
  280.     self.fil.close()
  281.     self.mode = 0
  282.     table.remove(self.fbuff)
  283.     --improper archive, if the last thing is a folder make sure to exit it!!
  284.     if #self.fbuff > 0 then
  285.         self.fbuff = {}
  286.         print("[WARNING] ARCHIVE MIGHT BE CORRUPTED")
  287.     end
  288. end
  289. function CCA:compress(fileName, folder)
  290.     self.mode = 2 -- mode - compress
  291.     self.fil = fs.open(fileName, 'wb') --open the output file
  292.     local name
  293.     for k,v in pairs(fs.list(folder)) do -- list the top-level folder
  294.         name = fs.combine(folder, v)
  295.         if fs.isDir(name) then -- is this file/ folder a directory?
  296.             self:compressFolder(name) -- call the compressor for folders
  297.         else
  298.             self:parseCode(0x01,name) -- else compress the file
  299.         end
  300.     end
  301.     --cleanup
  302.     self.mode = 0
  303.     self.fil.close()
  304. end
  305. function CCA:compressFolder(folder) -- compress the folder!
  306.     self:parseCode(0x02, fs.getName(folder)) --send the folder name to the encoder
  307.     local name -- prepare a name for files / folders
  308.     for k,v in pairs(fs.list(folder)) do -- go through each entry
  309.         name = fs.combine(folder, v) --get a name for the folder / file
  310.         if fs.isDir(name) then -- is it a fiel or folder?
  311.             self:compressFolder(name) -- folder! call the folder archiver recursively to handle
  312.         else
  313.             self:parseCode(0x01,name) --file! compress and add to the archive
  314.         end
  315.     end
  316.     self:parseCode(0x03) -- emit the end folder code to the stream
  317. end
  318. --function to get an archiver
  319. function newCCAArchiver()
  320.     return CCA:init()
  321. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement