Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local bit = bit32
- --BEGIN LZW
- --BEGIN LZW PACKING FUNCTIONS
- local function pack(bn1, bn2)
- bytes = {}
- bytes[1] = bit.band(bn1, 0xFF)
- bytes[2] = bit.rshift(bn1, 8) + bit.lshift(bit.band(bn2, 0xF), 4)
- bytes[3] = bit.rshift(bn2, 4)
- return bytes[1], bytes[2], bytes[3]
- end
- local function upack(b1, b2, b3)
- bn1 = b1 + bit.lshift(bit.band(b2, 0xF), 8)
- bn2 = bit.lshift(b3,4) + bit.band(bit.rshift(b2, 4), 0xF)
- return bn1, bn2
- end
- --END LZW PACKING FUNCTIONS
- --BEGIN LZW FUNCTIONS
- local function createDict(bool)
- local ret = {}
- for i = 1, 255 do
- if bool then
- ret[string.char(i)] = i
- else
- ret[i] = string.char(i)
- end
- end
- if not bool then ret[256] = 256 end
- return ret
- end
- --BEGIN LZW COMPRESS
- local function cp(sInput)
- local dic = createDict(true)
- local s = ""
- local ch
- local dlen = 256
- local len = string.len(sInput)
- local result = {}
- local temp
- for i = 1, len do
- if dlen == 4095 then
- result[#result + 1] = dic[s]
- result[#result + 1] = 256
- dic = createDict(true)
- dlen = 256
- s = ""
- end
- ch = string.sub(sInput, i, i)
- temp = s..ch
- if dic[temp] then
- s = temp
- else
- result[#result + 1] = dic[s]
- dlen = dlen +1
- dic[temp] = dlen
- s = ch
- end
- end
- result[#result + 1] = dic[s]
- return result
- end
- --END LZW COMPRESS
- --BEGIN LZW DECOMPRESS
- local function dc(data)
- local dic = createDict(false)
- local entry
- local ch
- local currCode
- local result = {}
- result[#result + 1] = dic[data[1]]
- prefix = dic[data[1]]
- for i = 2, #data do
- currCode = data[i]
- if currCode == 256 then
- dic = createDict(false)
- prefix = ""
- else
- entry = dic[currCode]
- if entry then--exists in dictionary
- ch = string.sub(entry, 1, 1)
- result[#result + 1] = entry
- if prefix ~= "" then
- dic[#dic+1] = prefix .. ch
- end
- else
- ch = string.sub(prefix, 1, 1)
- result[#result + 1] = prefix..ch
- dic[#dic + 1] = prefix..ch
- end
- prefix = dic[currCode]
- end
- end
- return table.concat(result)
- end
- --END LZW DECOMPRESS
- --END LZW FUNCTIONS
- --END LZW
- --BEGIN MISC
- local function trim(inp)
- for i = 0,2 do
- if inp[#inp] == 0 then
- inp[#inp] = nil
- end
- end
- end
- --END MISC
- --BEGIN PUBLIC LZW API
- function decompress(input)
- local rec = {}
- for i = 1, #input, 3 do
- rec[#rec+1], rec[#rec+2] = upack(input[i], input[i+1] or 0, input[i+2] or 0)
- end
- trim(rec)
- return dc(rec)
- end
- function compress(input)
- local rec = {}
- local data = cp(input)
- for i=1, #data, 2 do
- rec[#rec+1], rec[#rec+2], rec[#rec+3] = pack(data[i], data[i+1] or 0)
- end
- trim(rec)
- return rec
- end
- --END PUBLIC LZW API
- --END LZW
- --BEGIN BINARY RW FUNCTIONS
- --Writer helper functions
- function writeByte(fh, byt)
- fh.write(bit.band(byt, 0xFF))
- end
- function writeShort(fh, short)
- writeByte(fh, bit.band(short, 0xFF))
- writeByte(fh, bit.rshift(short, 8))
- end
- function writeInt(fh, int)
- writeShort(fh, bit.band(int, 0xFFFF))
- writeShort(fh, bit.rshift(int, 16))
- end
- function writeChar(fh, char)
- writeByte(fh, string.byte(char))
- end
- function writeString(fh, str)
- local chars = math.min(65535, #str)
- writeShort(fh, chars)
- for i = 1, chars do
- writeByte(fh, string.byte(str, i,i))
- end
- end
- function writeBytes(fh, bytes)
- writeInt(fh, #bytes)
- for k,v in ipairs(bytes) do
- writeByte(fh, v)
- end
- end
- --reader helper functions
- function readByte(fh)
- return fh.read()
- end
- function readShort(fh)
- return readByte(fh) + bit.lshift(readByte(fh), 8)
- end
- function readInt(fh)
- return readShort(fh) + bit.lshift(readShort(fh), 16)
- end
- function readChar(fh)
- return string.char(readByte(fh))
- end
- function readString(fh)
- local chars = {}
- local len = readShort(fh)
- for i = 1, len do
- chars[#chars+1] = string.char(readByte(fh))
- end
- return table.concat(chars)
- end
- function readBytes(fh)
- local len = readInt(fh)
- local ret = {}
- for i = 1, len do
- ret[i] = readByte(fh)
- end
- return ret
- end
- --END BINARY RW FUNCTIONS
- --BEGIN CCA FORMAT SPEC
- --[[
- * CCAs are a computercraft archival format designed by me (Minizbot2012) and internally compress with lzw and has a clear code
- * CCAs have "header" codes which determine what is the next set of data
- * CCAs have a special headers for folders, this is the only unique header
- * "header" spec [
- * (byte) 1 = the file code [
- (Short) String length
- (String) fileName
- (Int) Compressed file length
- (Byte[]) Compressed file Bytes
- ]
- * (byte) 2 = folder code = [
- * (Short) String len
- * (String) filenName
- ]
- * (byte) 3 = end folder code = [
- No Options!
- ]
- ]
- ]]
- CCACodes = {
- function(obj, data) --1 = File-Code
- if obj.mode == 1 then -- if in decompress mode, then return the folder, fileName, and the data
- local folder = table.concat(obj.fbuff, "/")
- local fileName = readString(obj.fil)
- local fileData = decompress(readBytes(obj.fil))
- return folder, fileName, fileData
- else -- else we are passed a filename, we also add the control code
- writeByte(obj.fil, 0x01)
- writeString(obj.fil, fs.getName(data))
- local tmp = fs.open(data, 'r')
- local fd = tmp.readAll()
- tmp.close()
- writeBytes(obj.fil, compress(fd))
- end
- end,
- function(obj, data) --2 = Folder-Begin
- if obj.mode == 1 then -- if in decompress mode, then read a string from the file and add the name to the stack
- local folderName = readString(obj.fil)
- table.insert(obj.fbuff, folderName)
- else -- else we are passed a foldername for data, add add the folder code with the folder's name
- writeByte(obj.fil, 0x02)
- writeString(obj.fil, data)
- end
- end,
- function(obj, data) -- 3 = folder end
- if obj.mode == 1 then -- if in decompress mode, pop the folder form the stack
- table.remove(obj.fbuff)
- else -- else we write the code to the filestream
- writeByte(obj.fil, 0x03)
- end
- end,
- }
- CCA = {}
- function CCA:init()
- --Object oriented compressor / decompressor
- local obj = {}
- obj.fbuff = {}
- obj.mode = 0
- setmetatable(obj, self)
- self.__index = self
- return obj
- end
- function CCA:parseCode(code, ex)
- --just runs a code from the table
- return CCACodes[code](self, ex)
- end
- function CCA:decompress(fileName, folder)
- table.insert(self.fbuff, folder) --insert the top folder
- self.mode = 1 -- set the mode
- self.fil = fs.open(fileName, 'rb') -- open the folder to decompress
- local code = readByte(self.fil)
- repeat
- local flder, fileName, data = self:parseCode(code) -- parese code and if output
- if not(folder and fileName and data) then -- its a folder (no output)
- else -- its a file
- if not fs.exists(flder) then --its folder doesn't exist yet...
- fs.makeDir(flder) --So Make it!
- end
- --open a file and write output
- local output = fs.open(fs.combine(flder, fileName), "w")
- output.write(data)
- output.close()
- end
- code = readByte(self.fil)
- until code == nil
- --close the input file
- self.fil.close()
- self.mode = 0
- table.remove(self.fbuff)
- --improper archive, if the last thing is a folder make sure to exit it!!
- if #self.fbuff > 0 then
- self.fbuff = {}
- print("[WARNING] ARCHIVE MIGHT BE CORRUPTED")
- end
- end
- function CCA:compress(fileName, folder)
- self.mode = 2 -- mode - compress
- self.fil = fs.open(fileName, 'wb') --open the output file
- local name
- for k,v in pairs(fs.list(folder)) do -- list the top-level folder
- name = fs.combine(folder, v)
- if fs.isDir(name) then -- is this file/ folder a directory?
- self:compressFolder(name) -- call the compressor for folders
- else
- self:parseCode(0x01,name) -- else compress the file
- end
- end
- --cleanup
- self.mode = 0
- self.fil.close()
- end
- function CCA:compressFolder(folder) -- compress the folder!
- self:parseCode(0x02, fs.getName(folder)) --send the folder name to the encoder
- local name -- prepare a name for files / folders
- for k,v in pairs(fs.list(folder)) do -- go through each entry
- name = fs.combine(folder, v) --get a name for the folder / file
- if fs.isDir(name) then -- is it a fiel or folder?
- self:compressFolder(name) -- folder! call the folder archiver recursively to handle
- else
- self:parseCode(0x01,name) --file! compress and add to the archive
- end
- end
- self:parseCode(0x03) -- emit the end folder code to the stream
- end
- --function to get an archiver
- function newCCAArchiver()
- return CCA:init()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement