mirror of
https://github.com/0x5eal/luau-unzip.git
synced 2025-04-10 17:20:53 +01:00
feat: add method
field to ZipEntry
Contains information about the compression method for the entry.
This commit is contained in:
parent
121869ad3d
commit
1078fd249c
2 changed files with 39 additions and 29 deletions
|
@ -1,5 +1,4 @@
|
||||||
local fs = require("@lune/fs")
|
local fs = require("@lune/fs")
|
||||||
local stdio = require("@lune/stdio")
|
|
||||||
local zip = require("../lib")
|
local zip = require("../lib")
|
||||||
|
|
||||||
local file = fs.readFile("tests/data/files_and_dirs.zip")
|
local file = fs.readFile("tests/data/files_and_dirs.zip")
|
||||||
|
@ -17,7 +16,7 @@ end)
|
||||||
print("\Children of `/`:")
|
print("\Children of `/`:")
|
||||||
local assets = reader:listDirectory("/")
|
local assets = reader:listDirectory("/")
|
||||||
for _, entry in assets do
|
for _, entry in assets do
|
||||||
print(` {entry.name} - {if entry.isDirectory then "DIR" else "FILE"}`)
|
print(` {entry.name} - {if entry.isDirectory then "DIR" else "FILE"} ({entry.method})`)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get archive statistics
|
-- Get archive statistics
|
||||||
|
|
|
@ -32,21 +32,28 @@ local function validateCrc(decompressed: buffer, validation: CrcValidationOption
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local DECOMPRESSION_ROUTINES: { [number]: (buffer, number, CrcValidationOptions) -> buffer } = table.freeze({
|
export type CompressionMethod = "STORE" | "DEFLATE"
|
||||||
|
local DECOMPRESSION_ROUTINES: { [number]: { name: CompressionMethod, decompress: (buffer, number, CrcValidationOptions) -> buffer } } = table.freeze({
|
||||||
-- `STORE` decompression method - No compression
|
-- `STORE` decompression method - No compression
|
||||||
[0x00] = function(buf, _, validation)
|
[0x00] = {
|
||||||
validateCrc(buf, validation)
|
name = "STORE" :: CompressionMethod,
|
||||||
return buf
|
decompress = function(buf, _, validation)
|
||||||
end,
|
validateCrc(buf, validation)
|
||||||
|
return buf
|
||||||
|
end
|
||||||
|
},
|
||||||
|
|
||||||
-- `DEFLATE` decompression method - Compressed raw deflate chunks
|
-- `DEFLATE` decompression method - Compressed raw deflate chunks
|
||||||
[0x08] = function(buf, uncompressedSize, validation)
|
[0x08] = {
|
||||||
-- FIXME: Why is uncompressedSize not getting inferred correctly although it
|
name = "DEFLATE" :: CompressionMethod,
|
||||||
-- is typed?
|
decompress = function(buf, uncompressedSize, validation)
|
||||||
local decompressed = inflate(buf, uncompressedSize :: any)
|
-- FIXME: Why is uncompressedSize not getting inferred correctly although it
|
||||||
validateCrc(decompressed, validation)
|
-- is typed?
|
||||||
return decompressed
|
local decompressed = inflate(buf, uncompressedSize :: any)
|
||||||
end,
|
validateCrc(decompressed, validation)
|
||||||
|
return decompressed
|
||||||
|
end
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
-- TODO: ERROR HANDLING!
|
-- TODO: ERROR HANDLING!
|
||||||
|
@ -55,23 +62,25 @@ local ZipEntry = {}
|
||||||
export type ZipEntry = typeof(setmetatable({} :: ZipEntryInner, { __index = ZipEntry }))
|
export type ZipEntry = typeof(setmetatable({} :: ZipEntryInner, { __index = ZipEntry }))
|
||||||
-- stylua: ignore
|
-- stylua: ignore
|
||||||
type ZipEntryInner = {
|
type ZipEntryInner = {
|
||||||
name: string, -- File path within ZIP, '/' suffix indicates directory
|
name: string, -- File path within ZIP, '/' suffix indicates directory
|
||||||
size: number, -- Uncompressed size in bytes
|
size: number, -- Uncompressed size in bytes
|
||||||
offset: number, -- Absolute position of local header in ZIP
|
offset: number, -- Absolute position of local header in ZIP
|
||||||
timestamp: number, -- MS-DOS format timestamp
|
timestamp: number, -- MS-DOS format timestamp
|
||||||
crc: number, -- CRC32 checksum of uncompressed data
|
method: CompressionMethod, -- Method used to compress the file
|
||||||
isDirectory: boolean, -- Whether the entry is a directory or not
|
crc: number, -- CRC32 checksum of uncompressed data
|
||||||
parent: ZipEntry?, -- The parent of the current entry, nil for root
|
isDirectory: boolean, -- Whether the entry is a directory or not
|
||||||
children: { ZipEntry }, -- The children of the entry
|
parent: ZipEntry?, -- The parent of the current entry, nil for root
|
||||||
|
children: { ZipEntry }, -- The children of the entry
|
||||||
}
|
}
|
||||||
|
|
||||||
function ZipEntry.new(name: string, size: number, offset: number, timestamp: number, crc: number): ZipEntry
|
function ZipEntry.new(name: string, size: number, offset: number, timestamp: number, method: CompressionMethod?, crc: number): ZipEntry
|
||||||
return setmetatable(
|
return setmetatable(
|
||||||
{
|
{
|
||||||
name = name,
|
name = name,
|
||||||
size = size,
|
size = size,
|
||||||
offset = offset,
|
offset = offset,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
|
method = method,
|
||||||
crc = crc,
|
crc = crc,
|
||||||
isDirectory = string.sub(name, -1) == "/",
|
isDirectory = string.sub(name, -1) == "/",
|
||||||
parent = nil,
|
parent = nil,
|
||||||
|
@ -104,7 +113,7 @@ type ZipReaderInner = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ZipReader.new(data): ZipReader
|
function ZipReader.new(data): ZipReader
|
||||||
local root = ZipEntry.new("/", 0, 0, 0, 0)
|
local root = ZipEntry.new("/", 0, 0, 0, nil, 0)
|
||||||
root.isDirectory = true
|
root.isDirectory = true
|
||||||
|
|
||||||
local this = setmetatable(
|
local this = setmetatable(
|
||||||
|
@ -150,6 +159,7 @@ function ZipReader.parseCentralDirectory(self: ZipReader): ()
|
||||||
-- ------------------------------------------------
|
-- ------------------------------------------------
|
||||||
-- 0 4 Central directory entry signature
|
-- 0 4 Central directory entry signature
|
||||||
-- 8 2 General purpose bitflags
|
-- 8 2 General purpose bitflags
|
||||||
|
-- 10 2 Compression method (8 = DEFLATE)
|
||||||
-- 12 4 Last mod time/date
|
-- 12 4 Last mod time/date
|
||||||
-- 28 2 File name length (n)
|
-- 28 2 File name length (n)
|
||||||
-- 30 2 Extra field length (m)
|
-- 30 2 Extra field length (m)
|
||||||
|
@ -162,6 +172,7 @@ function ZipReader.parseCentralDirectory(self: ZipReader): ()
|
||||||
-- 46+n+m k Comment
|
-- 46+n+m k Comment
|
||||||
|
|
||||||
local _bitflags = buffer.readu16(self.data, pos + 8)
|
local _bitflags = buffer.readu16(self.data, pos + 8)
|
||||||
|
local compressionMethod = buffer.readu16(self.data, pos + 10)
|
||||||
local nameLength = buffer.readu16(self.data, pos + 28)
|
local nameLength = buffer.readu16(self.data, pos + 28)
|
||||||
local extraLength = buffer.readu16(self.data, pos + 30)
|
local extraLength = buffer.readu16(self.data, pos + 30)
|
||||||
local commentLength = buffer.readu16(self.data, pos + 32)
|
local commentLength = buffer.readu16(self.data, pos + 32)
|
||||||
|
@ -171,7 +182,7 @@ function ZipReader.parseCentralDirectory(self: ZipReader): ()
|
||||||
local offset = buffer.readu32(self.data, pos + 42)
|
local offset = buffer.readu32(self.data, pos + 42)
|
||||||
local name = buffer.readstring(self.data, pos + 46, nameLength)
|
local name = buffer.readstring(self.data, pos + 46, nameLength)
|
||||||
|
|
||||||
local entry = ZipEntry.new(name, size, offset, timestamp, crc)
|
local entry = ZipEntry.new(name, size, offset, timestamp, DECOMPRESSION_ROUTINES[compressionMethod].name, crc)
|
||||||
table.insert(self.entries, entry)
|
table.insert(self.entries, entry)
|
||||||
|
|
||||||
pos = pos + 46 + nameLength + extraLength + commentLength
|
pos = pos + 46 + nameLength + extraLength + commentLength
|
||||||
|
@ -215,7 +226,7 @@ function ZipReader.buildDirectoryTree(self: ZipReader): ()
|
||||||
else
|
else
|
||||||
-- Create new directory entry for intermediate paths or undefined
|
-- Create new directory entry for intermediate paths or undefined
|
||||||
-- parent directories in the ZIP
|
-- parent directories in the ZIP
|
||||||
local dir = ZipEntry.new(path .. "/", 0, 0, entry.timestamp, 0)
|
local dir = ZipEntry.new(path .. "/", 0, 0, entry.timestamp, nil, 0)
|
||||||
dir.isDirectory = true
|
dir.isDirectory = true
|
||||||
dir.parent = current
|
dir.parent = current
|
||||||
self.directories[path] = dir
|
self.directories[path] = dir
|
||||||
|
@ -363,12 +374,12 @@ function ZipReader.extract(self: ZipReader, entry: ZipEntry, options: Extraction
|
||||||
|
|
||||||
if optionsOrDefault.decompress then
|
if optionsOrDefault.decompress then
|
||||||
local compressionMethod = buffer.readu16(self.data, entry.offset + 8)
|
local compressionMethod = buffer.readu16(self.data, entry.offset + 8)
|
||||||
local decompress = DECOMPRESSION_ROUTINES[compressionMethod]
|
local algo = DECOMPRESSION_ROUTINES[compressionMethod]
|
||||||
if decompress == nil then
|
if algo == nil then
|
||||||
error(`Unsupported compression, ID: {compressionMethod}`)
|
error(`Unsupported compression, ID: {compressionMethod}`)
|
||||||
end
|
end
|
||||||
|
|
||||||
content = decompress(content, uncompressedSize, {
|
content = algo.decompress(content, uncompressedSize, {
|
||||||
expected = crcChecksum,
|
expected = crcChecksum,
|
||||||
skip = optionsOrDefault.skipCrcValidation,
|
skip = optionsOrDefault.skipCrcValidation,
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue