MineOS/lib/compressor.lua
Igor Timofeev c441be1ab7 aef
2017-01-23 06:46:27 +03:00

216 lines
7.3 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

local unicode = require("unicode")
local fs = require("filesystem")
local compressor = {}
------------------------------------------------------------------------------------------------------------------
local function numberToByteArray(number)
local byteArray = {}
while number > 0 do
table.insert(byteArray, 1, bit32.band(number, 0xFF))
number = bit32.rshift(number, 8)
end
return byteArray
end
local function byteArrayToNumber(byteArray)
local number = byteArray[1]
for i = 2, #byteArray do
number = bit32.bor(byteArray[i], bit32.lshift(number, 8))
end
return number
end
local function info(showInfo, text)
if showInfo then
print(text)
end
end
------------------------------------------------------------------------------------------------------------------
local function writePath(compressedFile, path)
-- Получаем юникод-байтики названия файла или папки
local pathBytes = {}
for i = 1, unicode.len(path) do
local charBytes = { string.byte(unicode.sub(path, i, i), 1, 6) }
for j = 1, #charBytes do
table.insert(pathBytes, charBytes[j])
end
end
-- Записываем количество байт, необходимое для записи РАЗМЕРА байт пути
local bytesForCountOfBytesForPath = numberToByteArray(#pathBytes)
compressedFile:write(string.char(#bytesForCountOfBytesForPath))
-- Записываем количество байт, необходимое для записи самого пути
for i = 1, #bytesForCountOfBytesForPath do
compressedFile:write(string.char(bytesForCountOfBytesForPath[i]))
end
-- Записываем байтики пути
for i = 1, #pathBytes do
compressedFile:write(string.char(pathBytes[i]))
end
end
local function writeFileSize(compressedFile, path)
local size = fs.size(path)
local bytesForSize = numberToByteArray(size)
-- Записываем количество байт, необходимое для записи РАЗМЕРА байт размера файла
compressedFile:write(string.char(#bytesForSize))
-- Записываем сами байты размера файла
for i = 1, #bytesForSize do
compressedFile:write(string.char(bytesForSize[i]))
end
end
local function getFileList(path)
local fileList = {}
for file in fs.list(path) do
table.insert(fileList, path .. file)
end
return fileList
end
local function doCompressionRecursively(compressedFile, fileList, currentPackPath, pathToCompressedFile, showInfo)
for file = 1, #fileList do
local filename = (fs.name(fileList[file]) or "")
local filePackPath = currentPackPath .. filename
-- info(showInfo, "Local path: " .. filePackPath)
if fileList[file] == pathToCompressedFile or filename == "mnt" or filename == "dev" or filename == ".DS_Store" then
info(showInfo, "Skipping restricted path \"" .. fileList[file] .. "\"")
else
if fs.isDirectory(fileList[file]) then
info(showInfo, "Packing directory " .. fileList[file])
compressedFile:write("D")
writePath(compressedFile, filePackPath .. "/")
doCompressionRecursively(compressedFile, getFileList(fileList[file]), filePackPath .. "/", pathToCompressedFile, showInfo)
else
info(showInfo, "Packing file " .. fileList[file])
compressedFile:write("F")
writePath(compressedFile, filePackPath)
writeFileSize(compressedFile, fileList[file])
local fileToCompress = io.open(fileList[file], "rb")
compressedFile:write(fileToCompress:read("*a"))
fileToCompress:close()
end
end
-- require("ECSAPI").wait()
end
end
function compressor.pack(pathToCompressedFile, ...)
local data, showInfo = {...}, false
if type(data[#data]) == "boolean" then showInfo = data[#data]; data[#data] = nil end
info(showInfo, "Packing data to file \"" .. pathToCompressedFile .. "\"...")
info(showInfo, " ")
fs.makeDirectory(fs.path(pathToCompressedFile))
-- Открываем файл со сжатым контентом
local compressedFile, reason = io.open(pathToCompressedFile, "wb")
if not compressedFile then
error("Failed to open package file for writing: " .. tostring(reason))
end
-- Записываем сигнатурку
compressedFile:write("ARCH")
-- Пакуем данные
doCompressionRecursively(compressedFile, data, "", pathToCompressedFile, showInfo)
-- Закрываем файл со сжатым контентом
compressedFile:close()
info(showInfo, " ")
info(showInfo, "Data packing finished")
end
------------------------------------------------------------------------------------------------------------------
local function readPath(compressedFile)
local countOfBytesForPathBytes = string.byte(compressedFile:read(1))
local pathBytes = {}
for i = 1, countOfBytesForPathBytes do
table.insert(pathBytes, string.byte(compressedFile:read(1)))
end
local pathSize = byteArrayToNumber(pathBytes)
local path = compressedFile:read(pathSize)
-- info(showInfo, "Колво байт под байты пути: ", countOfBytesForPathBytes)
-- info(showInfo, "Колво байт под путь: ", pathSize)
-- info(showInfo, "Путь: ", path)
return path
end
local function readFileSize(compressedFile)
local countOfBytesForFileSize = string.byte(compressedFile:read(1))
local fileSizeBytes = {}
for i = 1, countOfBytesForFileSize do
table.insert(fileSizeBytes, string.byte(compressedFile:read(1)))
end
local fileSize = byteArrayToNumber(fileSizeBytes)
-- info(showInfo, "Размер файла: ", fileSize)
return fileSize
end
function compressor.unpack(pathToCompressedFile, pathWhereToUnpack, showInfo)
info(showInfo, "Unpacking data from file \"" .. pathToCompressedFile .. "\"...")
info(showInfo, " ")
fs.makeDirectory(pathWhereToUnpack)
local compressedFile, reason = io.open(pathToCompressedFile, "rb")
if not compressedFile then
error("Failed to open package file for reading: " .. tostring(reason))
end
local signature = compressedFile:read(4)
if signature == "ARCH" then
while true do
local type = compressedFile:read(1)
if type == "D" then
local path = readPath(compressedFile)
fs.makeDirectory(pathWhereToUnpack .. path)
info(showInfo, "Unpacking directory \"" .. path .. "\"")
elseif type == "F" then
local path = readPath(compressedFile)
local size = readFileSize(compressedFile)
info(showInfo, "Unpacking file \"" .. path .. "\"")
local file, reason = io.open(pathWhereToUnpack .. path, "wb")
if file then
file:write(compressedFile:read(size))
file:close()
else
compressedFile:read(size)
info(showInfo, "Failed to open file for writing while unpacking: " .. tostring(reason))
end
elseif not type then
break
else
compressedFile:close()
error("Packed file is corrupted, unknown path type: " .. tostring(type))
end
end
else
compressedFile:close()
error("Packed file is corrupted, wrong signature: " .. tostring(signature))
end
compressedFile:close()
info(showInfo, " ")
info(showInfo, "Unpacking data finished")
end
------------------------------------------------------------------------------------------------------------------
-- compressor.pack("/test1.pkg", "/MineOS/System/OS/", "/etc/", true)
-- info(showInfo, " ")
-- compressor.unpack("/test1.pkg", "/papkaUnpacked/", true)
------------------------------------------------------------------------------------------------------------------
return compressor