Skip to content

Commit

Permalink
f
Browse files Browse the repository at this point in the history
  • Loading branch information
guzba committed Apr 10, 2024
1 parent 3690080 commit b1794d7
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 200 deletions.
321 changes: 161 additions & 160 deletions src/zippy/ziparchives.nim
Original file line number Diff line number Diff line change
Expand Up @@ -452,166 +452,167 @@ proc extractAll*(
finally:
reader.close()

proc createZipArchive*(
entries: sink OrderedTable[string, string]
): string {.raises: [ZippyError].} =

proc add16(dst: var string, v: int16 | uint16) =
dst.setLen(dst.len + 2)
var tmp = v
copyMem(dst[^2].addr, tmp.addr, 2)

proc add32(dst: var string, v: int32 | uint32) =
dst.setLen(dst.len + 4)
var tmp = v
copyMem(dst[^4].addr, tmp.addr, 4)

proc add64(dst: var string, v: int | int64 | uint | uint64) =
dst.setLen(dst.len + 8)
var tmp = v
copyMem(dst[^8].addr, tmp.addr, 8)

proc msdos(time: Time): (uint16, uint16) =
let
dt = time.local()
seconds = (dt.second div 2).uint16
minutes = dt.minute.uint16
hours = dt.hour.uint16
days = dt.monthday.uint16
months = dt.month.uint16
years = (max(0, dt.year - 1980)).uint16

var time = seconds
time = (minutes shl 5) or time
time = (hours shl 11) or time

var date = days
date = (months shl 5) or date
date = (years shl 9) or date

(time, date)

let (lastModifiedTime, lastModifiedDate) = msdos(getTime())

type ArchiveEntry = object
fileHeaderOffset: int
uncompressedLen: int
compressedLen: int
compressionMethod: uint16
uncompressedCrc32: uint32

var records: seq[(string, ArchiveEntry)]
for fileName in toSeq(entries.keys): # The entries table is modified so use toSeq
if fileName == "":
raise newException(ZippyError, "Invalid empty file name")
if fileName[0] == '/':
raise newException(ZippyError, "File paths must be relative")
if fileName.len > uint16.high.int:
raise newException(ZippyError, "File name len > uint16.high")
when (NimMajor, NimMinor, NimPatch) >= (1, 6, 0):
proc createZipArchive*(
entries: sink OrderedTable[string, string]
): string {.raises: [ZippyError].} =

proc add16(dst: var string, v: int16 | uint16) =
dst.setLen(dst.len + 2)
var tmp = v
copyMem(dst[^2].addr, tmp.addr, 2)

proc add32(dst: var string, v: int32 | uint32) =
dst.setLen(dst.len + 4)
var tmp = v
copyMem(dst[^4].addr, tmp.addr, 4)

proc add64(dst: var string, v: int | int64 | uint | uint64) =
dst.setLen(dst.len + 8)
var tmp = v
copyMem(dst[^8].addr, tmp.addr, 8)

proc msdos(time: Time): (uint16, uint16) =
let
dt = time.local()
seconds = (dt.second div 2).uint16
minutes = dt.minute.uint16
hours = dt.hour.uint16
days = dt.monthday.uint16
months = dt.month.uint16
years = (max(0, dt.year - 1980)).uint16

var time = seconds
time = (minutes shl 5) or time
time = (hours shl 11) or time

var date = days
date = (months shl 5) or date
date = (years shl 9) or date

(time, date)

let (lastModifiedTime, lastModifiedDate) = msdos(getTime())

type ArchiveEntry = object
fileHeaderOffset: int
uncompressedLen: int
compressedLen: int
compressionMethod: uint16
uncompressedCrc32: uint32

var contents: string
discard entries.pop(fileName, contents)
var records: seq[(string, ArchiveEntry)]
for fileName in toSeq(entries.keys): # The entries table is modified so use toSeq
if fileName == "":
raise newException(ZippyError, "Invalid empty file name")
if fileName[0] == '/':
raise newException(ZippyError, "File paths must be relative")
if fileName.len > uint16.high.int:
raise newException(ZippyError, "File name len > uint16.high")

var
compressed: string
compressionMethod: uint16
if contents == "":
discard
else:
compressed = compress(contents, BestSpeed, dfDeflate)
compressionMethod = 8

let uncompressedCrc32 = crc32(contents)

records.add((fileName, ArchiveEntry(
fileHeaderOffset: result.len,
uncompressedLen: contents.len,
compressedLen: compressed.len,
compressionMethod: compressionMethod,
uncompressedCrc32: uncompressedCrc32
)))

result.add32(fileHeaderSignature)
result.add16(45) # Min version to extract
result.add16(1.uint16 shl 11) # General purpose flags
result.add16(compressionMethod)
result.add16(lastModifiedTime)
result.add16(lastModifiedDate)
result.add32(uncompressedCrc32) # CRC-32 of uncompressed data
result.add32(uint32.high) # Compressed size (or 0xffffffff for ZIP64)
result.add32(uint32.high) # Uncompressed size (or 0xffffffff for ZIP64)
result.add16(cast[uint16](fileName.len)) # File name length
result.add16(20) # Extra field length

result.add(fileName)

result.add16(zip64ExtraFieldId)
result.add16(16)
result.add64(contents.len)
result.add64(compressed.len)

# result.add(compressed)
if compressed != "":
result.setLen(result.len + compressed.len)
copyMem(
result[result.len - compressed.len].addr,
compressed.cstring,
compressed.len
)
var contents: string
discard entries.pop(fileName, contents)

let centralDirectoryStart = result.len

for i in 0 ..< records.len:
let entry = records[i][1]
result.add32(centralDirectoryFileHeaderSignature)
result.add16(45) # Version made by
result.add16(45) # Min version to extract
result.add16(1.uint16 shl 11) # General purpose flags
result.add16(entry.compressionMethod)
result.add16(lastModifiedTime)
result.add16(lastModifiedDate)
result.add32(entry.uncompressedCrc32)
result.add32(uint32.high) # Compressed size (or 0xffffffff for ZIP64)
result.add32(uint32.high) # Uncompressed size (or 0xffffffff for ZIP64)
result.add16(cast[uint16](records[i][0].len)) # File name length
result.add16(28) # Extra field length
result.add16(0) # File comment length
result.add16(0) # Disk number where file starts
result.add16(0) # Internal file attributes
result.add32(0) # External file attributes
result.add32(uint32.high) # Relative offset of local file header (or 0xffffffff for ZIP64)

result.add(records[i][0])

result.add16(zip64ExtraFieldId)
result.add16(24)
result.add64(entry.uncompressedLen)
result.add64(entry.compressedLen)
result.add64(entry.fileHeaderOffset)

let centralDirectoryEnd = result.len

result.add32(zip64EndOfCentralDirectorySignature)
result.add64(44)
result.add16(45)
result.add16(45)
result.add32(0)
result.add32(0)
result.add64(records.len)
result.add64(records.len)
result.add64(centralDirectoryEnd - centralDirectoryStart)
result.add64(centralDirectoryStart)

result.add32(zip64EndOfCentralDirectoryLocatorSignature)
result.add32(0)
result.add64(centralDirectoryEnd)
result.add32(1)

result.add32(endOfCentralDirectorySignature)
result.add16(0) # Number of this disk
result.add16(0) # Disk where central directory starts
result.add16(uint16.high) # Number of central directory records on this disk (or 0xffff for ZIP64)
result.add16(uint16.high) # Total number of central directory records (or 0xffff for ZIP64)
result.add32(uint32.high) # Size of central directory (bytes) (or 0xffffffff for ZIP64)
result.add32(uint32.high) # Offset of start of central directory, relative to start of archive (or 0xffffffff for ZIP64)
result.add16(0)
var
compressed: string
compressionMethod: uint16
if contents == "":
discard
else:
compressed = compress(contents, BestSpeed, dfDeflate)
compressionMethod = 8

let uncompressedCrc32 = crc32(contents)

records.add((fileName, ArchiveEntry(
fileHeaderOffset: result.len,
uncompressedLen: contents.len,
compressedLen: compressed.len,
compressionMethod: compressionMethod,
uncompressedCrc32: uncompressedCrc32
)))

result.add32(fileHeaderSignature)
result.add16(45) # Min version to extract
result.add16(1.uint16 shl 11) # General purpose flags
result.add16(compressionMethod)
result.add16(lastModifiedTime)
result.add16(lastModifiedDate)
result.add32(uncompressedCrc32) # CRC-32 of uncompressed data
result.add32(uint32.high) # Compressed size (or 0xffffffff for ZIP64)
result.add32(uint32.high) # Uncompressed size (or 0xffffffff for ZIP64)
result.add16(cast[uint16](fileName.len)) # File name length
result.add16(20) # Extra field length

result.add(fileName)

result.add16(zip64ExtraFieldId)
result.add16(16)
result.add64(contents.len)
result.add64(compressed.len)

# result.add(compressed)
if compressed != "":
result.setLen(result.len + compressed.len)
copyMem(
result[result.len - compressed.len].addr,
compressed.cstring,
compressed.len
)

let centralDirectoryStart = result.len

for i in 0 ..< records.len:
let entry = records[i][1]
result.add32(centralDirectoryFileHeaderSignature)
result.add16(45) # Version made by
result.add16(45) # Min version to extract
result.add16(1.uint16 shl 11) # General purpose flags
result.add16(entry.compressionMethod)
result.add16(lastModifiedTime)
result.add16(lastModifiedDate)
result.add32(entry.uncompressedCrc32)
result.add32(uint32.high) # Compressed size (or 0xffffffff for ZIP64)
result.add32(uint32.high) # Uncompressed size (or 0xffffffff for ZIP64)
result.add16(cast[uint16](records[i][0].len)) # File name length
result.add16(28) # Extra field length
result.add16(0) # File comment length
result.add16(0) # Disk number where file starts
result.add16(0) # Internal file attributes
result.add32(0) # External file attributes
result.add32(uint32.high) # Relative offset of local file header (or 0xffffffff for ZIP64)

result.add(records[i][0])

result.add16(zip64ExtraFieldId)
result.add16(24)
result.add64(entry.uncompressedLen)
result.add64(entry.compressedLen)
result.add64(entry.fileHeaderOffset)

let centralDirectoryEnd = result.len

result.add32(zip64EndOfCentralDirectorySignature)
result.add64(44)
result.add16(45)
result.add16(45)
result.add32(0)
result.add32(0)
result.add64(records.len)
result.add64(records.len)
result.add64(centralDirectoryEnd - centralDirectoryStart)
result.add64(centralDirectoryStart)

result.add32(zip64EndOfCentralDirectoryLocatorSignature)
result.add32(0)
result.add64(centralDirectoryEnd)
result.add32(1)

result.add32(endOfCentralDirectorySignature)
result.add16(0) # Number of this disk
result.add16(0) # Disk where central directory starts
result.add16(uint16.high) # Number of central directory records on this disk (or 0xffff for ZIP64)
result.add16(uint16.high) # Total number of central directory records (or 0xffff for ZIP64)
result.add32(uint32.high) # Size of central directory (bytes) (or 0xffffffff for ZIP64)
result.add32(uint32.high) # Offset of start of central directory, relative to start of archive (or 0xffffffff for ZIP64)
result.add16(0)
Loading

0 comments on commit b1794d7

Please sign in to comment.