-- ************************************************************************
-- Script: GZIP
-- Version: 0.3
-- Date: 2009-08-08
-- Author: Valentin Schmidt
--
-- implements GZIP compression according to RFC1952 (http://www.faqs.org/rfcs/rfc1952)
-- data is e.g. compatible with the GZIP commandline tool or PHP's gzencode() function.
--
-- Notice: unfortunately the current implementation of the compress/uncompress functions of director's bytearray class use
-- ZLIB (i.e. gzcompress) instead of raw DEFLATE compression. this is no limitation for compressing data (as
-- DEFLATE/GZIP/ZIP files) since the 6 additional ZLIB bytes (2 bytes at the beginning, 4 adler32 checksum bytes at the end)
-- just need to be removed to create valid DEFLATE compressed data (that can then be used to create GZIP/ZIP files). but
-- unfortunately the bytearray's uncompress() function fails if the 4 adler32 checksum bytes at the end of the bytearray are
-- missing or incorrect, and therefor INFLATE, GZIP and ZIP decompression is only possible if the adler32 checksums of the
-- original file(s) are known.
-- one way to lessen this strong limitation a little bit, so at least GZIP and ZIP files created with those scripts can be
-- decompressed without requiring any additional info, is to store the adler32 checksums as comments inside the GZP/ZIP file.
-- such files can be decompresssed with any 3rd party tool (which just ignore the comments) as well as with code based on
-- byterrays's uncompress function.
--
-- Requirements:
-- * fileIO xtra
-- * fileIO wrapper functions file_get_bytes()/file_put_bytes()
-- * str_replace() function
-- * crc32_bytearray() function (or alternatively an xtra for faster CRC32 calculation, e.g. crypto xtra)
--
-- ************************************************************************
----------------------------------------
-- NOTICE:
-- passed bytearray baInput is changed (bytearray is compressed)!!!
-- if this is not wanted, either create a copy or use ba.uncompress to restore original state
----------------------------------------
on gzip_encode_bytearray (baInput, tFilename, tComment, tDate)
info = [:]
crc32 = crc32_bytearray(baInput)
orgLen = baInput.length
baInput.compress()
baInput.position = baInput.length-3
baInput.endian=#bigEndian
info["adler32"] = baInput.readInt32()
baOutput = bytearray()
flags = 0
if not voidP(tFilename) then flags = flags + 8
if not voidP(tComment) then flags = flags + 16
if voidP(tDate) then tDate = the systemdate
unixTime = dateToUnixTime(tDate)
baOutput.writeInt8(31)
baOutput.writeInt8(139)
baOutput.writeInt8(8)
baOutput.writeInt8(flags)
baOutput.writeInt32(unixTime)
baOutput.writeInt8(2)
baOutput.writeInt8(255) -- ???
if not voidP(tFilename) then
if the platform contains "win" then
tFilename = str_replace("\", "/", tFilename)
else
tFilename = str_replace(":", "/", tFilename)
end if
baOutput.writeRawString(tFilename, tFilename.length)
baOutput.writeInt8(0)
end if
if not voidP(tComment) then
if tComment=#adler32 then
tComment = string(info["adler32"])
end if
baOutput.writeRawString(tComment, tComment.length)
baOutput.writeInt8(0)
end if
baOutput.writeByteArray(baInput, 3, baInput.length-6) -- remove header and adler32 checksum bytes
baOutput.writeInt32( crc32 )
baOutput.writeInt32( orgLen )
info["data"] = baOutput
return info
end
----------------------------------------
--
----------------------------------------
on gzip_encode_file (inputFile, outputFile, tComment, tDate)
if voidP(outputFile) then
outputFile = inputFile & ".gz"
end if
baInput = file_get_bytes(inputFile)
-- get filename from path
od = the itemdelimiter
the itemdelimiter = the last char of _movie.path
inputFileName = the last item of inputFile
the itemdelimiter = od
info = gzip_encode_bytearray (baInput, inputFileName, tComment, tDate)
file_put_bytes(outputFile, info["data"])
return info["adler32"]
end
----------------------------------------
-- uncompress gzip-encoded data (unfortunately only possible if adler32 checksum is known)
----------------------------------------
on gzip_decode_bytearray (baInput, adler32)
info = [:]
flags = baInput[4]
baInput.position = 5
unixTime = baInput.readInt32()
info["date"] = unixTimeToDate(unixTime)
startPos = 11
if bitand(flags, 8) then
baInput.position = startPos
repeat while baInput[startPos]<>0
startPos=startPos+1
end repeat
info["filename"] = baInput.readRawString(startPos-baInput.position)
startPos=startPos+1
end if
if bitand(flags, 16) then
baInput.position = startPos
repeat while baInput[startPos]<>0
startPos=startPos+1
end repeat
info["comment"] = baInput.readRawString(startPos-baInput.position)
startPos=startPos+1
end if
if adler32=#comment then -- adler32 stored as comment
if voidP(info["comment"]) then
-- error
else
adler32 = integer(info["comment"])
end if
end if
baInput.position = startPos
tmp = baInput.readByteArray(baInput.length-startPos-8+1)
-- INFLATE
data = bytearray(tmp.length+6)
data[1] = 120
data[2] = 218
data.position = 3
data.writeByteArray(tmp)
data.endian = #bigEndian
data.writeInt32(adler32)
data.uncompress()
info["data"] = data
return info
end
----------------------------------------
-- uncompress gzip-encoded data (unfortunately only possible if adler32 checksum is known)
----------------------------------------
on gzip_decode_file (inputFile, outputFile, adler32)
baInput = file_get_bytes(inputFile)
info = gzip_decode_bytearray (baInput, adler32)
if voidP(outputFile) then
od = the itemdelimiter
pd = the last char of _movie.path
the itemdelimiter = pd
outputFile = inputFile
delete the last item of outputFile
put pd after outputFile
if not voidP(info["filename"]) then -- extract filename from GZIP file?
put info["filename"] after outputFile
else -- else extract filename from GZIP filename
inputFileName = the last item of inputFile
the itemdelimiter = "."
delete the last item of inputFileName
put inputFileName after outputFile
end if
the itemdelimiter = od
end if
file_put_bytes(outputFile, info["data"])
return info
end
----------------------------------------
--
----------------------------------------
on dateToUnixTime (tDate)
days = tDate - date(1970, 1, 1)
return days*24*60*60 + tDate.seconds
end
----------------------------------------
--
----------------------------------------
on unixTimeToDate (unixTime)
tDate = date(1970, 1, 1)
days = unixTime / 86400
tDate = tDate + days
tDate.seconds = unixTime mod 86400
return tDate
end