@onekeyfe/react-native-range-downloader 3.0.79 → 3.0.81-alpha.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.
Files changed (113) hide show
  1. package/ReactNativeRangeDownloader.podspec +8 -0
  2. package/android/build.gradle +1 -0
  3. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +284 -0
  4. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +842 -0
  5. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +148 -0
  6. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +75 -0
  7. package/ios/FirmwareArchiveMinizipBridge.h +33 -0
  8. package/ios/FirmwareArchiveMinizipBridge.mm +213 -0
  9. package/ios/FirmwareArtifactStore.swift +1276 -0
  10. package/ios/FirmwareBackgroundSessionEventRouter.swift +17 -0
  11. package/ios/ReactNativeRangeDownloader.swift +638 -6
  12. package/lib/module/index.js +2 -1
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +95 -1
  15. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
  16. package/lib/typescript/src/index.d.ts +1 -0
  17. package/lib/typescript/src/index.d.ts.map +1 -1
  18. package/nitrogen/generated/android/c++/JDownloadChannel.hpp +3 -0
  19. package/nitrogen/generated/android/c++/JFirmwareArchiveExpectedEntry.hpp +69 -0
  20. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +86 -0
  21. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeResult.hpp +80 -0
  22. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializedArtifact.hpp +63 -0
  23. package/nitrogen/generated/android/c++/JFirmwareArtifactCancelParams.hpp +57 -0
  24. package/nitrogen/generated/android/c++/JFirmwareArtifactCapabilities.hpp +88 -0
  25. package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +98 -0
  26. package/nitrogen/generated/android/c++/JFirmwareArtifactLease.hpp +57 -0
  27. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseCreateParams.hpp +57 -0
  28. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReconcileParams.hpp +76 -0
  29. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReleaseParams.hpp +61 -0
  30. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseRetainParams.hpp +61 -0
  31. package/nitrogen/generated/android/c++/JFirmwareArtifactReaderCloseParams.hpp +57 -0
  32. package/nitrogen/generated/android/c++/JFirmwareArtifactReaderInfo.hpp +61 -0
  33. package/nitrogen/generated/android/c++/JFirmwareArtifactReaderReadParams.hpp +65 -0
  34. package/nitrogen/generated/android/c++/JFirmwareArtifactReceipt.hpp +65 -0
  35. package/nitrogen/generated/android/c++/JFirmwareArtifactRefParams.hpp +57 -0
  36. package/nitrogen/generated/android/c++/JFirmwareArtifactSweepResult.hpp +61 -0
  37. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +267 -0
  38. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +13 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/DownloadChannel.kt +2 -1
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveExpectedEntry.kt +47 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +44 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeResult.kt +38 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializedArtifact.kt +41 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCancelParams.kt +38 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCapabilities.kt +47 -0
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +68 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLease.kt +38 -0
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseCreateParams.kt +38 -0
  49. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReconcileParams.kt +38 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReleaseParams.kt +41 -0
  51. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseRetainParams.kt +41 -0
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderCloseParams.kt +38 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderInfo.kt +41 -0
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderReadParams.kt +44 -0
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReceipt.kt +44 -0
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactRefParams.kt +38 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactSweepResult.kt +41 -0
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +53 -0
  59. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.cpp +48 -0
  60. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +329 -0
  61. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +56 -0
  62. package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +163 -0
  63. package/nitrogen/generated/ios/swift/DownloadChannel.swift +4 -0
  64. package/nitrogen/generated/ios/swift/FirmwareArchiveExpectedEntry.swift +69 -0
  65. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +70 -0
  66. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeResult.swift +48 -0
  67. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializedArtifact.swift +47 -0
  68. package/nitrogen/generated/ios/swift/FirmwareArtifactCancelParams.swift +36 -0
  69. package/nitrogen/generated/ios/swift/FirmwareArtifactCapabilities.swift +81 -0
  70. package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +177 -0
  71. package/nitrogen/generated/ios/swift/FirmwareArtifactLease.swift +36 -0
  72. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseCreateParams.swift +36 -0
  73. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReconcileParams.swift +48 -0
  74. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReleaseParams.swift +47 -0
  75. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseRetainParams.swift +47 -0
  76. package/nitrogen/generated/ios/swift/FirmwareArtifactReaderCloseParams.swift +36 -0
  77. package/nitrogen/generated/ios/swift/FirmwareArtifactReaderInfo.swift +47 -0
  78. package/nitrogen/generated/ios/swift/FirmwareArtifactReaderReadParams.swift +58 -0
  79. package/nitrogen/generated/ios/swift/FirmwareArtifactReceipt.swift +58 -0
  80. package/nitrogen/generated/ios/swift/FirmwareArtifactRefParams.swift +36 -0
  81. package/nitrogen/generated/ios/swift/FirmwareArtifactSweepResult.swift +47 -0
  82. package/nitrogen/generated/ios/swift/Func_void_FirmwareArchiveMaterializeResult.swift +47 -0
  83. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactLease.swift +47 -0
  84. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReaderInfo.swift +47 -0
  85. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReceipt.swift +47 -0
  86. package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactSweepResult.swift +47 -0
  87. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer_.swift +47 -0
  88. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +13 -0
  89. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +240 -0
  90. package/nitrogen/generated/shared/c++/DownloadChannel.hpp +4 -0
  91. package/nitrogen/generated/shared/c++/FirmwareArchiveExpectedEntry.hpp +87 -0
  92. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +86 -0
  93. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeResult.hpp +77 -0
  94. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializedArtifact.hpp +81 -0
  95. package/nitrogen/generated/shared/c++/FirmwareArtifactCancelParams.hpp +75 -0
  96. package/nitrogen/generated/shared/c++/FirmwareArtifactCapabilities.hpp +88 -0
  97. package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +116 -0
  98. package/nitrogen/generated/shared/c++/FirmwareArtifactLease.hpp +75 -0
  99. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseCreateParams.hpp +75 -0
  100. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReconcileParams.hpp +76 -0
  101. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReleaseParams.hpp +79 -0
  102. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseRetainParams.hpp +79 -0
  103. package/nitrogen/generated/shared/c++/FirmwareArtifactReaderCloseParams.hpp +75 -0
  104. package/nitrogen/generated/shared/c++/FirmwareArtifactReaderInfo.hpp +79 -0
  105. package/nitrogen/generated/shared/c++/FirmwareArtifactReaderReadParams.hpp +83 -0
  106. package/nitrogen/generated/shared/c++/FirmwareArtifactReceipt.hpp +83 -0
  107. package/nitrogen/generated/shared/c++/FirmwareArtifactRefParams.hpp +75 -0
  108. package/nitrogen/generated/shared/c++/FirmwareArtifactSweepResult.hpp +79 -0
  109. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +13 -0
  110. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +62 -0
  111. package/package.json +4 -2
  112. package/src/ReactNativeRangeDownloader.nitro.ts +134 -1
  113. package/src/index.tsx +3 -1
