diff --git a/Sources/ZIPFoundation/Archive+ZIP64.swift b/Sources/ZIPFoundation/Archive+ZIP64.swift index 8b94b7c3..c0ee2d0b 100644 --- a/Sources/ZIPFoundation/Archive+ZIP64.swift +++ b/Sources/ZIPFoundation/Archive+ZIP64.swift @@ -13,10 +13,6 @@ import Foundation let zip64EOCDRecordStructSignature = 0x06064b50 let zip64EOCDLocatorStructSignature = 0x07064b50 -enum ExtraFieldHeaderID: UInt16 { - case zip64ExtendedInformation = 0x0001 -} - extension Archive { struct ZIP64EndOfCentralDirectory { let record: ZIP64EndOfCentralDirectoryRecord diff --git a/Sources/ZIPFoundation/Archive.swift b/Sources/ZIPFoundation/Archive.swift index 1f7fc0d6..567b79fc 100644 --- a/Sources/ZIPFoundation/Archive.swift +++ b/Sources/ZIPFoundation/Archive.swift @@ -116,6 +116,11 @@ public final class Archive: Sequence { case v45 = 45 } + enum ExtraFieldHeaderID: UInt16 { + case zip64ExtendedInformation = 0x0001 + case infoZIPUnicodePath = 0x7075 + } + struct EndOfCentralDirectoryRecord: DataSerializable { let endOfCentralDirectorySignature = UInt32(endOfCentralDirectoryStructSignature) let numberOfDisk: UInt16 diff --git a/Sources/ZIPFoundation/Entry+InfoZIP.swift b/Sources/ZIPFoundation/Entry+InfoZIP.swift new file mode 100644 index 00000000..022dbd03 --- /dev/null +++ b/Sources/ZIPFoundation/Entry+InfoZIP.swift @@ -0,0 +1,62 @@ +// +// Entry+InfoZIP.swift +// ZIPFoundation +// +// Copyright © 2017-2025 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors. +// Released under the MIT License. +// +// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information. +// + +import Foundation + +extension Entry { + + struct InfoZIPUnicodePath: ExtensibleDataField { + var headerID: UInt16 { Archive.ExtraFieldHeaderID.infoZIPUnicodePath.rawValue } + let dataSize: UInt16 + let version: UInt8 + let nameCRC32: UInt32 + let unicodeName: Data + } + + var infoZIPExtraField: InfoZIPUnicodePath? { + let extraField = self.localFileHeader.extraFields?.first { $0 is InfoZIPUnicodePath } + return extraField as? InfoZIPUnicodePath + } +} + +extension Entry.InfoZIPUnicodePath { + + static func scanForUnicodePath(in data: Data) -> Entry.InfoZIPUnicodePath? { + guard data.isEmpty == false else { return nil } + var offset = 0 + var headerID: UInt16 + var dataSize: UInt16 + let extraFieldLength = data.count + let headerSize = 4 + + while offset < extraFieldLength - headerSize { + headerID = data.scanValue(start: offset) + dataSize = data.scanValue(start: offset + 2) + let nextOffset = offset + headerSize + Int(dataSize) + guard nextOffset <= extraFieldLength else { return nil } + + if headerID == Archive.ExtraFieldHeaderID.infoZIPUnicodePath.rawValue { + let fieldData = data.subdata(in: offset..