7z-iterator 0.1.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 (166) hide show
  1. package/LICENSE +42 -0
  2. package/README.md +142 -0
  3. package/dist/cjs/FileEntry.d.cts +12 -0
  4. package/dist/cjs/FileEntry.d.ts +12 -0
  5. package/dist/cjs/FileEntry.js +147 -0
  6. package/dist/cjs/FileEntry.js.map +1 -0
  7. package/dist/cjs/SevenZipIterator.d.cts +8 -0
  8. package/dist/cjs/SevenZipIterator.d.ts +8 -0
  9. package/dist/cjs/SevenZipIterator.js +204 -0
  10. package/dist/cjs/SevenZipIterator.js.map +1 -0
  11. package/dist/cjs/index.d.cts +4 -0
  12. package/dist/cjs/index.d.ts +4 -0
  13. package/dist/cjs/index.js +40 -0
  14. package/dist/cjs/index.js.map +1 -0
  15. package/dist/cjs/lib/Lock.d.cts +11 -0
  16. package/dist/cjs/lib/Lock.d.ts +11 -0
  17. package/dist/cjs/lib/Lock.js +65 -0
  18. package/dist/cjs/lib/Lock.js.map +1 -0
  19. package/dist/cjs/lib/streamToSource.d.cts +20 -0
  20. package/dist/cjs/lib/streamToSource.d.ts +20 -0
  21. package/dist/cjs/lib/streamToSource.js +116 -0
  22. package/dist/cjs/lib/streamToSource.js.map +1 -0
  23. package/dist/cjs/nextEntry.d.cts +4 -0
  24. package/dist/cjs/nextEntry.d.ts +4 -0
  25. package/dist/cjs/nextEntry.js +102 -0
  26. package/dist/cjs/nextEntry.js.map +1 -0
  27. package/dist/cjs/package.json +1 -0
  28. package/dist/cjs/sevenz/NumberCodec.d.cts +58 -0
  29. package/dist/cjs/sevenz/NumberCodec.d.ts +58 -0
  30. package/dist/cjs/sevenz/NumberCodec.js +133 -0
  31. package/dist/cjs/sevenz/NumberCodec.js.map +1 -0
  32. package/dist/cjs/sevenz/SevenZipParser.d.cts +96 -0
  33. package/dist/cjs/sevenz/SevenZipParser.d.ts +96 -0
  34. package/dist/cjs/sevenz/SevenZipParser.js +457 -0
  35. package/dist/cjs/sevenz/SevenZipParser.js.map +1 -0
  36. package/dist/cjs/sevenz/codecs/Aes.d.cts +22 -0
  37. package/dist/cjs/sevenz/codecs/Aes.d.ts +22 -0
  38. package/dist/cjs/sevenz/codecs/Aes.js +179 -0
  39. package/dist/cjs/sevenz/codecs/Aes.js.map +1 -0
  40. package/dist/cjs/sevenz/codecs/BZip2.d.cts +14 -0
  41. package/dist/cjs/sevenz/codecs/BZip2.d.ts +14 -0
  42. package/dist/cjs/sevenz/codecs/BZip2.js +37 -0
  43. package/dist/cjs/sevenz/codecs/BZip2.js.map +1 -0
  44. package/dist/cjs/sevenz/codecs/Bcj.d.cts +15 -0
  45. package/dist/cjs/sevenz/codecs/Bcj.d.ts +15 -0
  46. package/dist/cjs/sevenz/codecs/Bcj.js +89 -0
  47. package/dist/cjs/sevenz/codecs/Bcj.js.map +1 -0
  48. package/dist/cjs/sevenz/codecs/Copy.d.cts +14 -0
  49. package/dist/cjs/sevenz/codecs/Copy.d.ts +14 -0
  50. package/dist/cjs/sevenz/codecs/Copy.js +28 -0
  51. package/dist/cjs/sevenz/codecs/Copy.js.map +1 -0
  52. package/dist/cjs/sevenz/codecs/Deflate.d.cts +14 -0
  53. package/dist/cjs/sevenz/codecs/Deflate.d.ts +14 -0
  54. package/dist/cjs/sevenz/codecs/Deflate.js +39 -0
  55. package/dist/cjs/sevenz/codecs/Deflate.js.map +1 -0
  56. package/dist/cjs/sevenz/codecs/Delta.d.cts +15 -0
  57. package/dist/cjs/sevenz/codecs/Delta.d.ts +15 -0
  58. package/dist/cjs/sevenz/codecs/Delta.js +55 -0
  59. package/dist/cjs/sevenz/codecs/Delta.js.map +1 -0
  60. package/dist/cjs/sevenz/codecs/Lzma.d.cts +14 -0
  61. package/dist/cjs/sevenz/codecs/Lzma.d.ts +14 -0
  62. package/dist/cjs/sevenz/codecs/Lzma.js +53 -0
  63. package/dist/cjs/sevenz/codecs/Lzma.js.map +1 -0
  64. package/dist/cjs/sevenz/codecs/Lzma2.d.cts +14 -0
  65. package/dist/cjs/sevenz/codecs/Lzma2.d.ts +14 -0
  66. package/dist/cjs/sevenz/codecs/Lzma2.js +160 -0
  67. package/dist/cjs/sevenz/codecs/Lzma2.js.map +1 -0
  68. package/dist/cjs/sevenz/codecs/createBufferingDecoder.d.cts +7 -0
  69. package/dist/cjs/sevenz/codecs/createBufferingDecoder.d.ts +7 -0
  70. package/dist/cjs/sevenz/codecs/createBufferingDecoder.js +36 -0
  71. package/dist/cjs/sevenz/codecs/createBufferingDecoder.js.map +1 -0
  72. package/dist/cjs/sevenz/codecs/index.d.cts +24 -0
  73. package/dist/cjs/sevenz/codecs/index.d.ts +24 -0
  74. package/dist/cjs/sevenz/codecs/index.js +128 -0
  75. package/dist/cjs/sevenz/codecs/index.js.map +1 -0
  76. package/dist/cjs/sevenz/codecs/streams.d.cts +18 -0
  77. package/dist/cjs/sevenz/codecs/streams.d.ts +18 -0
  78. package/dist/cjs/sevenz/codecs/streams.js +79 -0
  79. package/dist/cjs/sevenz/codecs/streams.js.map +1 -0
  80. package/dist/cjs/sevenz/constants.d.cts +86 -0
  81. package/dist/cjs/sevenz/constants.d.ts +86 -0
  82. package/dist/cjs/sevenz/constants.js +162 -0
  83. package/dist/cjs/sevenz/constants.js.map +1 -0
  84. package/dist/cjs/sevenz/headers.d.cts +70 -0
  85. package/dist/cjs/sevenz/headers.d.ts +70 -0
  86. package/dist/cjs/sevenz/headers.js +661 -0
  87. package/dist/cjs/sevenz/headers.js.map +1 -0
  88. package/dist/cjs/sevenz/index.d.cts +4 -0
  89. package/dist/cjs/sevenz/index.d.ts +4 -0
  90. package/dist/cjs/sevenz/index.js +33 -0
  91. package/dist/cjs/sevenz/index.js.map +1 -0
  92. package/dist/cjs/types.d.cts +37 -0
  93. package/dist/cjs/types.d.ts +37 -0
  94. package/dist/cjs/types.js +32 -0
  95. package/dist/cjs/types.js.map +1 -0
  96. package/dist/esm/FileEntry.d.ts +12 -0
  97. package/dist/esm/FileEntry.js +63 -0
  98. package/dist/esm/FileEntry.js.map +1 -0
  99. package/dist/esm/SevenZipIterator.d.ts +8 -0
  100. package/dist/esm/SevenZipIterator.js +116 -0
  101. package/dist/esm/SevenZipIterator.js.map +1 -0
  102. package/dist/esm/index.d.ts +4 -0
  103. package/dist/esm/index.js +3 -0
  104. package/dist/esm/index.js.map +1 -0
  105. package/dist/esm/lib/Lock.d.ts +11 -0
  106. package/dist/esm/lib/Lock.js +41 -0
  107. package/dist/esm/lib/Lock.js.map +1 -0
  108. package/dist/esm/lib/streamToSource.d.ts +20 -0
  109. package/dist/esm/lib/streamToSource.js +100 -0
  110. package/dist/esm/lib/streamToSource.js.map +1 -0
  111. package/dist/esm/nextEntry.d.ts +4 -0
  112. package/dist/esm/nextEntry.js +86 -0
  113. package/dist/esm/nextEntry.js.map +1 -0
  114. package/dist/esm/package.json +1 -0
  115. package/dist/esm/sevenz/NumberCodec.d.ts +58 -0
  116. package/dist/esm/sevenz/NumberCodec.js +138 -0
  117. package/dist/esm/sevenz/NumberCodec.js.map +1 -0
  118. package/dist/esm/sevenz/SevenZipParser.d.ts +96 -0
  119. package/dist/esm/sevenz/SevenZipParser.js +419 -0
  120. package/dist/esm/sevenz/SevenZipParser.js.map +1 -0
  121. package/dist/esm/sevenz/codecs/Aes.d.ts +22 -0
  122. package/dist/esm/sevenz/codecs/Aes.js +162 -0
  123. package/dist/esm/sevenz/codecs/Aes.js.map +1 -0
  124. package/dist/esm/sevenz/codecs/BZip2.d.ts +14 -0
  125. package/dist/esm/sevenz/codecs/BZip2.js +22 -0
  126. package/dist/esm/sevenz/codecs/BZip2.js.map +1 -0
  127. package/dist/esm/sevenz/codecs/Bcj.d.ts +15 -0
  128. package/dist/esm/sevenz/codecs/Bcj.js +75 -0
  129. package/dist/esm/sevenz/codecs/Bcj.js.map +1 -0
  130. package/dist/esm/sevenz/codecs/Copy.d.ts +14 -0
  131. package/dist/esm/sevenz/codecs/Copy.js +18 -0
  132. package/dist/esm/sevenz/codecs/Copy.js.map +1 -0
  133. package/dist/esm/sevenz/codecs/Deflate.d.ts +14 -0
  134. package/dist/esm/sevenz/codecs/Deflate.js +24 -0
  135. package/dist/esm/sevenz/codecs/Deflate.js.map +1 -0
  136. package/dist/esm/sevenz/codecs/Delta.d.ts +15 -0
  137. package/dist/esm/sevenz/codecs/Delta.js +41 -0
  138. package/dist/esm/sevenz/codecs/Delta.js.map +1 -0
  139. package/dist/esm/sevenz/codecs/Lzma.d.ts +14 -0
  140. package/dist/esm/sevenz/codecs/Lzma.js +38 -0
  141. package/dist/esm/sevenz/codecs/Lzma.js.map +1 -0
  142. package/dist/esm/sevenz/codecs/Lzma2.d.ts +14 -0
  143. package/dist/esm/sevenz/codecs/Lzma2.js +145 -0
  144. package/dist/esm/sevenz/codecs/Lzma2.js.map +1 -0
  145. package/dist/esm/sevenz/codecs/createBufferingDecoder.d.ts +7 -0
  146. package/dist/esm/sevenz/codecs/createBufferingDecoder.js +25 -0
  147. package/dist/esm/sevenz/codecs/createBufferingDecoder.js.map +1 -0
  148. package/dist/esm/sevenz/codecs/index.d.ts +24 -0
  149. package/dist/esm/sevenz/codecs/index.js +108 -0
  150. package/dist/esm/sevenz/codecs/index.js.map +1 -0
  151. package/dist/esm/sevenz/codecs/streams.d.ts +18 -0
  152. package/dist/esm/sevenz/codecs/streams.js +66 -0
  153. package/dist/esm/sevenz/codecs/streams.js.map +1 -0
  154. package/dist/esm/sevenz/constants.d.ts +86 -0
  155. package/dist/esm/sevenz/constants.js +131 -0
  156. package/dist/esm/sevenz/constants.js.map +1 -0
  157. package/dist/esm/sevenz/headers.d.ts +70 -0
  158. package/dist/esm/sevenz/headers.js +646 -0
  159. package/dist/esm/sevenz/headers.js.map +1 -0
  160. package/dist/esm/sevenz/index.d.ts +4 -0
  161. package/dist/esm/sevenz/index.js +5 -0
  162. package/dist/esm/sevenz/index.js.map +1 -0
  163. package/dist/esm/types.d.ts +37 -0
  164. package/dist/esm/types.js +2 -0
  165. package/dist/esm/types.js.map +1 -0
  166. package/package.json +77 -0
@@ -0,0 +1,646 @@
1
+ // 7z header parsing
2
+ // Reference: https://py7zr.readthedocs.io/en/latest/archive_format.html
3
+ import { bufferEquals, readUInt64LE, verifyCrc32Region } from 'extract-base-iterator';
4
+ import { createCodedError, ErrorCode, PropertyId, SEVENZ_MAGIC } from './constants.js';
5
+ import { readDefinedVector, readNumber } from './NumberCodec.js';
6
+ /**
7
+ * Parse the signature header (first 32 bytes)
8
+ */ export function parseSignatureHeader(buf) {
9
+ // Verify magic bytes
10
+ if (!bufferEquals(buf, 0, SEVENZ_MAGIC)) {
11
+ throw createCodedError('Not a valid 7z archive', ErrorCode.INVALID_SIGNATURE);
12
+ }
13
+ // Read version
14
+ var majorVersion = buf[6];
15
+ var minorVersion = buf[7];
16
+ // Version check - we support 0.x (current is 0.4)
17
+ if (majorVersion > 0) {
18
+ throw createCodedError(`Unsupported 7z version: ${majorVersion}.${minorVersion}`, ErrorCode.UNSUPPORTED_VERSION);
19
+ }
20
+ // Read start header CRC (CRC of the next 20 bytes)
21
+ var startHeaderCRC = buf.readUInt32LE(8);
22
+ // Verify start header CRC
23
+ if (!verifyCrc32Region(buf, 12, 20, startHeaderCRC)) {
24
+ throw createCodedError('Start header CRC mismatch', ErrorCode.CRC_MISMATCH);
25
+ }
26
+ // Read next header location
27
+ var nextHeaderOffset = readUInt64LE(buf, 12);
28
+ var nextHeaderSize = readUInt64LE(buf, 20);
29
+ var nextHeaderCRC = buf.readUInt32LE(28);
30
+ return {
31
+ majorVersion: majorVersion,
32
+ minorVersion: minorVersion,
33
+ startHeaderCRC: startHeaderCRC,
34
+ nextHeaderOffset: nextHeaderOffset,
35
+ nextHeaderSize: nextHeaderSize,
36
+ nextHeaderCRC: nextHeaderCRC
37
+ };
38
+ }
39
+ /**
40
+ * Parse the encoded header (metadata block at end of archive)
41
+ */ export function parseEncodedHeader(buf, expectedCRC) {
42
+ // Verify CRC
43
+ if (!verifyCrc32Region(buf, 0, buf.length, expectedCRC)) {
44
+ throw createCodedError('Encoded header CRC mismatch', ErrorCode.CRC_MISMATCH);
45
+ }
46
+ var offset = 0;
47
+ // Read property ID
48
+ var propertyId = buf[offset++];
49
+ // Handle kEncodedHeader - means the header itself is compressed
50
+ if (propertyId === PropertyId.kEncodedHeader) {
51
+ // Return indicator that we need to decompress
52
+ throw createCodedError('Compressed header - needs decompression first', ErrorCode.COMPRESSED_HEADER);
53
+ }
54
+ // Should be kHeader
55
+ if (propertyId !== PropertyId.kHeader) {
56
+ throw createCodedError(`Expected kHeader, got ${propertyId}`, ErrorCode.CORRUPT_HEADER);
57
+ }
58
+ // Parse header contents (after kHeader byte)
59
+ return parseHeaderContent(buf, offset);
60
+ }
61
+ /**
62
+ * Parse header content (after kHeader byte has been read)
63
+ * Used by parseEncodedHeader and for decompressed headers
64
+ */ export function parseHeaderContent(buf, offset) {
65
+ var result = {
66
+ filesInfo: []
67
+ };
68
+ // Parse header contents
69
+ while(offset < buf.length){
70
+ var propertyId = buf[offset++];
71
+ if (propertyId === PropertyId.kEnd) {
72
+ break;
73
+ }
74
+ switch(propertyId){
75
+ case PropertyId.kArchiveProperties:
76
+ offset = skipArchiveProperties(buf, offset);
77
+ break;
78
+ case PropertyId.kAdditionalStreamsInfo:
79
+ // Additional streams - skip for now
80
+ offset = skipStreamsInfo(buf, offset);
81
+ break;
82
+ case PropertyId.kMainStreamsInfo:
83
+ {
84
+ var streamsResult = parseStreamsInfo(buf, offset);
85
+ result.streamsInfo = streamsResult.info;
86
+ offset = streamsResult.offset;
87
+ break;
88
+ }
89
+ case PropertyId.kFilesInfo:
90
+ {
91
+ var filesResult = parseFilesInfo(buf, offset);
92
+ result.filesInfo = filesResult.files;
93
+ offset = filesResult.offset;
94
+ break;
95
+ }
96
+ default:
97
+ throw createCodedError(`Unknown property ID in header: ${propertyId}`, ErrorCode.CORRUPT_HEADER);
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ /**
103
+ * Parse StreamsInfo block
104
+ */ function parseStreamsInfo(buf, offset) {
105
+ var info = {
106
+ packPos: 0,
107
+ packSizes: [],
108
+ folders: [],
109
+ numUnpackStreamsPerFolder: [],
110
+ unpackSizes: []
111
+ };
112
+ while(offset < buf.length){
113
+ var propertyId = buf[offset++];
114
+ if (propertyId === PropertyId.kEnd) {
115
+ break;
116
+ }
117
+ switch(propertyId){
118
+ case PropertyId.kPackInfo:
119
+ {
120
+ var packResult = parsePackInfo(buf, offset);
121
+ info.packPos = packResult.packPos;
122
+ info.packSizes = packResult.packSizes;
123
+ info.packCRCs = packResult.packCRCs;
124
+ offset = packResult.offset;
125
+ break;
126
+ }
127
+ case PropertyId.kUnpackInfo:
128
+ {
129
+ var unpackResult = parseUnpackInfo(buf, offset);
130
+ info.folders = unpackResult.folders;
131
+ offset = unpackResult.offset;
132
+ break;
133
+ }
134
+ case PropertyId.kSubStreamsInfo:
135
+ {
136
+ var subResult = parseSubStreamsInfo(buf, offset, info.folders);
137
+ info.numUnpackStreamsPerFolder = subResult.numUnpackStreamsPerFolder;
138
+ info.unpackSizes = subResult.unpackSizes;
139
+ info.unpackCRCs = subResult.unpackCRCs;
140
+ offset = subResult.offset;
141
+ break;
142
+ }
143
+ default:
144
+ throw createCodedError(`Unknown property ID in StreamsInfo: ${propertyId}`, ErrorCode.CORRUPT_HEADER);
145
+ }
146
+ }
147
+ // If no SubStreamsInfo, each folder produces one file
148
+ if (info.unpackSizes.length === 0 && info.folders.length > 0) {
149
+ for(var i = 0; i < info.folders.length; i++){
150
+ var folder = info.folders[i];
151
+ // Get the final unpack size (last coder's output)
152
+ var finalSize = folder.unpackSizes[folder.unpackSizes.length - 1];
153
+ info.unpackSizes.push(finalSize);
154
+ info.numUnpackStreamsPerFolder.push(1);
155
+ }
156
+ }
157
+ return {
158
+ info: info,
159
+ offset: offset
160
+ };
161
+ }
162
+ /**
163
+ * Parse PackInfo block
164
+ */ function parsePackInfo(buf, offset) {
165
+ // Pack position
166
+ var packPosResult = readNumber(buf, offset);
167
+ var packPos = packPosResult.value;
168
+ offset += packPosResult.bytesRead;
169
+ // Number of pack streams
170
+ var numPackResult = readNumber(buf, offset);
171
+ var numPackStreams = numPackResult.value;
172
+ offset += numPackResult.bytesRead;
173
+ var packSizes = [];
174
+ var packCRCs;
175
+ while(offset < buf.length){
176
+ var propertyId = buf[offset++];
177
+ if (propertyId === PropertyId.kEnd) {
178
+ break;
179
+ }
180
+ if (propertyId === PropertyId.kSize) {
181
+ for(var i = 0; i < numPackStreams; i++){
182
+ var sizeResult = readNumber(buf, offset);
183
+ packSizes.push(sizeResult.value);
184
+ offset += sizeResult.bytesRead;
185
+ }
186
+ } else if (propertyId === PropertyId.kCRC) {
187
+ packCRCs = [];
188
+ var definedResult = readDefinedVector(buf, offset, numPackStreams);
189
+ offset += definedResult.bytesRead;
190
+ for(var j = 0; j < numPackStreams; j++){
191
+ if (definedResult.defined[j]) {
192
+ packCRCs.push(buf.readUInt32LE(offset));
193
+ offset += 4;
194
+ } else {
195
+ packCRCs.push(0);
196
+ }
197
+ }
198
+ }
199
+ }
200
+ return {
201
+ packPos: packPos,
202
+ packSizes: packSizes,
203
+ packCRCs: packCRCs,
204
+ offset: offset
205
+ };
206
+ }
207
+ /**
208
+ * Parse UnpackInfo block
209
+ */ function parseUnpackInfo(buf, offset) {
210
+ var folders = [];
211
+ while(offset < buf.length){
212
+ var propertyId = buf[offset++];
213
+ if (propertyId === PropertyId.kEnd) {
214
+ break;
215
+ }
216
+ if (propertyId === PropertyId.kFolder) {
217
+ // Number of folders
218
+ var numFoldersResult = readNumber(buf, offset);
219
+ var numFolders = numFoldersResult.value;
220
+ offset += numFoldersResult.bytesRead;
221
+ // External flag
222
+ var external = buf[offset++];
223
+ if (external !== 0) {
224
+ throw createCodedError('External folders not supported', ErrorCode.CORRUPT_HEADER);
225
+ }
226
+ // Parse each folder
227
+ for(var i = 0; i < numFolders; i++){
228
+ var folderResult = parseFolder(buf, offset);
229
+ folders.push(folderResult.folder);
230
+ offset = folderResult.offset;
231
+ }
232
+ } else if (propertyId === PropertyId.kCodersUnpackSize) {
233
+ // Unpack sizes for each coder output
234
+ for(var j = 0; j < folders.length; j++){
235
+ var folder = folders[j];
236
+ folder.unpackSizes = [];
237
+ // One unpack size per coder output stream
238
+ var numOutputs = 0;
239
+ for(var k = 0; k < folder.coders.length; k++){
240
+ numOutputs += folder.coders[k].numOutStreams;
241
+ }
242
+ for(var l = 0; l < numOutputs; l++){
243
+ var sizeResult = readNumber(buf, offset);
244
+ folder.unpackSizes.push(sizeResult.value);
245
+ offset += sizeResult.bytesRead;
246
+ }
247
+ }
248
+ } else if (propertyId === PropertyId.kCRC) {
249
+ // CRCs for folders
250
+ var definedResult = readDefinedVector(buf, offset, folders.length);
251
+ offset += definedResult.bytesRead;
252
+ for(var m = 0; m < folders.length; m++){
253
+ folders[m].hasCRC = definedResult.defined[m];
254
+ if (definedResult.defined[m]) {
255
+ folders[m].unpackCRC = buf.readUInt32LE(offset);
256
+ offset += 4;
257
+ }
258
+ }
259
+ }
260
+ }
261
+ return {
262
+ folders: folders,
263
+ offset: offset
264
+ };
265
+ }
266
+ /**
267
+ * Parse a single Folder structure
268
+ */ function parseFolder(buf, offset) {
269
+ // Number of coders
270
+ var numCodersResult = readNumber(buf, offset);
271
+ var numCoders = numCodersResult.value;
272
+ offset += numCodersResult.bytesRead;
273
+ var coders = [];
274
+ var numInStreamsTotal = 0;
275
+ var numOutStreamsTotal = 0;
276
+ for(var i = 0; i < numCoders; i++){
277
+ var flags = buf[offset++];
278
+ var idSize = flags & 0x0f;
279
+ var isComplex = (flags & 0x10) !== 0;
280
+ var hasAttributes = (flags & 0x20) !== 0;
281
+ // Read codec ID
282
+ var id = [];
283
+ for(var j = 0; j < idSize; j++){
284
+ id.push(buf[offset++]);
285
+ }
286
+ var numInStreams = 1;
287
+ var numOutStreams = 1;
288
+ if (isComplex) {
289
+ var inResult = readNumber(buf, offset);
290
+ numInStreams = inResult.value;
291
+ offset += inResult.bytesRead;
292
+ var outResult = readNumber(buf, offset);
293
+ numOutStreams = outResult.value;
294
+ offset += outResult.bytesRead;
295
+ }
296
+ var properties;
297
+ if (hasAttributes) {
298
+ var propsLenResult = readNumber(buf, offset);
299
+ offset += propsLenResult.bytesRead;
300
+ properties = buf.slice(offset, offset + propsLenResult.value);
301
+ offset += propsLenResult.value;
302
+ }
303
+ coders.push({
304
+ id: id,
305
+ numInStreams: numInStreams,
306
+ numOutStreams: numOutStreams,
307
+ properties: properties
308
+ });
309
+ numInStreamsTotal += numInStreams;
310
+ numOutStreamsTotal += numOutStreams;
311
+ }
312
+ // Bind pairs
313
+ var numBindPairs = numOutStreamsTotal - 1;
314
+ var bindPairs = [];
315
+ for(var k = 0; k < numBindPairs; k++){
316
+ var inIndexResult = readNumber(buf, offset);
317
+ offset += inIndexResult.bytesRead;
318
+ var outIndexResult = readNumber(buf, offset);
319
+ offset += outIndexResult.bytesRead;
320
+ bindPairs.push({
321
+ inIndex: inIndexResult.value,
322
+ outIndex: outIndexResult.value
323
+ });
324
+ }
325
+ // Packed stream indices
326
+ var numPackedStreams = numInStreamsTotal - numBindPairs;
327
+ var packedStreams = [];
328
+ if (numPackedStreams === 1) {
329
+ // Find the unbound input stream
330
+ for(var m = 0; m < numInStreamsTotal; m++){
331
+ var isBound = false;
332
+ for(var n = 0; n < bindPairs.length; n++){
333
+ if (bindPairs[n].inIndex === m) {
334
+ isBound = true;
335
+ break;
336
+ }
337
+ }
338
+ if (!isBound) {
339
+ packedStreams.push(m);
340
+ break;
341
+ }
342
+ }
343
+ } else {
344
+ for(var p = 0; p < numPackedStreams; p++){
345
+ var indexResult = readNumber(buf, offset);
346
+ packedStreams.push(indexResult.value);
347
+ offset += indexResult.bytesRead;
348
+ }
349
+ }
350
+ return {
351
+ folder: {
352
+ coders: coders,
353
+ bindPairs: bindPairs,
354
+ packedStreams: packedStreams,
355
+ unpackSizes: [],
356
+ hasCRC: false
357
+ },
358
+ offset: offset
359
+ };
360
+ }
361
+ /**
362
+ * Parse SubStreamsInfo block
363
+ */ function parseSubStreamsInfo(buf, offset, folders) {
364
+ var numUnpackStreamsPerFolder = [];
365
+ var unpackSizes = [];
366
+ var unpackCRCs;
367
+ // Default: 1 file per folder
368
+ for(var i = 0; i < folders.length; i++){
369
+ numUnpackStreamsPerFolder.push(1);
370
+ }
371
+ while(offset < buf.length){
372
+ var propertyId = buf[offset++];
373
+ if (propertyId === PropertyId.kEnd) {
374
+ break;
375
+ }
376
+ if (propertyId === PropertyId.kNumUnpackStream) {
377
+ for(var j = 0; j < folders.length; j++){
378
+ var numResult = readNumber(buf, offset);
379
+ numUnpackStreamsPerFolder[j] = numResult.value;
380
+ offset += numResult.bytesRead;
381
+ }
382
+ } else if (propertyId === PropertyId.kSize) {
383
+ for(var k = 0; k < folders.length; k++){
384
+ var numStreams = numUnpackStreamsPerFolder[k];
385
+ if (numStreams === 0) continue;
386
+ // Read sizes for all but last stream in folder (last is calculated)
387
+ var remaining = folders[k].unpackSizes[folders[k].unpackSizes.length - 1];
388
+ for(var l = 0; l < numStreams - 1; l++){
389
+ var sizeResult = readNumber(buf, offset);
390
+ unpackSizes.push(sizeResult.value);
391
+ remaining -= sizeResult.value;
392
+ offset += sizeResult.bytesRead;
393
+ }
394
+ // Last stream size is remainder
395
+ unpackSizes.push(remaining);
396
+ }
397
+ } else if (propertyId === PropertyId.kCRC) {
398
+ // Count files that need CRC
399
+ var numFiles = 0;
400
+ for(var m = 0; m < folders.length; m++){
401
+ var numStreamsInFolder = numUnpackStreamsPerFolder[m];
402
+ // Only count if folder doesn't have CRC or has multiple streams
403
+ if (!folders[m].hasCRC || numStreamsInFolder > 1) {
404
+ numFiles += numStreamsInFolder;
405
+ }
406
+ }
407
+ unpackCRCs = [];
408
+ var definedResult = readDefinedVector(buf, offset, numFiles);
409
+ offset += definedResult.bytesRead;
410
+ for(var n = 0; n < numFiles; n++){
411
+ if (definedResult.defined[n]) {
412
+ unpackCRCs.push(buf.readUInt32LE(offset));
413
+ offset += 4;
414
+ } else {
415
+ unpackCRCs.push(0);
416
+ }
417
+ }
418
+ }
419
+ }
420
+ // If no sizes specified, use folder unpack sizes
421
+ if (unpackSizes.length === 0) {
422
+ for(var p = 0; p < folders.length; p++){
423
+ var folder = folders[p];
424
+ unpackSizes.push(folder.unpackSizes[folder.unpackSizes.length - 1]);
425
+ }
426
+ }
427
+ return {
428
+ numUnpackStreamsPerFolder: numUnpackStreamsPerFolder,
429
+ unpackSizes: unpackSizes,
430
+ unpackCRCs: unpackCRCs,
431
+ offset: offset
432
+ };
433
+ }
434
+ /**
435
+ * Parse FilesInfo block
436
+ */ function parseFilesInfo(buf, offset) {
437
+ // Number of files
438
+ var numFilesResult = readNumber(buf, offset);
439
+ var numFiles = numFilesResult.value;
440
+ offset += numFilesResult.bytesRead;
441
+ // Initialize files array
442
+ var files = [];
443
+ for(var i = 0; i < numFiles; i++){
444
+ files.push({
445
+ name: '',
446
+ size: 0,
447
+ isDirectory: false,
448
+ isAntiFile: false,
449
+ hasStream: true
450
+ });
451
+ }
452
+ var emptyStreamFlags = [];
453
+ var emptyFileFlags = [];
454
+ while(offset < buf.length){
455
+ var propertyId = buf[offset++];
456
+ if (propertyId === PropertyId.kEnd) {
457
+ break;
458
+ }
459
+ // Read property size
460
+ var propSizeResult = readNumber(buf, offset);
461
+ var propSize = propSizeResult.value;
462
+ offset += propSizeResult.bytesRead;
463
+ var propEnd = offset + propSize;
464
+ switch(propertyId){
465
+ case PropertyId.kEmptyStream:
466
+ emptyStreamFlags = readBoolVector(buf, offset, numFiles);
467
+ // Mark files that don't have streams
468
+ for(var j = 0; j < numFiles; j++){
469
+ files[j].hasStream = !emptyStreamFlags[j];
470
+ }
471
+ break;
472
+ case PropertyId.kEmptyFile:
473
+ {
474
+ var numEmptyStreams = 0;
475
+ for(var k = 0; k < emptyStreamFlags.length; k++){
476
+ if (emptyStreamFlags[k]) numEmptyStreams++;
477
+ }
478
+ emptyFileFlags = readBoolVector(buf, offset, numEmptyStreams);
479
+ break;
480
+ }
481
+ case PropertyId.kAnti:
482
+ {
483
+ var numAnti = 0;
484
+ for(var l = 0; l < emptyStreamFlags.length; l++){
485
+ if (emptyStreamFlags[l]) numAnti++;
486
+ }
487
+ var antiFlags = readBoolVector(buf, offset, numAnti);
488
+ var antiIdx = 0;
489
+ for(var m = 0; m < numFiles; m++){
490
+ if (emptyStreamFlags[m]) {
491
+ files[m].isAntiFile = antiFlags[antiIdx++];
492
+ }
493
+ }
494
+ break;
495
+ }
496
+ case PropertyId.kName:
497
+ offset = parseFileNames(buf, offset, files);
498
+ break;
499
+ case PropertyId.kCTime:
500
+ offset = parseFileTimes(buf, offset, files, 'ctime');
501
+ break;
502
+ case PropertyId.kATime:
503
+ offset = parseFileTimes(buf, offset, files, 'atime');
504
+ break;
505
+ case PropertyId.kMTime:
506
+ offset = parseFileTimes(buf, offset, files, 'mtime');
507
+ break;
508
+ case PropertyId.kWinAttributes:
509
+ offset = parseAttributes(buf, offset, files);
510
+ break;
511
+ case PropertyId.kDummy:
512
+ break;
513
+ default:
514
+ break;
515
+ }
516
+ offset = propEnd;
517
+ }
518
+ // Determine directories from empty stream + not empty file
519
+ var emptyIdx = 0;
520
+ for(var n = 0; n < numFiles; n++){
521
+ if (emptyStreamFlags[n]) {
522
+ // Empty stream - could be directory or empty file
523
+ if (emptyIdx < emptyFileFlags.length && emptyFileFlags[emptyIdx]) {
524
+ files[n].isDirectory = false; // Empty file
525
+ } else {
526
+ files[n].isDirectory = true; // Directory
527
+ }
528
+ emptyIdx++;
529
+ }
530
+ }
531
+ return {
532
+ files: files,
533
+ offset: offset
534
+ };
535
+ }
536
+ /**
537
+ * Read a boolean vector (bit-packed)
538
+ */ function readBoolVector(buf, offset, count) {
539
+ var result = [];
540
+ var byteIdx = 0;
541
+ var bitMask = 0x80;
542
+ for(var i = 0; i < count; i++){
543
+ result.push((buf[offset + byteIdx] & bitMask) !== 0);
544
+ bitMask = bitMask >>> 1;
545
+ if (bitMask === 0) {
546
+ bitMask = 0x80;
547
+ byteIdx++;
548
+ }
549
+ }
550
+ return result;
551
+ }
552
+ /**
553
+ * Parse file names (UTF-16LE encoded)
554
+ */ function parseFileNames(buf, offset, files) {
555
+ // External flag
556
+ var external = buf[offset++];
557
+ if (external !== 0) {
558
+ throw createCodedError('External file names not supported', ErrorCode.CORRUPT_HEADER);
559
+ }
560
+ // Names are UTF-16LE, null-terminated
561
+ for(var i = 0; i < files.length; i++){
562
+ var nameChars = [];
563
+ while(offset < buf.length){
564
+ var charCode = buf.readUInt16LE(offset);
565
+ offset += 2;
566
+ if (charCode === 0) break;
567
+ nameChars.push(charCode);
568
+ }
569
+ files[i].name = String.fromCharCode.apply(null, nameChars);
570
+ }
571
+ return offset;
572
+ }
573
+ /**
574
+ * Parse file times (Windows FILETIME format)
575
+ */ function parseFileTimes(buf, offset, files, timeType) {
576
+ // Read defined vector (allDefined byte + optional bitmask)
577
+ var definedResult = readDefinedVector(buf, offset, files.length);
578
+ offset += definedResult.bytesRead;
579
+ // External flag - 0x00 means data follows inline, non-zero means external stream
580
+ var external = buf[offset++];
581
+ if (external !== 0) {
582
+ throw createCodedError('External file times not supported', ErrorCode.UNSUPPORTED_FEATURE);
583
+ }
584
+ // Read times
585
+ for(var i = 0; i < files.length; i++){
586
+ if (definedResult.defined[i]) {
587
+ var filetime = readUInt64LE(buf, offset);
588
+ offset += 8;
589
+ // Convert FILETIME (100ns since 1601) to JavaScript Date
590
+ // FILETIME epoch: 1601-01-01
591
+ // JS Date epoch: 1970-01-01
592
+ // Difference: 11644473600 seconds
593
+ var ms = filetime / 10000 - 11644473600000;
594
+ files[i][timeType] = new Date(ms);
595
+ }
596
+ }
597
+ return offset;
598
+ }
599
+ /**
600
+ * Parse Windows file attributes
601
+ */ function parseAttributes(buf, offset, files) {
602
+ // Read defined vector (allDefined byte + optional bitmask)
603
+ var definedResult = readDefinedVector(buf, offset, files.length);
604
+ offset += definedResult.bytesRead;
605
+ // External flag - 0x00 means data follows inline, non-zero means external stream
606
+ var external = buf[offset++];
607
+ if (external !== 0) {
608
+ throw createCodedError('External file attributes not supported', ErrorCode.UNSUPPORTED_FEATURE);
609
+ }
610
+ // Read attributes
611
+ for(var i = 0; i < files.length; i++){
612
+ if (definedResult.defined[i]) {
613
+ files[i].attributes = buf.readUInt32LE(offset);
614
+ offset += 4;
615
+ }
616
+ }
617
+ return offset;
618
+ }
619
+ /**
620
+ * Skip archive properties block
621
+ */ function skipArchiveProperties(buf, offset) {
622
+ while(offset < buf.length){
623
+ var propertyId = buf[offset++];
624
+ if (propertyId === PropertyId.kEnd) {
625
+ break;
626
+ }
627
+ var sizeResult = readNumber(buf, offset);
628
+ offset += sizeResult.bytesRead + sizeResult.value;
629
+ }
630
+ return offset;
631
+ }
632
+ /**
633
+ * Skip streams info block (for additional streams)
634
+ */ function skipStreamsInfo(buf, offset) {
635
+ while(offset < buf.length){
636
+ var propertyId = buf[offset++];
637
+ if (propertyId === PropertyId.kEnd) {
638
+ break;
639
+ }
640
+ // For nested structures, recurse
641
+ if (propertyId === PropertyId.kPackInfo || propertyId === PropertyId.kUnpackInfo || propertyId === PropertyId.kSubStreamsInfo) {
642
+ offset = skipStreamsInfo(buf, offset);
643
+ }
644
+ }
645
+ return offset;
646
+ }