lottie-ios 3.4.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +1 -0
- package/.github/workflows/main.yml +7 -7
- package/Gemfile.lock +1 -1
- package/Lottie.xcodeproj/project.pbxproj +286 -68
- package/Lottie.xcworkspace/xcshareddata/swiftpm/Package.resolved +18 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/Lottie.xcworkspace/xcuserdata/calstephens.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +3 -24
- package/Package.swift +2 -2
- package/README.md +10 -3
- package/Rakefile +1 -1
- package/Sources/Private/CoreAnimation/Animations/CALayer+addAnimation.swift +6 -9
- package/Sources/Private/CoreAnimation/Animations/CustomPathAnimation.swift +42 -3
- package/Sources/Private/CoreAnimation/Animations/EllipseAnimation.swift +5 -17
- package/Sources/Private/CoreAnimation/Animations/GradientAnimations.swift +25 -17
- package/Sources/Private/CoreAnimation/Animations/LayerProperty.swift +2 -2
- package/Sources/Private/CoreAnimation/Animations/OpacityAnimation.swift +1 -1
- package/Sources/Private/CoreAnimation/Animations/RectangleAnimation.swift +9 -20
- package/Sources/Private/CoreAnimation/Animations/ShapeAnimation.swift +39 -25
- package/Sources/Private/CoreAnimation/Animations/StarAnimation.swift +13 -32
- package/Sources/Private/CoreAnimation/Animations/StrokeAnimation.swift +4 -4
- package/Sources/Private/CoreAnimation/Animations/TransformAnimations.swift +13 -13
- package/Sources/Private/CoreAnimation/CoreAnimationLayer.swift +4 -4
- package/Sources/Private/CoreAnimation/Extensions/KeyframeGroup+exactlyOneKeyframe.swift +4 -12
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combined.swift +222 -0
- package/Sources/Private/CoreAnimation/Layers/AnimationLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/CALayer+setupLayerHierarchy.swift +8 -6
- package/Sources/Private/CoreAnimation/Layers/GradientRenderLayer.swift +13 -10
- package/Sources/Private/CoreAnimation/Layers/InfiniteOpaqueAnimationLayer.swift +56 -0
- package/Sources/Private/CoreAnimation/Layers/LayerModel+makeAnimationLayer.swift +1 -1
- package/Sources/Private/CoreAnimation/Layers/PreCompLayer.swift +2 -3
- package/Sources/Private/CoreAnimation/Layers/RepeaterLayer.swift +9 -9
- package/Sources/Private/CoreAnimation/Layers/ShapeItemLayer.swift +9 -3
- package/Sources/Private/CoreAnimation/Layers/ShapeLayer.swift +7 -18
- package/Sources/Private/CoreAnimation/ValueProviderStore.swift +2 -3
- package/Sources/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.swift +5 -5
- package/Sources/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.swift +3 -3
- package/Sources/Private/MainThread/LayerContainers/CompLayers/SolidCompositionLayer.swift +2 -2
- package/Sources/Private/MainThread/LayerContainers/MainThreadAnimationLayer.swift +7 -6
- package/Sources/Private/MainThread/LayerContainers/Utility/InvertedMatteLayer.swift +1 -3
- package/Sources/Private/MainThread/LayerContainers/Utility/LayerTransformNode.swift +10 -10
- package/Sources/Private/MainThread/NodeRenderSystem/Extensions/ItemsExtension.swift +7 -0
- package/Sources/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/GroupInterpolator.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/RoundedCornersNode.swift +85 -0
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/ModifierNodes/TrimPathNode.swift +4 -4
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientFillRenderer.swift +11 -12
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/GradientStrokeRenderer.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/LegacyGradientFillRenderer.swift +10 -11
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/Renderables/StrokeRenderer.swift +1 -1
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/EllipseNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/PolygonNode.swift +5 -5
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/RectNode.swift +3 -3
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/PathNodes/StarNode.swift +9 -9
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderContainers/GroupNode.swift +14 -14
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/FillNode.swift +2 -2
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientFillNode.swift +3 -3
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/GradientStrokeNode.swift +10 -10
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/RenderNodes/StrokeNode.swift +14 -14
- package/Sources/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.swift +11 -11
- package/Sources/Private/MainThread/NodeRenderSystem/RenderLayers/ShapeContainerLayer.swift +1 -1
- package/Sources/Private/Model/DotLottie/DotLottieAnimation.swift +52 -0
- package/Sources/Private/Model/DotLottie/DotLottieConfiguration.swift +15 -0
- package/Sources/Private/Model/DotLottie/DotLottieImageProvider.swift +73 -0
- package/Sources/Private/Model/DotLottie/DotLottieManifest.swift +53 -0
- package/Sources/Private/Model/DotLottie/DotLottieUtils.swift +58 -0
- package/Sources/Private/Model/DotLottie/Zip/Data+Compression.swift +134 -0
- package/Sources/Private/Model/DotLottie/Zip/Data+Serialization.swift +87 -0
- package/Sources/Private/Model/DotLottie/Zip/FileManager+ZIP.swift +130 -0
- package/Sources/Private/Model/DotLottie/Zip/ZipArchive.swift +474 -0
- package/Sources/Private/Model/DotLottie/Zip/ZipEntry+Serialization.swift +189 -0
- package/Sources/Private/Model/DotLottie/Zip/ZipEntry+ZIP64.swift +179 -0
- package/Sources/Private/Model/DotLottie/Zip/ZipEntry.swift +227 -0
- package/Sources/Private/Model/Extensions/Bundle.swift +26 -0
- package/Sources/Private/Model/Keyframes/KeyframeData.swift +12 -12
- package/Sources/Private/Model/Keyframes/KeyframeGroup.swift +9 -2
- package/Sources/Private/Model/Layers/LayerModel.swift +1 -1
- package/Sources/Private/Model/Layers/PreCompLayerModel.swift +3 -3
- package/Sources/Private/Model/Layers/TextLayerModel.swift +1 -1
- package/Sources/Private/Model/Objects/DashPattern.swift +2 -2
- package/Sources/Private/Model/Objects/Mask.swift +10 -8
- package/Sources/Private/Model/Objects/Transform.swift +39 -35
- package/Sources/Private/Model/ShapeItems/Ellipse.swift +6 -6
- package/Sources/Private/Model/ShapeItems/Fill.swift +6 -6
- package/Sources/Private/Model/ShapeItems/GradientFill.swift +15 -15
- package/Sources/Private/Model/ShapeItems/GradientStroke.swift +19 -19
- package/Sources/Private/Model/ShapeItems/Rectangle.swift +9 -9
- package/Sources/Private/Model/ShapeItems/Repeater.swift +37 -32
- package/Sources/Private/Model/ShapeItems/RoundedCorners.swift +47 -0
- package/Sources/Private/Model/ShapeItems/ShapeItem.swift +4 -0
- package/Sources/Private/Model/ShapeItems/ShapeTransform.swift +34 -28
- package/Sources/Private/Model/ShapeItems/Star.swift +21 -21
- package/Sources/Private/Model/ShapeItems/Stroke.swift +10 -10
- package/Sources/Private/Model/ShapeItems/Trim.swift +9 -9
- package/Sources/Private/Model/Text/Font.swift +1 -1
- package/Sources/Private/Model/Text/TextAnimator.swift +33 -33
- package/Sources/Private/Model/Text/TextDocument.swift +8 -8
- package/Sources/Private/RootAnimationLayer.swift +4 -4
- package/Sources/Private/Utility/Debugging/LayerDebugging.swift +12 -18
- package/Sources/Private/Utility/Extensions/AnimationKeypathExtension.swift +2 -2
- package/Sources/Private/Utility/Extensions/CGColor+RGB.swift +12 -7
- package/Sources/Private/Utility/Interpolatable/InterpolatableExtensions.swift +1 -1
- package/Sources/Private/Utility/Interpolatable/KeyframeGroup+Extensions.swift +4 -4
- package/Sources/Private/Utility/Primitives/BezierPathRoundExtension.swift +147 -0
- package/Sources/Private/Utility/Primitives/ColorExtension.swift +8 -11
- package/Sources/Private/Utility/Primitives/VectorsExtensions.swift +22 -22
- package/Sources/{Private/Model/Animation.swift → Public/Animation/LottieAnimation.swift} +8 -8
- package/Sources/Public/Animation/{AnimationPublic.swift → LottieAnimationHelpers.swift} +62 -27
- package/Sources/Public/Animation/{AnimationView.swift → LottieAnimationView.swift} +122 -26
- package/Sources/Public/Animation/LottieAnimationViewInitializers.swift +222 -0
- package/Sources/Public/AnimationCache/AnimationCacheProvider.swift +4 -4
- package/Sources/Public/AnimationCache/DefaultAnimationCache.swift +54 -0
- package/Sources/Public/AnimationCache/LRUAnimationCache.swift +4 -52
- package/Sources/Public/AnimationCache/LottieAnimationCache.swift +15 -0
- package/Sources/Public/DotLottie/Cache/DotLottieCache.swift +53 -0
- package/Sources/Public/DotLottie/Cache/DotLottieCacheProvider.swift +23 -0
- package/Sources/Public/DotLottie/DotLottieFile.swift +161 -0
- package/Sources/Public/DotLottie/DotLottieFileHelpers.swift +265 -0
- package/Sources/Public/DynamicProperties/AnimationKeypath.swift +3 -3
- package/Sources/Public/DynamicProperties/AnyValueProvider.swift +2 -2
- package/Sources/Public/DynamicProperties/ValueProviders/ColorValueProvider.swift +10 -10
- package/Sources/Public/DynamicProperties/ValueProviders/FloatValueProvider.swift +4 -4
- package/Sources/Public/DynamicProperties/ValueProviders/GradientValueProvider.swift +5 -5
- package/Sources/Public/DynamicProperties/ValueProviders/PointValueProvider.swift +2 -2
- package/Sources/Public/DynamicProperties/ValueProviders/SizeValueProvider.swift +2 -2
- package/Sources/Public/FontProvider/AnimationFontProvider.swift +1 -1
- package/Sources/Public/ImageProvider/AnimationImageProvider.swift +2 -2
- package/Sources/Public/Keyframes/Interpolatable.swift +17 -17
- package/Sources/Public/Keyframes/Keyframe.swift +10 -10
- package/Sources/Public/LottieConfiguration.swift +25 -7
- package/Sources/Public/Primitives/{Color.swift → LottieColor.swift} +3 -3
- package/Sources/Public/Primitives/Vectors.swift +4 -4
- package/Sources/Public/TextProvider/AnimationTextProvider.swift +1 -1
- package/Sources/Public/iOS/AnimatedButton.swift +1 -1
- package/Sources/Public/iOS/AnimatedControl.swift +6 -6
- package/Sources/Public/iOS/AnimatedSwitch.swift +1 -1
- package/Sources/Public/iOS/Compatibility/CompatibleAnimationView.swift +8 -8
- package/Sources/Public/iOS/{AnimationViewBase.swift → LottieAnimationViewBase.swift} +4 -4
- package/Sources/Public/iOS/UIColorExtension.swift +2 -2
- package/Sources/Public/macOS/{AnimationViewBase.macOS.swift → LottieAnimationViewBase.macOS.swift} +4 -4
- package/Tests/AnimationCacheProviderTests.swift +40 -0
- package/Tests/AnimationKeypathTests.swift +20 -13
- package/Tests/AnimationViewTests.swift +64 -0
- package/Tests/AutomaticEngineTests.swift +11 -2
- package/Tests/ParsingTests.swift +4 -2
- package/Tests/PerformanceTests.swift +9 -9
- package/Tests/SnapshotConfiguration.swift +7 -7
- package/Tests/SnapshotTests.swift +57 -27
- package/Tests/ValueProvidersTests.swift +9 -6
- package/lottie-ios.podspec +4 -4
- package/package.json +2 -2
- package/script/test-carthage/Cartfile +1 -1
- package/script/test-carthage/CarthageTest/ViewController.swift +1 -1
- package/script/test-carthage/CarthageTest-macOS/ViewController.swift +1 -1
- package/Sources/Private/CoreAnimation/Extensions/Keyframes+combinedIfPossible.swift +0 -154
- package/Sources/Public/Animation/AnimationViewInitializers.swift +0 -109
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw== +0 -1
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~7IO4FqH8-VUsSfuA0_wMGUgfGdV7MwzpkzjwWbiB50jXZKQRHtU4G0A7ZXTvsR4mDdrWiawpNC_eriOALfZ7Og== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~ClRW5-6rvrgZHHbrPS1VEREaxelpVcKlERPzSa3f2rtFNXdoqATxFCsPge3a_nO4mhhtpmTmbYvyfI-obAu8Qg== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~Jr2dFGcJc1188nMoMha82z9wM99lCLm4MKzhjfVVjIxKeCZVGWGZH3HgJFNkTpozdk5p1u5o91dCiY4-cR1Zhg== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~MBDeCFoahVzzmtSQrVhhy9VMJV6CmcnUkFya_qwe0XsMFscmY2nS8dxrTEYkY16sH68sEH7WVCCzTuW1z1Hocw== +0 -1
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~U4plJajEDM_uSZdglhDyj6gQO63mbeKxYP9X94aIaOtL0yErtfQnGz5SgsqQ0ToSZcJj6Ao9Wx-nesZVwBTRuw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~UsLY5O-vDMEHEe3bF8XJvkKkYxdsKlIytgJW7zgNuetw53fG3SssfZiiMfbuYnOtvvvYBXTwpbz07V6Czp029Q== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~cpg4cb7H0TaehHy4a-xINJMFfBM2_AEHal_0tIX8ymNVJPsjlCysi3-Cad0Ca_SvuGwVM7ONF55OBUuC9YKZvA== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~cuHjdTCuciVumvEpvozxwDj2wAdgWe13bzd1pUAGN45-WOgY0kIid9aUlBX675OnS-xNEc_pyQWo0RO1dKAjcw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~f_-bfn-KRHHFg39_MtwgBulEAuWH6F05yqGYydXhil6kqZ51eAoRX6tsiqOr0Oa6eL3dK1tcCBD1bWX5orCZhw== +0 -1
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~jdIx7vWS6ej1cqYcbCS4KyZErSMF13ELR95YwClVc98tIXcsglh7KuGvI1gIxEPDtPXQpfC3XijIAGn1quL8Dw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~lz3e0YeBa8TvMBSSymToh--gc6zznUIdH2jO0AJ4so5OPNdw6wpYmJebhaENGsRoD3beUXvlyD5f7_WeZrzyNQ== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~u_5dWbliYJ__YkyUCDFjdxiFds9M8Epr_RvbU1rIRCbBQdhRJ_TUBXXcL_Qq-wVp4umNLTOzud4OpQItSpO6Rg== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/data.0~v-YGzhC2l_icsAsyp1XAbEWYwxNook-rARAWlVZINioEsgT9LGvhg2oh78nYqoeH78m1fihr5HCcGNfF7SQj0g== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~7IO4FqH8-VUsSfuA0_wMGUgfGdV7MwzpkzjwWbiB50jXZKQRHtU4G0A7ZXTvsR4mDdrWiawpNC_eriOALfZ7Og== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~ClRW5-6rvrgZHHbrPS1VEREaxelpVcKlERPzSa3f2rtFNXdoqATxFCsPge3a_nO4mhhtpmTmbYvyfI-obAu8Qg== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~Jr2dFGcJc1188nMoMha82z9wM99lCLm4MKzhjfVVjIxKeCZVGWGZH3HgJFNkTpozdk5p1u5o91dCiY4-cR1Zhg== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~MBDeCFoahVzzmtSQrVhhy9VMJV6CmcnUkFya_qwe0XsMFscmY2nS8dxrTEYkY16sH68sEH7WVCCzTuW1z1Hocw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~U4plJajEDM_uSZdglhDyj6gQO63mbeKxYP9X94aIaOtL0yErtfQnGz5SgsqQ0ToSZcJj6Ao9Wx-nesZVwBTRuw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~UsLY5O-vDMEHEe3bF8XJvkKkYxdsKlIytgJW7zgNuetw53fG3SssfZiiMfbuYnOtvvvYBXTwpbz07V6Czp029Q== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~cpg4cb7H0TaehHy4a-xINJMFfBM2_AEHal_0tIX8ymNVJPsjlCysi3-Cad0Ca_SvuGwVM7ONF55OBUuC9YKZvA== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~cuHjdTCuciVumvEpvozxwDj2wAdgWe13bzd1pUAGN45-WOgY0kIid9aUlBX675OnS-xNEc_pyQWo0RO1dKAjcw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~f_-bfn-KRHHFg39_MtwgBulEAuWH6F05yqGYydXhil6kqZ51eAoRX6tsiqOr0Oa6eL3dK1tcCBD1bWX5orCZhw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~jdIx7vWS6ej1cqYcbCS4KyZErSMF13ELR95YwClVc98tIXcsglh7KuGvI1gIxEPDtPXQpfC3XijIAGn1quL8Dw== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~lz3e0YeBa8TvMBSSymToh--gc6zznUIdH2jO0AJ4so5OPNdw6wpYmJebhaENGsRoD3beUXvlyD5f7_WeZrzyNQ== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~u_5dWbliYJ__YkyUCDFjdxiFds9M8Epr_RvbU1rIRCbBQdhRJ_TUBXXcL_Qq-wVp4umNLTOzud4OpQItSpO6Rg== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Data/refs.0~v-YGzhC2l_icsAsyp1XAbEWYwxNook-rARAWlVZINioEsgT9LGvhg2oh78nYqoeH78m1fihr5HCcGNfF7SQj0g== +0 -0
- package/Tests/Artifacts/LottieTests.xcresult/Info.plist +0 -29
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FileManager+ZIP.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
extension FileManager {
|
|
14
|
+
|
|
15
|
+
/// The default permissions for newly added entries.
|
|
16
|
+
static let defaultFilePermissions = UInt16(0o644)
|
|
17
|
+
|
|
18
|
+
class func attributes(from entry: ZipEntry) -> [FileAttributeKey: Any] {
|
|
19
|
+
let centralDirectoryStructure = entry.centralDirectoryStructure
|
|
20
|
+
let fileTime = centralDirectoryStructure.lastModFileTime
|
|
21
|
+
let fileDate = centralDirectoryStructure.lastModFileDate
|
|
22
|
+
let defaultPermissions = defaultFilePermissions
|
|
23
|
+
var attributes = [.posixPermissions: defaultPermissions] as [FileAttributeKey: Any]
|
|
24
|
+
// Certain keys are not yet supported in swift-corelibs
|
|
25
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
26
|
+
attributes[.modificationDate] = Date(dateTime: (fileDate, fileTime))
|
|
27
|
+
#endif
|
|
28
|
+
let externalFileAttributes = centralDirectoryStructure.externalFileAttributes
|
|
29
|
+
let permissions = permissions(for: externalFileAttributes)
|
|
30
|
+
attributes[.posixPermissions] = NSNumber(value: permissions)
|
|
31
|
+
return attributes
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class func permissions(for externalFileAttributes: UInt32) -> UInt16 {
|
|
35
|
+
let permissions = mode_t(externalFileAttributes >> 16) & ~S_IFMT
|
|
36
|
+
let defaultPermissions = defaultFilePermissions
|
|
37
|
+
return permissions == 0 ? defaultPermissions : UInt16(permissions)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Unzips the contents at the specified source URL to the destination URL.
|
|
41
|
+
///
|
|
42
|
+
/// - Parameters:
|
|
43
|
+
/// - sourceURL: The file URL pointing to an existing ZIP file.
|
|
44
|
+
/// - destinationURL: The file URL that identifies the destination directory of the unzip operation.
|
|
45
|
+
/// - Throws: Throws an error if the source item does not exist or the destination URL is not writable.
|
|
46
|
+
func unzipItem(at sourceURL: URL, to destinationURL: URL) throws {
|
|
47
|
+
guard (try? sourceURL.checkResourceIsReachable()) == true else {
|
|
48
|
+
throw CocoaError(.fileReadNoSuchFile, userInfo: [NSFilePathErrorKey: sourceURL.path])
|
|
49
|
+
}
|
|
50
|
+
guard let archive = ZipArchive(url: sourceURL) else {
|
|
51
|
+
throw ZipArchive.ArchiveError.unreadableArchive
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for entry in archive {
|
|
55
|
+
let path = entry.path
|
|
56
|
+
let entryURL = destinationURL.appendingPathComponent(path)
|
|
57
|
+
guard entryURL.isContained(in: destinationURL) else {
|
|
58
|
+
throw CocoaError(
|
|
59
|
+
.fileReadInvalidFileName,
|
|
60
|
+
userInfo: [NSFilePathErrorKey: entryURL.path])
|
|
61
|
+
}
|
|
62
|
+
let crc32: UInt32 = try archive.extract(entry, to: entryURL)
|
|
63
|
+
|
|
64
|
+
func verifyChecksumIfNecessary() throws {
|
|
65
|
+
if crc32 != entry.checksum {
|
|
66
|
+
throw ZipArchive.ArchiveError.invalidCRC32
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
try verifyChecksumIfNecessary()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// MARK: - Helpers
|
|
74
|
+
|
|
75
|
+
func createParentDirectoryStructure(for url: URL) throws {
|
|
76
|
+
let parentDirectoryURL = url.deletingLastPathComponent()
|
|
77
|
+
try createDirectory(at: parentDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
extension Date {
|
|
83
|
+
fileprivate init(dateTime: (UInt16, UInt16)) {
|
|
84
|
+
var msdosDateTime = Int(dateTime.0)
|
|
85
|
+
msdosDateTime <<= 16
|
|
86
|
+
msdosDateTime |= Int(dateTime.1)
|
|
87
|
+
var unixTime = tm()
|
|
88
|
+
unixTime.tm_sec = Int32((msdosDateTime & 31) * 2)
|
|
89
|
+
unixTime.tm_min = Int32((msdosDateTime >> 5) & 63)
|
|
90
|
+
unixTime.tm_hour = Int32((Int(dateTime.1) >> 11) & 31)
|
|
91
|
+
unixTime.tm_mday = Int32((msdosDateTime >> 16) & 31)
|
|
92
|
+
unixTime.tm_mon = Int32((msdosDateTime >> 21) & 15)
|
|
93
|
+
unixTime.tm_mon -= 1 // UNIX time struct month entries are zero based.
|
|
94
|
+
unixTime.tm_year = Int32(1980 + (msdosDateTime >> 25))
|
|
95
|
+
unixTime.tm_year -= 1900 // UNIX time structs count in "years since 1900".
|
|
96
|
+
let time = timegm(&unixTime)
|
|
97
|
+
self = Date(timeIntervalSince1970: TimeInterval(time))
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#if swift(>=4.2)
|
|
102
|
+
#else
|
|
103
|
+
|
|
104
|
+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
105
|
+
#else
|
|
106
|
+
|
|
107
|
+
// The swift-corelibs-foundation version of NSError.swift was missing a convenience method to create
|
|
108
|
+
// error objects from error codes. (https://github.com/apple/swift-corelibs-foundation/pull/1420)
|
|
109
|
+
// We have to provide an implementation for non-Darwin platforms using Swift versions < 4.2.
|
|
110
|
+
|
|
111
|
+
extension CocoaError {
|
|
112
|
+
fileprivate static func error(_ code: CocoaError.Code, userInfo: [AnyHashable: Any]? = nil, url: URL? = nil) -> Error {
|
|
113
|
+
var info: [String: Any] = userInfo as? [String: Any] ?? [:]
|
|
114
|
+
if let url = url {
|
|
115
|
+
info[NSURLErrorKey] = url
|
|
116
|
+
}
|
|
117
|
+
return NSError(domain: NSCocoaErrorDomain, code: code.rawValue, userInfo: info)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#endif
|
|
122
|
+
#endif
|
|
123
|
+
|
|
124
|
+
extension URL {
|
|
125
|
+
fileprivate func isContained(in parentDirectoryURL: URL) -> Bool {
|
|
126
|
+
// Ensure this URL is contained in the passed in URL
|
|
127
|
+
let parentDirectoryURL = URL(fileURLWithPath: parentDirectoryURL.path, isDirectory: true).standardized
|
|
128
|
+
return standardized.absoluteString.hasPrefix(parentDirectoryURL.absoluteString)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Archive.swift
|
|
3
|
+
// ZIPFoundation
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2017-2021 Thomas Zoechling, https://www.peakstep.com and the ZIP Foundation project authors.
|
|
6
|
+
// Released under the MIT License.
|
|
7
|
+
//
|
|
8
|
+
// See https://github.com/weichsel/ZIPFoundation/blob/master/LICENSE for license information.
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
import Foundation
|
|
12
|
+
|
|
13
|
+
// MARK: - ZipArchive
|
|
14
|
+
|
|
15
|
+
final class ZipArchive: Sequence {
|
|
16
|
+
|
|
17
|
+
// MARK: Lifecycle
|
|
18
|
+
|
|
19
|
+
/// Initializes a new ZIP `Archive`.
|
|
20
|
+
///
|
|
21
|
+
/// You can use this initalizer to create new archive files or to read and update existing ones.
|
|
22
|
+
/// - Parameters:
|
|
23
|
+
/// - url: File URL to the receivers backing file.
|
|
24
|
+
/// - Returns: An archive initialized with a backing file at the passed in file URL and the given access mode
|
|
25
|
+
/// or `nil` if the following criteria are not met:
|
|
26
|
+
init?(url: URL) {
|
|
27
|
+
self.url = url
|
|
28
|
+
guard let config = ZipArchive.makeBackingConfiguration(for: url) else { return nil }
|
|
29
|
+
archiveFile = config.file
|
|
30
|
+
endOfCentralDirectoryRecord = config.endOfCentralDirectoryRecord
|
|
31
|
+
zip64EndOfCentralDirectory = config.zip64EndOfCentralDirectory
|
|
32
|
+
setvbuf(archiveFile, nil, _IOFBF, Int(Self.defaultPOSIXBufferSize))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
deinit {
|
|
36
|
+
fclose(self.archiveFile)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// MARK: Internal
|
|
40
|
+
|
|
41
|
+
typealias LocalFileHeader = ZipEntry.LocalFileHeader
|
|
42
|
+
typealias DataDescriptor = ZipEntry.DefaultDataDescriptor
|
|
43
|
+
typealias ZIP64DataDescriptor = ZipEntry.ZIP64DataDescriptor
|
|
44
|
+
typealias CentralDirectoryStructure = ZipEntry.CentralDirectoryStructure
|
|
45
|
+
|
|
46
|
+
/// An error that occurs during reading, creating or updating a ZIP file.
|
|
47
|
+
enum ArchiveError: Error {
|
|
48
|
+
/// Thrown when an archive file is either damaged or inaccessible.
|
|
49
|
+
case unreadableArchive
|
|
50
|
+
/// Thrown when an archive is either opened with AccessMode.read or the destination file is unwritable.
|
|
51
|
+
case unwritableArchive
|
|
52
|
+
/// Thrown when the path of an `Entry` cannot be stored in an archive.
|
|
53
|
+
case invalidEntryPath
|
|
54
|
+
/// Thrown when an `Entry` can't be stored in the archive with the proposed compression method.
|
|
55
|
+
case invalidCompressionMethod
|
|
56
|
+
/// Thrown when the stored checksum of an `Entry` doesn't match the checksum during reading.
|
|
57
|
+
case invalidCRC32
|
|
58
|
+
/// Thrown when an extract, add or remove operation was canceled.
|
|
59
|
+
case cancelledOperation
|
|
60
|
+
/// Thrown when an extract operation was called with zero or negative `bufferSize` parameter.
|
|
61
|
+
case invalidBufferSize
|
|
62
|
+
/// Thrown when uncompressedSize/compressedSize exceeds `Int64.max` (Imposed by file API).
|
|
63
|
+
case invalidEntrySize
|
|
64
|
+
/// Thrown when the offset of local header data exceeds `Int64.max` (Imposed by file API).
|
|
65
|
+
case invalidLocalHeaderDataOffset
|
|
66
|
+
/// Thrown when the size of local header exceeds `Int64.max` (Imposed by file API).
|
|
67
|
+
case invalidLocalHeaderSize
|
|
68
|
+
/// Thrown when the offset of central directory exceeds `Int64.max` (Imposed by file API).
|
|
69
|
+
case invalidCentralDirectoryOffset
|
|
70
|
+
/// Thrown when the size of central directory exceeds `UInt64.max` (Imposed by ZIP specification).
|
|
71
|
+
case invalidCentralDirectorySize
|
|
72
|
+
/// Thrown when number of entries in central directory exceeds `UInt64.max` (Imposed by ZIP specification).
|
|
73
|
+
case invalidCentralDirectoryEntryCount
|
|
74
|
+
/// Thrown when an archive does not contain the required End of Central Directory Record.
|
|
75
|
+
case missingEndOfCentralDirectoryRecord
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
struct EndOfCentralDirectoryRecord: DataSerializable {
|
|
79
|
+
let endOfCentralDirectorySignature = UInt32(endOfCentralDirectoryStructSignature)
|
|
80
|
+
let numberOfDisk: UInt16
|
|
81
|
+
let numberOfDiskStart: UInt16
|
|
82
|
+
let totalNumberOfEntriesOnDisk: UInt16
|
|
83
|
+
let totalNumberOfEntriesInCentralDirectory: UInt16
|
|
84
|
+
let sizeOfCentralDirectory: UInt32
|
|
85
|
+
let offsetToStartOfCentralDirectory: UInt32
|
|
86
|
+
let zipFileCommentLength: UInt16
|
|
87
|
+
let zipFileCommentData: Data
|
|
88
|
+
static let size = 22
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// MARK: - Helpers
|
|
92
|
+
|
|
93
|
+
typealias EndOfCentralDirectoryStructure = (EndOfCentralDirectoryRecord, ZIP64EndOfCentralDirectory?)
|
|
94
|
+
|
|
95
|
+
struct BackingConfiguration {
|
|
96
|
+
let file: FILEPointer
|
|
97
|
+
let endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
|
|
98
|
+
let zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
|
|
99
|
+
|
|
100
|
+
init(
|
|
101
|
+
file: FILEPointer,
|
|
102
|
+
endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord,
|
|
103
|
+
zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?)
|
|
104
|
+
{
|
|
105
|
+
self.file = file
|
|
106
|
+
self.endOfCentralDirectoryRecord = endOfCentralDirectoryRecord
|
|
107
|
+
self.zip64EndOfCentralDirectory = zip64EndOfCentralDirectory
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static let defaultPOSIXBufferSize = Int(16 * 1024)
|
|
112
|
+
static let minEndOfCentralDirectoryOffset = Int64(22)
|
|
113
|
+
static let endOfCentralDirectoryStructSignature = 0x06054b50
|
|
114
|
+
|
|
115
|
+
/// The default chunk size when reading entry data from an archive.
|
|
116
|
+
static let defaultReadChunkSize = Int(16 * 1024)
|
|
117
|
+
|
|
118
|
+
/// URL of an Archive's backing file.
|
|
119
|
+
let url: URL
|
|
120
|
+
var archiveFile: FILEPointer
|
|
121
|
+
var endOfCentralDirectoryRecord: EndOfCentralDirectoryRecord
|
|
122
|
+
var zip64EndOfCentralDirectory: ZIP64EndOfCentralDirectory?
|
|
123
|
+
|
|
124
|
+
var totalNumberOfEntriesInCentralDirectory: UInt64 {
|
|
125
|
+
zip64EndOfCentralDirectory?.record.totalNumberOfEntriesInCentralDirectory
|
|
126
|
+
?? UInt64(endOfCentralDirectoryRecord.totalNumberOfEntriesInCentralDirectory)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
var sizeOfCentralDirectory: UInt64 {
|
|
130
|
+
zip64EndOfCentralDirectory?.record.sizeOfCentralDirectory
|
|
131
|
+
?? UInt64(endOfCentralDirectoryRecord.sizeOfCentralDirectory)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
var offsetToStartOfCentralDirectory: UInt64 {
|
|
135
|
+
zip64EndOfCentralDirectory?.record.offsetToStartOfCentralDirectory
|
|
136
|
+
?? UInt64(endOfCentralDirectoryRecord.offsetToStartOfCentralDirectory)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static func scanForEndOfCentralDirectoryRecord(in file: FILEPointer)
|
|
140
|
+
-> EndOfCentralDirectoryStructure?
|
|
141
|
+
{
|
|
142
|
+
var eocdOffset: UInt64 = 0
|
|
143
|
+
var index = minEndOfCentralDirectoryOffset
|
|
144
|
+
fseeko(file, 0, SEEK_END)
|
|
145
|
+
let archiveLength = Int64(ftello(file))
|
|
146
|
+
while eocdOffset == 0, index <= archiveLength {
|
|
147
|
+
fseeko(file, off_t(archiveLength - index), SEEK_SET)
|
|
148
|
+
var potentialDirectoryEndTag = UInt32()
|
|
149
|
+
fread(&potentialDirectoryEndTag, 1, MemoryLayout<UInt32>.size, file)
|
|
150
|
+
if potentialDirectoryEndTag == UInt32(endOfCentralDirectoryStructSignature) {
|
|
151
|
+
eocdOffset = UInt64(archiveLength - index)
|
|
152
|
+
guard let eocd: EndOfCentralDirectoryRecord = Data.readStruct(from: file, at: eocdOffset) else {
|
|
153
|
+
return nil
|
|
154
|
+
}
|
|
155
|
+
let zip64EOCD = scanForZIP64EndOfCentralDirectory(in: file, eocdOffset: eocdOffset)
|
|
156
|
+
return (eocd, zip64EOCD)
|
|
157
|
+
}
|
|
158
|
+
index += 1
|
|
159
|
+
}
|
|
160
|
+
return nil
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static func makeBackingConfiguration(for url: URL) -> BackingConfiguration? {
|
|
164
|
+
let fileManager = FileManager()
|
|
165
|
+
let fileSystemRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
166
|
+
guard
|
|
167
|
+
let archiveFile = fopen(fileSystemRepresentation, "rb"),
|
|
168
|
+
let (eocdRecord, zip64EOCD) = ZipArchive.scanForEndOfCentralDirectoryRecord(in: archiveFile) else
|
|
169
|
+
{
|
|
170
|
+
return nil
|
|
171
|
+
}
|
|
172
|
+
return BackingConfiguration(
|
|
173
|
+
file: archiveFile,
|
|
174
|
+
endOfCentralDirectoryRecord: eocdRecord,
|
|
175
|
+
zip64EndOfCentralDirectory: zip64EOCD)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func makeIterator() -> AnyIterator<ZipEntry> {
|
|
179
|
+
let totalNumberOfEntriesInCD = totalNumberOfEntriesInCentralDirectory
|
|
180
|
+
var directoryIndex = offsetToStartOfCentralDirectory
|
|
181
|
+
var index = 0
|
|
182
|
+
return AnyIterator {
|
|
183
|
+
guard index < totalNumberOfEntriesInCD else { return nil }
|
|
184
|
+
guard
|
|
185
|
+
let centralDirStruct: CentralDirectoryStructure = Data.readStruct(
|
|
186
|
+
from: self.archiveFile,
|
|
187
|
+
at: directoryIndex) else
|
|
188
|
+
{
|
|
189
|
+
return nil
|
|
190
|
+
}
|
|
191
|
+
let offset = UInt64(centralDirStruct.effectiveRelativeOffsetOfLocalHeader)
|
|
192
|
+
guard
|
|
193
|
+
let localFileHeader: LocalFileHeader = Data.readStruct(
|
|
194
|
+
from: self.archiveFile,
|
|
195
|
+
at: offset) else { return nil }
|
|
196
|
+
var dataDescriptor: DataDescriptor?
|
|
197
|
+
var zip64DataDescriptor: ZIP64DataDescriptor?
|
|
198
|
+
if centralDirStruct.usesDataDescriptor {
|
|
199
|
+
let additionalSize = UInt64(localFileHeader.fileNameLength) + UInt64(localFileHeader.extraFieldLength)
|
|
200
|
+
let isCompressed = centralDirStruct.compressionMethod != 0
|
|
201
|
+
let dataSize = isCompressed
|
|
202
|
+
? centralDirStruct.effectiveCompressedSize
|
|
203
|
+
: centralDirStruct.effectiveUncompressedSize
|
|
204
|
+
let descriptorPosition = offset + UInt64(LocalFileHeader.size) + additionalSize + dataSize
|
|
205
|
+
if centralDirStruct.isZIP64 {
|
|
206
|
+
zip64DataDescriptor = Data.readStruct(from: self.archiveFile, at: descriptorPosition)
|
|
207
|
+
} else {
|
|
208
|
+
dataDescriptor = Data.readStruct(from: self.archiveFile, at: descriptorPosition)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
defer {
|
|
212
|
+
directoryIndex += UInt64(CentralDirectoryStructure.size)
|
|
213
|
+
directoryIndex += UInt64(centralDirStruct.fileNameLength)
|
|
214
|
+
directoryIndex += UInt64(centralDirStruct.extraFieldLength)
|
|
215
|
+
directoryIndex += UInt64(centralDirStruct.fileCommentLength)
|
|
216
|
+
index += 1
|
|
217
|
+
}
|
|
218
|
+
return ZipEntry(
|
|
219
|
+
centralDirectoryStructure: centralDirStruct,
|
|
220
|
+
localFileHeader: localFileHeader,
|
|
221
|
+
dataDescriptor: dataDescriptor,
|
|
222
|
+
zip64DataDescriptor: zip64DataDescriptor)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/// Retrieve the ZIP `Entry` with the given `path` from the receiver.
|
|
227
|
+
///
|
|
228
|
+
/// - Note: The ZIP file format specification does not enforce unique paths for entries.
|
|
229
|
+
/// Therefore an archive can contain multiple entries with the same path. This method
|
|
230
|
+
/// always returns the first `Entry` with the given `path`.
|
|
231
|
+
///
|
|
232
|
+
/// - Parameter path: A relative file path identifying the corresponding `Entry`.
|
|
233
|
+
/// - Returns: An `Entry` with the given `path`. Otherwise, `nil`.
|
|
234
|
+
subscript(path: String) -> ZipEntry? {
|
|
235
|
+
first { $0.path == path }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/// Read a ZIP `Entry` from the receiver and write it to `url`.
|
|
239
|
+
///
|
|
240
|
+
/// - Parameters:
|
|
241
|
+
/// - entry: The ZIP `Entry` to read.
|
|
242
|
+
/// - url: The destination file URL.
|
|
243
|
+
/// - bufferSize: The maximum size of the read buffer and the decompression buffer (if needed).
|
|
244
|
+
/// - Returns: The checksum of the processed content or 0 if the `skipCRC32` flag was set to `true`.
|
|
245
|
+
/// - Throws: An error if the destination file cannot be written or the entry contains malformed content.
|
|
246
|
+
func extract(_ entry: ZipEntry, to url: URL, bufferSize: Int = defaultReadChunkSize) throws -> UInt32 {
|
|
247
|
+
guard bufferSize > 0 else { throw ArchiveError.invalidBufferSize }
|
|
248
|
+
let fileManager = FileManager()
|
|
249
|
+
try fileManager.createParentDirectoryStructure(for: url)
|
|
250
|
+
let destinationRepresentation = fileManager.fileSystemRepresentation(withPath: url.path)
|
|
251
|
+
guard let destinationFile: FILEPointer = fopen(destinationRepresentation, "wb+") else {
|
|
252
|
+
throw CocoaError(.fileNoSuchFile)
|
|
253
|
+
}
|
|
254
|
+
defer { fclose(destinationFile) }
|
|
255
|
+
|
|
256
|
+
guard bufferSize > 0 else { throw ArchiveError.invalidBufferSize }
|
|
257
|
+
guard entry.dataOffset <= .max else { throw ArchiveError.invalidLocalHeaderDataOffset }
|
|
258
|
+
fseeko(archiveFile, off_t(entry.dataOffset), SEEK_SET)
|
|
259
|
+
|
|
260
|
+
let attributes = FileManager.attributes(from: entry)
|
|
261
|
+
try fileManager.setAttributes(attributes, ofItemAtPath: url.path)
|
|
262
|
+
|
|
263
|
+
let size = entry.centralDirectoryStructure.effectiveCompressedSize
|
|
264
|
+
guard size <= .max else { throw ArchiveError.invalidEntrySize }
|
|
265
|
+
return try Data.decompress(size: Int64(size), bufferSize: bufferSize, provider: { _, chunkSize -> Data in
|
|
266
|
+
try Data.readChunk(of: chunkSize, from: self.archiveFile)
|
|
267
|
+
}, consumer: { data in
|
|
268
|
+
_ = try Data.write(chunk: data, to: destinationFile)
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// MARK: Private
|
|
273
|
+
|
|
274
|
+
private static func scanForZIP64EndOfCentralDirectory(in file: FILEPointer, eocdOffset: UInt64)
|
|
275
|
+
-> ZIP64EndOfCentralDirectory?
|
|
276
|
+
{
|
|
277
|
+
guard UInt64(ZIP64EndOfCentralDirectoryLocator.size) < eocdOffset else {
|
|
278
|
+
return nil
|
|
279
|
+
}
|
|
280
|
+
let locatorOffset = eocdOffset - UInt64(ZIP64EndOfCentralDirectoryLocator.size)
|
|
281
|
+
|
|
282
|
+
guard UInt64(ZIP64EndOfCentralDirectoryRecord.size) < locatorOffset else {
|
|
283
|
+
return nil
|
|
284
|
+
}
|
|
285
|
+
let recordOffset = locatorOffset - UInt64(ZIP64EndOfCentralDirectoryRecord.size)
|
|
286
|
+
guard
|
|
287
|
+
let locator: ZIP64EndOfCentralDirectoryLocator = Data.readStruct(from: file, at: locatorOffset),
|
|
288
|
+
let record: ZIP64EndOfCentralDirectoryRecord = Data.readStruct(from: file, at: recordOffset) else
|
|
289
|
+
{
|
|
290
|
+
return nil
|
|
291
|
+
}
|
|
292
|
+
return ZIP64EndOfCentralDirectory(record: record, locator: locator)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// MARK: - Zip64
|
|
297
|
+
|
|
298
|
+
extension ZipArchive {
|
|
299
|
+
|
|
300
|
+
struct ZIP64EndOfCentralDirectory {
|
|
301
|
+
let record: ZIP64EndOfCentralDirectoryRecord
|
|
302
|
+
let locator: ZIP64EndOfCentralDirectoryLocator
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
struct ZIP64EndOfCentralDirectoryRecord: DataSerializable {
|
|
306
|
+
let zip64EOCDRecordSignature = UInt32(zip64EOCDRecordStructSignature)
|
|
307
|
+
let sizeOfZIP64EndOfCentralDirectoryRecord: UInt64
|
|
308
|
+
let versionMadeBy: UInt16
|
|
309
|
+
let versionNeededToExtract: UInt16
|
|
310
|
+
let numberOfDisk: UInt32
|
|
311
|
+
let numberOfDiskStart: UInt32
|
|
312
|
+
let totalNumberOfEntriesOnDisk: UInt64
|
|
313
|
+
let totalNumberOfEntriesInCentralDirectory: UInt64
|
|
314
|
+
let sizeOfCentralDirectory: UInt64
|
|
315
|
+
let offsetToStartOfCentralDirectory: UInt64
|
|
316
|
+
let zip64ExtensibleDataSector: Data
|
|
317
|
+
static let size = 56
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
struct ZIP64EndOfCentralDirectoryLocator: DataSerializable {
|
|
321
|
+
let zip64EOCDLocatorSignature = UInt32(zip64EOCDLocatorStructSignature)
|
|
322
|
+
let numberOfDiskWithZIP64EOCDRecordStart: UInt32
|
|
323
|
+
let relativeOffsetOfZIP64EOCDRecord: UInt64
|
|
324
|
+
let totalNumberOfDisk: UInt32
|
|
325
|
+
static let size = 20
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static let zip64EOCDRecordStructSignature = 0x06064b50
|
|
329
|
+
static let zip64EOCDLocatorStructSignature = 0x07064b50
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
extension ZipArchive.ZIP64EndOfCentralDirectoryRecord {
|
|
333
|
+
|
|
334
|
+
// MARK: Lifecycle
|
|
335
|
+
|
|
336
|
+
init?(data: Data, additionalDataProvider _: (Int) throws -> Data) {
|
|
337
|
+
guard data.count == ZipArchive.ZIP64EndOfCentralDirectoryRecord.size else { return nil }
|
|
338
|
+
guard data.scanValue(start: 0) == zip64EOCDRecordSignature else { return nil }
|
|
339
|
+
sizeOfZIP64EndOfCentralDirectoryRecord = data.scanValue(start: 4)
|
|
340
|
+
versionMadeBy = data.scanValue(start: 12)
|
|
341
|
+
versionNeededToExtract = data.scanValue(start: 14)
|
|
342
|
+
// Version Needed to Extract: 4.5 - File uses ZIP64 format extensions
|
|
343
|
+
guard versionNeededToExtract >= 45 else { return nil }
|
|
344
|
+
numberOfDisk = data.scanValue(start: 16)
|
|
345
|
+
numberOfDiskStart = data.scanValue(start: 20)
|
|
346
|
+
totalNumberOfEntriesOnDisk = data.scanValue(start: 24)
|
|
347
|
+
totalNumberOfEntriesInCentralDirectory = data.scanValue(start: 32)
|
|
348
|
+
sizeOfCentralDirectory = data.scanValue(start: 40)
|
|
349
|
+
offsetToStartOfCentralDirectory = data.scanValue(start: 48)
|
|
350
|
+
zip64ExtensibleDataSector = Data()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
init(
|
|
354
|
+
record: ZipArchive.ZIP64EndOfCentralDirectoryRecord,
|
|
355
|
+
numberOfEntriesOnDisk: UInt64,
|
|
356
|
+
numberOfEntriesInCD: UInt64,
|
|
357
|
+
sizeOfCentralDirectory: UInt64,
|
|
358
|
+
offsetToStartOfCD: UInt64)
|
|
359
|
+
{
|
|
360
|
+
sizeOfZIP64EndOfCentralDirectoryRecord = record.sizeOfZIP64EndOfCentralDirectoryRecord
|
|
361
|
+
versionMadeBy = record.versionMadeBy
|
|
362
|
+
versionNeededToExtract = record.versionNeededToExtract
|
|
363
|
+
numberOfDisk = record.numberOfDisk
|
|
364
|
+
numberOfDiskStart = record.numberOfDiskStart
|
|
365
|
+
totalNumberOfEntriesOnDisk = numberOfEntriesOnDisk
|
|
366
|
+
totalNumberOfEntriesInCentralDirectory = numberOfEntriesInCD
|
|
367
|
+
self.sizeOfCentralDirectory = sizeOfCentralDirectory
|
|
368
|
+
offsetToStartOfCentralDirectory = offsetToStartOfCD
|
|
369
|
+
zip64ExtensibleDataSector = record.zip64ExtensibleDataSector
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// MARK: Internal
|
|
373
|
+
|
|
374
|
+
var data: Data {
|
|
375
|
+
var zip64EOCDRecordSignature = zip64EOCDRecordSignature
|
|
376
|
+
var sizeOfZIP64EOCDRecord = sizeOfZIP64EndOfCentralDirectoryRecord
|
|
377
|
+
var versionMadeBy = versionMadeBy
|
|
378
|
+
var versionNeededToExtract = versionNeededToExtract
|
|
379
|
+
var numberOfDisk = numberOfDisk
|
|
380
|
+
var numberOfDiskStart = numberOfDiskStart
|
|
381
|
+
var totalNumberOfEntriesOnDisk = totalNumberOfEntriesOnDisk
|
|
382
|
+
var totalNumberOfEntriesInCD = totalNumberOfEntriesInCentralDirectory
|
|
383
|
+
var sizeOfCD = sizeOfCentralDirectory
|
|
384
|
+
var offsetToStartOfCD = offsetToStartOfCentralDirectory
|
|
385
|
+
var data = Data()
|
|
386
|
+
withUnsafePointer(to: &zip64EOCDRecordSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
387
|
+
withUnsafePointer(to: &sizeOfZIP64EOCDRecord) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
388
|
+
withUnsafePointer(to: &versionMadeBy) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
389
|
+
withUnsafePointer(to: &versionNeededToExtract) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
390
|
+
withUnsafePointer(to: &numberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
391
|
+
withUnsafePointer(to: &numberOfDiskStart) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
392
|
+
withUnsafePointer(to: &totalNumberOfEntriesOnDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
393
|
+
withUnsafePointer(to: &totalNumberOfEntriesInCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
394
|
+
withUnsafePointer(to: &sizeOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
395
|
+
withUnsafePointer(to: &offsetToStartOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
396
|
+
data.append(zip64ExtensibleDataSector)
|
|
397
|
+
return data
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
extension ZipArchive.ZIP64EndOfCentralDirectoryLocator {
|
|
403
|
+
|
|
404
|
+
// MARK: Lifecycle
|
|
405
|
+
|
|
406
|
+
init?(data: Data, additionalDataProvider _: (Int) throws -> Data) {
|
|
407
|
+
guard data.count == ZipArchive.ZIP64EndOfCentralDirectoryLocator.size else { return nil }
|
|
408
|
+
guard data.scanValue(start: 0) == zip64EOCDLocatorSignature else { return nil }
|
|
409
|
+
numberOfDiskWithZIP64EOCDRecordStart = data.scanValue(start: 4)
|
|
410
|
+
relativeOffsetOfZIP64EOCDRecord = data.scanValue(start: 8)
|
|
411
|
+
totalNumberOfDisk = data.scanValue(start: 16)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// MARK: Internal
|
|
415
|
+
|
|
416
|
+
var data: Data {
|
|
417
|
+
var zip64EOCDLocatorSignature = zip64EOCDLocatorSignature
|
|
418
|
+
var numberOfDiskWithZIP64EOCD = numberOfDiskWithZIP64EOCDRecordStart
|
|
419
|
+
var offsetOfZIP64EOCDRecord = relativeOffsetOfZIP64EOCDRecord
|
|
420
|
+
var totalNumberOfDisk = totalNumberOfDisk
|
|
421
|
+
var data = Data()
|
|
422
|
+
withUnsafePointer(to: &zip64EOCDLocatorSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
423
|
+
withUnsafePointer(to: &numberOfDiskWithZIP64EOCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
424
|
+
withUnsafePointer(to: &offsetOfZIP64EOCDRecord) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
425
|
+
withUnsafePointer(to: &totalNumberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
426
|
+
return data
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
extension ZipArchive.EndOfCentralDirectoryRecord {
|
|
432
|
+
|
|
433
|
+
// MARK: Lifecycle
|
|
434
|
+
|
|
435
|
+
init?(data: Data, additionalDataProvider provider: (Int) throws -> Data) {
|
|
436
|
+
guard data.count == ZipArchive.EndOfCentralDirectoryRecord.size else { return nil }
|
|
437
|
+
guard data.scanValue(start: 0) == endOfCentralDirectorySignature else { return nil }
|
|
438
|
+
numberOfDisk = data.scanValue(start: 4)
|
|
439
|
+
numberOfDiskStart = data.scanValue(start: 6)
|
|
440
|
+
totalNumberOfEntriesOnDisk = data.scanValue(start: 8)
|
|
441
|
+
totalNumberOfEntriesInCentralDirectory = data.scanValue(start: 10)
|
|
442
|
+
sizeOfCentralDirectory = data.scanValue(start: 12)
|
|
443
|
+
offsetToStartOfCentralDirectory = data.scanValue(start: 16)
|
|
444
|
+
zipFileCommentLength = data.scanValue(start: 20)
|
|
445
|
+
guard let commentData = try? provider(Int(zipFileCommentLength)) else { return nil }
|
|
446
|
+
guard commentData.count == Int(zipFileCommentLength) else { return nil }
|
|
447
|
+
zipFileCommentData = commentData
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// MARK: Internal
|
|
451
|
+
|
|
452
|
+
var data: Data {
|
|
453
|
+
var endOfCDSignature = endOfCentralDirectorySignature
|
|
454
|
+
var numberOfDisk = numberOfDisk
|
|
455
|
+
var numberOfDiskStart = numberOfDiskStart
|
|
456
|
+
var totalNumberOfEntriesOnDisk = totalNumberOfEntriesOnDisk
|
|
457
|
+
var totalNumberOfEntriesInCD = totalNumberOfEntriesInCentralDirectory
|
|
458
|
+
var sizeOfCentralDirectory = sizeOfCentralDirectory
|
|
459
|
+
var offsetToStartOfCD = offsetToStartOfCentralDirectory
|
|
460
|
+
var zipFileCommentLength = zipFileCommentLength
|
|
461
|
+
var data = Data()
|
|
462
|
+
withUnsafePointer(to: &endOfCDSignature) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
463
|
+
withUnsafePointer(to: &numberOfDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
464
|
+
withUnsafePointer(to: &numberOfDiskStart) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
465
|
+
withUnsafePointer(to: &totalNumberOfEntriesOnDisk) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
466
|
+
withUnsafePointer(to: &totalNumberOfEntriesInCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
467
|
+
withUnsafePointer(to: &sizeOfCentralDirectory) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
468
|
+
withUnsafePointer(to: &offsetToStartOfCD) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
469
|
+
withUnsafePointer(to: &zipFileCommentLength) { data.append(UnsafeBufferPointer(start: $0, count: 1)) }
|
|
470
|
+
data.append(zipFileCommentData)
|
|
471
|
+
return data
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
}
|