@@ -15,6 +15,7 @@ Pod::Spec.new do |s|
15
15
 
16
16
  s.source_files = [
17
17
  "ios/**/*.{swift}",
18
+ "ios/**/*.{h}",
18
19
  "ios/**/*.{m,mm}",
19
20
  "cpp/**/*.{hpp,cpp}",
20
21
  ]
@@ -22,6 +23,13 @@ Pod::Spec.new do |s|
22
23
  s.dependency 'React-jsi'
23
24
  s.dependency 'React-callinvoker'
24
25
  s.dependency 'ReactNativeNativeLogger'
26
+ s.dependency 'SniConnect', package["version"]
27
+ s.public_header_files = "ios/FirmwareArchiveMinizipBridge.h"
28
+ s.pod_target_xcconfig = {
29
+ 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/SSZipArchive/SSZipArchive/minizip"',
30
+ }
31
+
32
+ s.dependency 'SSZipArchive', '>= 2.5.4'
25
33
 
26
34
  load 'nitrogen/generated/ios/ReactNativeRangeDownloader+autolinking.rb'
27
35
  add_nitrogen_files(s)
@@ -127,6 +127,7 @@ dependencies {
127
127
  implementation project(":react-native-nitro-modules")
128
128
 
129
129
  implementation project(":onekeyfe_react-native-native-logger")
130
+ implementation project(":onekeyfe_react-native-sni-connect")
130
131
 
131
132
  implementation "com.squareup.okhttp3:okhttp:4.12.0"
132
133
 
@@ -0,0 +1,284 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import java.io.File
4
+ import java.io.RandomAccessFile
5
+ import java.nio.ByteBuffer
6
+ import java.nio.ByteOrder
7
+ import java.nio.charset.CodingErrorAction
8
+ import java.nio.charset.StandardCharsets
9
+ import java.text.Normalizer
10
+ import java.util.Locale
11
+
12
+ internal data class FirmwareArchiveCentralEntry(
13
+ val name: String,
14
+ val compressedSize: Long,
15
+ val uncompressedSize: Long,
16
+ val crc32: Long,
17
+ val flags: Int,
18
+ val compressionMethod: Int,
19
+ val versionMadeBy: Int,
20
+ val externalAttributes: Long,
21
+ val diskNumber: Int,
22
+ )
23
+
24
+ internal object FirmwareArchiveRules {
25
+ private const val MAX_EOCD_SEARCH_BYTES = 65_557
26
+ private const val EOCD_SIGNATURE = 0x06054b50L
27
+ private const val CENTRAL_ENTRY_SIGNATURE = 0x02014b50L
28
+ private const val MAX_ARCHIVE_ENTRIES = 4096
29
+ private const val MAX_ARCHIVE_ENTRY_BYTES = 128L * 1024 * 1024
30
+ private const val MAX_ARCHIVE_EXPANDED_BYTES = 512L * 1024 * 1024
31
+ private val nestedArchiveExtensions = setOf(
32
+ ".zip",
33
+ ".7z",
34
+ ".rar",
35
+ ".tar",
36
+ ".gz",
37
+ ".tgz",
38
+ )
39
+ private val sha256Pattern = Regex("^[a-fA-F0-9]{64}$")
40
+
41
+ fun validateRequirements(
42
+ expectedEntries: Array<FirmwareArchiveExpectedEntry>,
43
+ ): List<FirmwareArchiveExpectedEntry> {
44
+ require(expectedEntries.isNotEmpty() && expectedEntries.size <= MAX_ARCHIVE_ENTRIES) {
45
+ "Firmware archive expected entry count is invalid"
46
+ }
47
+ val names = mutableSetOf<String>()
48
+ val canonicalNames = mutableSetOf<String>()
49
+ var totalSize = 0L
50
+ expectedEntries.forEach { entry ->
51
+ require(
52
+ entry.artifactId.isNotEmpty() &&
53
+ entry.artifactId.length <= 160 &&
54
+ entry.artifactId.all { it.isLetterOrDigit() || it in "._-" }
55
+ ) {
56
+ "Firmware archive artifactId is invalid"
57
+ }
58
+ require(
59
+ names.add(entry.entryName) &&
60
+ validatePortableName(entry.entryName, canonicalNames)
61
+ ) {
62
+ "Firmware archive expected entry name is invalid"
63
+ }
64
+ val expectedSize = entry.expectedSize.toExactPositiveLong("entry size")
65
+ require(expectedSize <= MAX_ARCHIVE_ENTRY_BYTES) {
66
+ "Firmware archive entry exceeds size limits"
67
+ }
68
+ totalSize = Math.addExact(totalSize, expectedSize)
69
+ require(totalSize <= MAX_ARCHIVE_EXPANDED_BYTES) {
70
+ "Firmware archive expected entries exceed size limits"
71
+ }
72
+ require(sha256Pattern.matches(entry.expectedSha256)) {
73
+ "Firmware archive entry SHA-256 is invalid"
74
+ }
75
+ }
76
+ return expectedEntries.toList()
77
+ }
78
+
79
+ fun validateCentralDirectory(
80
+ file: File,
81
+ requirements: List<FirmwareArchiveExpectedEntry>,
82
+ ): List<FirmwareArchiveCentralEntry> {
83
+ val entries = scanCentralDirectory(file)
84
+ require(entries.size == requirements.size) {
85
+ "Firmware archive has missing or extra entries"
86
+ }
87
+ val requirementsByName = requirements.associateBy { it.entryName }
88
+ val names = mutableSetOf<String>()
89
+ val canonicalNames = mutableSetOf<String>()
90
+ entries.forEach { entry ->
91
+ val requirement = requirementsByName[entry.name]
92
+ ?: error("Firmware archive contains an unexpected entry")
93
+ val expectedSize = requirement.expectedSize.toLong()
94
+ require(
95
+ names.add(entry.name) &&
96
+ validatePortableName(entry.name, canonicalNames) &&
97
+ entry.uncompressedSize == expectedSize &&
98
+ entry.uncompressedSize > 0 &&
99
+ entry.compressedSize in 0..MAX_ARCHIVE_EXPANDED_BYTES &&
100
+ entry.uncompressedSize <=
101
+ Math.multiplyExact(entry.compressedSize.coerceAtLeast(1), 1000) &&
102
+ entry.diskNumber == 0 &&
103
+ entry.flags and 1 == 0 &&
104
+ (entry.compressionMethod == 0 || entry.compressionMethod == 8) &&
105
+ isRegularEntry(entry)
106
+ ) {
107
+ "Firmware archive entry metadata is invalid"
108
+ }
109
+ }
110
+ return entries
111
+ }
112
+
113
+ private fun scanCentralDirectory(file: File): List<FirmwareArchiveCentralEntry> {
114
+ require(file.isFile && file.length() >= 22) {
115
+ "Firmware archive is invalid"
116
+ }
117
+ RandomAccessFile(file, "r").use { archive ->
118
+ val tailSize = minOf(file.length(), MAX_EOCD_SEARCH_BYTES.toLong()).toInt()
119
+ val tail = ByteArray(tailSize)
120
+ archive.seek(file.length() - tailSize)
121
+ archive.readFully(tail)
122
+ val eocdIndex = findSignatureBackwards(tail, EOCD_SIGNATURE)
123
+ require(eocdIndex >= 0 && eocdIndex + 22 <= tail.size) {
124
+ "Firmware archive EOCD is missing"
125
+ }
126
+ val eocd = ByteBuffer.wrap(tail, eocdIndex, tail.size - eocdIndex)
127
+ .order(ByteOrder.LITTLE_ENDIAN)
128
+ require(eocd.int.toUnsignedLong() == EOCD_SIGNATURE) {
129
+ "Firmware archive EOCD is invalid"
130
+ }
131
+ val diskNumber = eocd.short.toUnsignedInt()
132
+ val centralDisk = eocd.short.toUnsignedInt()
133
+ val entriesOnDisk = eocd.short.toUnsignedInt()
134
+ val totalEntries = eocd.short.toUnsignedInt()
135
+ val centralSize = eocd.int.toUnsignedLong()
136
+ val centralOffset = eocd.int.toUnsignedLong()
137
+ val commentLength = eocd.short.toUnsignedInt()
138
+ require(
139
+ diskNumber == 0 &&
140
+ centralDisk == 0 &&
141
+ entriesOnDisk == totalEntries &&
142
+ totalEntries in 1..MAX_ARCHIVE_ENTRIES &&
143
+ totalEntries != 0xffff &&
144
+ centralSize != 0xffff_ffffL &&
145
+ centralOffset != 0xffff_ffffL &&
146
+ eocdIndex + 22 + commentLength == tail.size &&
147
+ centralOffset <= file.length() - centralSize &&
148
+ centralOffset + centralSize <= file.length() - tailSize + eocdIndex
149
+ ) {
150
+ "Firmware archive is multi-disk, ZIP64, or malformed"
151
+ }
152
+
153
+ archive.seek(centralOffset)
154
+ val entries = ArrayList<FirmwareArchiveCentralEntry>(totalEntries)
155
+ repeat(totalEntries) {
156
+ val header = ByteArray(46)
157
+ archive.readFully(header)
158
+ val buffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN)
159
+ require(buffer.int.toUnsignedLong() == CENTRAL_ENTRY_SIGNATURE) {
160
+ "Firmware archive central entry is malformed"
161
+ }
162
+ val versionMadeBy = buffer.short.toUnsignedInt()
163
+ buffer.short
164
+ val flags = buffer.short.toUnsignedInt()
165
+ val method = buffer.short.toUnsignedInt()
166
+ buffer.position(buffer.position() + 4)
167
+ val crc32 = buffer.int.toUnsignedLong()
168
+ val compressedSize = buffer.int.toUnsignedLong()
169
+ val uncompressedSize = buffer.int.toUnsignedLong()
170
+ val nameLength = buffer.short.toUnsignedInt()
171
+ val extraLength = buffer.short.toUnsignedInt()
172
+ val entryCommentLength = buffer.short.toUnsignedInt()
173
+ val entryDisk = buffer.short.toUnsignedInt()
174
+ buffer.short
175
+ val externalAttributes = buffer.int.toUnsignedLong()
176
+ val localHeaderOffset = buffer.int.toUnsignedLong()
177
+ require(
178
+ nameLength in 1..4096 &&
179
+ compressedSize != 0xffff_ffffL &&
180
+ uncompressedSize != 0xffff_ffffL &&
181
+ entryDisk != 0xffff &&
182
+ localHeaderOffset != 0xffff_ffffL &&
183
+ localHeaderOffset < centralOffset
184
+ ) {
185
+ "Firmware archive ZIP64 entry is not supported"
186
+ }
187
+ val nameBytes = ByteArray(nameLength)
188
+ archive.readFully(nameBytes)
189
+ val name = decodeEntryName(nameBytes, flags)
190
+ val skipLength = Math.addExact(extraLength, entryCommentLength)
191
+ archive.seek(Math.addExact(archive.filePointer, skipLength.toLong()))
192
+ entries += FirmwareArchiveCentralEntry(
193
+ name = name,
194
+ compressedSize = compressedSize,
195
+ uncompressedSize = uncompressedSize,
196
+ crc32 = crc32,
197
+ flags = flags,
198
+ compressionMethod = method,
199
+ versionMadeBy = versionMadeBy,
200
+ externalAttributes = externalAttributes,
201
+ diskNumber = entryDisk,
202
+ )
203
+ }
204
+ require(archive.filePointer == centralOffset + centralSize) {
205
+ "Firmware archive central directory size mismatch"
206
+ }
207
+ return entries
208
+ }
209
+ }
210
+
211
+ private fun decodeEntryName(bytes: ByteArray, flags: Int): String {
212
+ if (flags and (1 shl 11) == 0) {
213
+ require(bytes.all { it.toInt() and 0xff < 0x80 }) {
214
+ "Firmware archive entry name must be UTF-8 or ASCII"
215
+ }
216
+ return String(bytes, StandardCharsets.US_ASCII)
217
+ }
218
+ val decoder = StandardCharsets.UTF_8.newDecoder()
219
+ .onMalformedInput(CodingErrorAction.REPORT)
220
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
221
+ return decoder.decode(ByteBuffer.wrap(bytes)).toString()
222
+ }
223
+
224
+ private fun validatePortableName(
225
+ name: String,
226
+ canonicalNames: MutableSet<String>,
227
+ ): Boolean {
228
+ val normalized = Normalizer.normalize(name, Normalizer.Form.NFC)
229
+ val folded = normalized.lowercase(Locale.ROOT)
230
+ val components = name.split('/')
231
+ return name.isNotEmpty() &&
232
+ name.length <= 512 &&
233
+ name == normalized &&
234
+ !name.startsWith("/") &&
235
+ !name.startsWith("\\") &&
236
+ '\\' !in name &&
237
+ ':' !in name &&
238
+ name.none { it.code < 0x20 || it.code == 0x7f } &&
239
+ components.all {
240
+ it.isNotEmpty() &&
241
+ it != "." &&
242
+ it != ".." &&
243
+ !it.endsWith(".") &&
244
+ !it.endsWith(" ")
245
+ } &&
246
+ nestedArchiveExtensions.none { folded.endsWith(it) } &&
247
+ canonicalNames.add(folded)
248
+ }
249
+
250
+ private fun isRegularEntry(entry: FirmwareArchiveCentralEntry): Boolean {
251
+ val hostSystem = entry.versionMadeBy ushr 8
252
+ if (hostSystem == 3 || hostSystem == 19) {
253
+ val fileType = (entry.externalAttributes ushr 16) and 0xf000
254
+ return fileType == 0L || fileType == 0x8000L
255
+ }
256
+ return entry.externalAttributes and 0x10 == 0L
257
+ }
258
+
259
+ private fun findSignatureBackwards(bytes: ByteArray, signature: Long): Int {
260
+ for (index in bytes.size - 4 downTo 0) {
261
+ val value = (bytes[index].toLong() and 0xff) or
262
+ ((bytes[index + 1].toLong() and 0xff) shl 8) or
263
+ ((bytes[index + 2].toLong() and 0xff) shl 16) or
264
+ ((bytes[index + 3].toLong() and 0xff) shl 24)
265
+ if (value == signature) return index
266
+ }
267
+ return -1
268
+ }
269
+
270
+ private fun Int.toUnsignedLong(): Long = toLong() and 0xffff_ffffL
271
+
272
+ private fun Short.toUnsignedInt(): Int = toInt() and 0xffff
273
+
274
+ private fun Double.toExactPositiveLong(label: String): Long {
275
+ require(isFinite() && this > 0 && this <= Long.MAX_VALUE.toDouble()) {
276
+ "Invalid firmware archive $label"
277
+ }
278
+ val converted = toLong()
279
+ require(converted.toDouble() == this) {
280
+ "Invalid firmware archive $label"
281
+ }
282
+ return converted
283
+ }
284
+ }