diff options
Diffstat (limited to 'CPP/7zip/Archive/DmgHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/DmgHandler.cpp | 1785 |
1 files changed, 1785 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/DmgHandler.cpp b/CPP/7zip/Archive/DmgHandler.cpp new file mode 100644 index 0000000..1f2ca26 --- /dev/null +++ b/CPP/7zip/Archive/DmgHandler.cpp | |||
@@ -0,0 +1,1785 @@ | |||
1 | // DmgHandler.cpp | ||
2 | |||
3 | #include "StdAfx.h" | ||
4 | |||
5 | #include "../../../C/CpuArch.h" | ||
6 | |||
7 | #include "../../Common/ComTry.h" | ||
8 | #include "../../Common/IntToString.h" | ||
9 | #include "../../Common/MyXml.h" | ||
10 | #include "../../Common/UTFConvert.h" | ||
11 | |||
12 | #include "../../Windows/PropVariant.h" | ||
13 | |||
14 | #include "../Common/LimitedStreams.h" | ||
15 | #include "../Common/ProgressUtils.h" | ||
16 | #include "../Common/RegisterArc.h" | ||
17 | #include "../Common/StreamObjects.h" | ||
18 | #include "../Common/StreamUtils.h" | ||
19 | |||
20 | #include "../Compress/BZip2Decoder.h" | ||
21 | #include "../Compress/CopyCoder.h" | ||
22 | #include "../Compress/LzfseDecoder.h" | ||
23 | #include "../Compress/ZlibDecoder.h" | ||
24 | |||
25 | #include "Common/OutStreamWithCRC.h" | ||
26 | |||
27 | // #define DMG_SHOW_RAW | ||
28 | |||
29 | // #include <stdio.h> | ||
30 | #define PRF(x) // x | ||
31 | |||
32 | #define Get16(p) GetBe16(p) | ||
33 | #define Get32(p) GetBe32(p) | ||
34 | #define Get64(p) GetBe64(p) | ||
35 | |||
36 | Byte *Base64ToBin(Byte *dest, const char *src); | ||
37 | |||
38 | namespace NArchive { | ||
39 | namespace NDmg { | ||
40 | |||
41 | |||
42 | static const UInt32 METHOD_ZERO_0 = 0; | ||
43 | static const UInt32 METHOD_COPY = 1; | ||
44 | static const UInt32 METHOD_ZERO_2 = 2; // without file CRC calculation | ||
45 | static const UInt32 METHOD_ADC = 0x80000004; | ||
46 | static const UInt32 METHOD_ZLIB = 0x80000005; | ||
47 | static const UInt32 METHOD_BZIP2 = 0x80000006; | ||
48 | static const UInt32 METHOD_LZFSE = 0x80000007; | ||
49 | static const UInt32 METHOD_COMMENT = 0x7FFFFFFE; // is used to comment "+beg" and "+end" in extra field. | ||
50 | static const UInt32 METHOD_END = 0xFFFFFFFF; | ||
51 | |||
52 | |||
53 | struct CBlock | ||
54 | { | ||
55 | UInt32 Type; | ||
56 | UInt64 UnpPos; | ||
57 | UInt64 UnpSize; | ||
58 | UInt64 PackPos; | ||
59 | UInt64 PackSize; | ||
60 | |||
61 | UInt64 GetNextPackOffset() const { return PackPos + PackSize; } | ||
62 | UInt64 GetNextUnpPos() const { return UnpPos + UnpSize; } | ||
63 | |||
64 | bool IsZeroMethod() const { return Type == METHOD_ZERO_0 || Type == METHOD_ZERO_2; } | ||
65 | bool ThereAreDataInBlock() const { return Type != METHOD_COMMENT && Type != METHOD_END; } | ||
66 | }; | ||
67 | |||
68 | static const UInt32 kCheckSumType_CRC = 2; | ||
69 | |||
70 | static const size_t kChecksumSize_Max = 0x80; | ||
71 | |||
72 | struct CChecksum | ||
73 | { | ||
74 | UInt32 Type; | ||
75 | UInt32 NumBits; | ||
76 | Byte Data[kChecksumSize_Max]; | ||
77 | |||
78 | bool IsCrc32() const { return Type == kCheckSumType_CRC && NumBits == 32; } | ||
79 | UInt32 GetCrc32() const { return Get32(Data); } | ||
80 | void Parse(const Byte *p); | ||
81 | }; | ||
82 | |||
83 | void CChecksum::Parse(const Byte *p) | ||
84 | { | ||
85 | Type = Get32(p); | ||
86 | NumBits = Get32(p + 4); | ||
87 | memcpy(Data, p + 8, kChecksumSize_Max); | ||
88 | }; | ||
89 | |||
90 | struct CFile | ||
91 | { | ||
92 | UInt64 Size; | ||
93 | UInt64 PackSize; | ||
94 | UInt64 StartPos; | ||
95 | AString Name; | ||
96 | CRecordVector<CBlock> Blocks; | ||
97 | CChecksum Checksum; | ||
98 | bool FullFileChecksum; | ||
99 | |||
100 | HRESULT Parse(const Byte *p, UInt32 size); | ||
101 | }; | ||
102 | |||
103 | #ifdef DMG_SHOW_RAW | ||
104 | struct CExtraFile | ||
105 | { | ||
106 | CByteBuffer Data; | ||
107 | AString Name; | ||
108 | }; | ||
109 | #endif | ||
110 | |||
111 | |||
112 | struct CForkPair | ||
113 | { | ||
114 | UInt64 Offset; | ||
115 | UInt64 Len; | ||
116 | |||
117 | void Parse(const Byte *p) | ||
118 | { | ||
119 | Offset = Get64(p); | ||
120 | Len = Get64(p + 8); | ||
121 | } | ||
122 | |||
123 | bool UpdateTop(UInt64 limit, UInt64 &top) | ||
124 | { | ||
125 | if (Offset > limit || Len > limit - Offset) | ||
126 | return false; | ||
127 | UInt64 top2 = Offset + Len; | ||
128 | if (top <= top2) | ||
129 | top = top2; | ||
130 | return true; | ||
131 | } | ||
132 | }; | ||
133 | |||
134 | |||
135 | class CHandler: | ||
136 | public IInArchive, | ||
137 | public IInArchiveGetStream, | ||
138 | public CMyUnknownImp | ||
139 | { | ||
140 | CMyComPtr<IInStream> _inStream; | ||
141 | CObjectVector<CFile> _files; | ||
142 | bool _masterCrcError; | ||
143 | bool _headersError; | ||
144 | |||
145 | UInt32 _dataStartOffset; | ||
146 | UInt64 _startPos; | ||
147 | UInt64 _phySize; | ||
148 | |||
149 | AString _name; | ||
150 | |||
151 | #ifdef DMG_SHOW_RAW | ||
152 | CObjectVector<CExtraFile> _extras; | ||
153 | #endif | ||
154 | |||
155 | HRESULT ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf); | ||
156 | bool ParseBlob(const CByteBuffer &data); | ||
157 | HRESULT Open2(IInStream *stream); | ||
158 | HRESULT Extract(IInStream *stream); | ||
159 | public: | ||
160 | MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) | ||
161 | INTERFACE_IInArchive(;) | ||
162 | STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); | ||
163 | }; | ||
164 | |||
165 | // that limit can be increased, if there are such dmg files | ||
166 | static const size_t kXmlSizeMax = 0xFFFF0000; // 4 GB - 64 KB; | ||
167 | |||
168 | struct CMethods | ||
169 | { | ||
170 | CRecordVector<UInt32> Types; | ||
171 | CRecordVector<UInt32> ChecksumTypes; | ||
172 | |||
173 | void Update(const CFile &file); | ||
174 | void GetString(AString &s) const; | ||
175 | }; | ||
176 | |||
177 | void CMethods::Update(const CFile &file) | ||
178 | { | ||
179 | ChecksumTypes.AddToUniqueSorted(file.Checksum.Type); | ||
180 | FOR_VECTOR (i, file.Blocks) | ||
181 | Types.AddToUniqueSorted(file.Blocks[i].Type); | ||
182 | } | ||
183 | |||
184 | void CMethods::GetString(AString &res) const | ||
185 | { | ||
186 | res.Empty(); | ||
187 | |||
188 | unsigned i; | ||
189 | |||
190 | for (i = 0; i < Types.Size(); i++) | ||
191 | { | ||
192 | const UInt32 type = Types[i]; | ||
193 | if (type == METHOD_COMMENT || type == METHOD_END) | ||
194 | continue; | ||
195 | char buf[16]; | ||
196 | const char *s; | ||
197 | switch (type) | ||
198 | { | ||
199 | case METHOD_ZERO_0: s = "Zero0"; break; | ||
200 | case METHOD_ZERO_2: s = "Zero2"; break; | ||
201 | case METHOD_COPY: s = "Copy"; break; | ||
202 | case METHOD_ADC: s = "ADC"; break; | ||
203 | case METHOD_ZLIB: s = "ZLIB"; break; | ||
204 | case METHOD_BZIP2: s = "BZip2"; break; | ||
205 | case METHOD_LZFSE: s = "LZFSE"; break; | ||
206 | default: ConvertUInt32ToString(type, buf); s = buf; | ||
207 | } | ||
208 | res.Add_OptSpaced(s); | ||
209 | } | ||
210 | |||
211 | for (i = 0; i < ChecksumTypes.Size(); i++) | ||
212 | { | ||
213 | res.Add_Space_if_NotEmpty(); | ||
214 | UInt32 type = ChecksumTypes[i]; | ||
215 | switch (type) | ||
216 | { | ||
217 | case kCheckSumType_CRC: res += "CRC"; break; | ||
218 | default: | ||
219 | res += "Check"; | ||
220 | res.Add_UInt32(type); | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | struct CAppleName | ||
226 | { | ||
227 | bool IsFs; | ||
228 | const char *Ext; | ||
229 | const char *AppleName; | ||
230 | }; | ||
231 | |||
232 | static const CAppleName k_Names[] = | ||
233 | { | ||
234 | { true, "hfs", "Apple_HFS" }, | ||
235 | { true, "hfsx", "Apple_HFSX" }, | ||
236 | { true, "ufs", "Apple_UFS" }, | ||
237 | { true, "apfs", "Apple_APFS" }, | ||
238 | |||
239 | // efi_sys partition is FAT32, but it's not main file. So we use (IsFs = false) | ||
240 | { false, "efi_sys", "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" }, | ||
241 | |||
242 | { false, "free", "Apple_Free" }, | ||
243 | { false, "ddm", "DDM" }, | ||
244 | { false, NULL, "Apple_partition_map" }, | ||
245 | { false, NULL, " GPT " }, | ||
246 | { false, NULL, "MBR" }, | ||
247 | { false, NULL, "Driver" }, | ||
248 | { false, NULL, "Patches" } | ||
249 | }; | ||
250 | |||
251 | static const unsigned kNumAppleNames = ARRAY_SIZE(k_Names); | ||
252 | |||
253 | static const Byte kProps[] = | ||
254 | { | ||
255 | kpidPath, | ||
256 | kpidSize, | ||
257 | kpidPackSize, | ||
258 | kpidCRC, | ||
259 | kpidComment, | ||
260 | kpidMethod | ||
261 | // kpidOffset | ||
262 | }; | ||
263 | |||
264 | IMP_IInArchive_Props | ||
265 | |||
266 | static const Byte kArcProps[] = | ||
267 | { | ||
268 | kpidMethod, | ||
269 | kpidNumBlocks, | ||
270 | kpidComment | ||
271 | }; | ||
272 | |||
273 | STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) | ||
274 | { | ||
275 | COM_TRY_BEGIN | ||
276 | NWindows::NCOM::CPropVariant prop; | ||
277 | switch (propID) | ||
278 | { | ||
279 | case kpidMethod: | ||
280 | { | ||
281 | CMethods m; | ||
282 | FOR_VECTOR (i, _files) | ||
283 | m.Update(_files[i]); | ||
284 | AString s; | ||
285 | m.GetString(s); | ||
286 | if (!s.IsEmpty()) | ||
287 | prop = s; | ||
288 | break; | ||
289 | } | ||
290 | case kpidNumBlocks: | ||
291 | { | ||
292 | UInt64 numBlocks = 0; | ||
293 | FOR_VECTOR (i, _files) | ||
294 | numBlocks += _files[i].Blocks.Size(); | ||
295 | prop = numBlocks; | ||
296 | break; | ||
297 | } | ||
298 | case kpidMainSubfile: | ||
299 | { | ||
300 | int mainIndex = -1; | ||
301 | unsigned numFS = 0; | ||
302 | unsigned numUnknown = 0; | ||
303 | FOR_VECTOR (i, _files) | ||
304 | { | ||
305 | const AString &name = _files[i].Name; | ||
306 | unsigned n; | ||
307 | for (n = 0; n < kNumAppleNames; n++) | ||
308 | { | ||
309 | const CAppleName &appleName = k_Names[n]; | ||
310 | // if (name.Find(appleName.AppleName) >= 0) | ||
311 | if (strstr(name, appleName.AppleName)) | ||
312 | { | ||
313 | if (appleName.IsFs) | ||
314 | { | ||
315 | numFS++; | ||
316 | mainIndex = i; | ||
317 | } | ||
318 | break; | ||
319 | } | ||
320 | } | ||
321 | if (n == kNumAppleNames) | ||
322 | { | ||
323 | mainIndex = i; | ||
324 | numUnknown++; | ||
325 | } | ||
326 | } | ||
327 | if (numFS + numUnknown == 1) | ||
328 | prop = (UInt32)mainIndex; | ||
329 | break; | ||
330 | } | ||
331 | case kpidWarning: | ||
332 | if (_masterCrcError) | ||
333 | prop = "Master CRC error"; | ||
334 | break; | ||
335 | |||
336 | case kpidWarningFlags: | ||
337 | { | ||
338 | UInt32 v = 0; | ||
339 | if (_headersError) v |= kpv_ErrorFlags_HeadersError; | ||
340 | if (v != 0) | ||
341 | prop = v; | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | case kpidOffset: prop = _startPos; break; | ||
346 | case kpidPhySize: prop = _phySize; break; | ||
347 | |||
348 | case kpidComment: | ||
349 | if (!_name.IsEmpty() && _name.Len() < 256) | ||
350 | prop = _name; | ||
351 | break; | ||
352 | |||
353 | case kpidName: | ||
354 | if (!_name.IsEmpty() && _name.Len() < 256) | ||
355 | { | ||
356 | prop = _name + ".dmg"; | ||
357 | } | ||
358 | break; | ||
359 | } | ||
360 | prop.Detach(value); | ||
361 | return S_OK; | ||
362 | COM_TRY_END | ||
363 | } | ||
364 | |||
365 | IMP_IInArchive_ArcProps | ||
366 | |||
367 | HRESULT CFile::Parse(const Byte *p, UInt32 size) | ||
368 | { | ||
369 | const UInt32 kHeadSize = 0xCC; | ||
370 | if (size < kHeadSize) | ||
371 | return S_FALSE; | ||
372 | if (Get32(p) != 0x6D697368) // "mish" signature | ||
373 | return S_FALSE; | ||
374 | if (Get32(p + 4) != 1) // version | ||
375 | return S_FALSE; | ||
376 | // UInt64 firstSectorNumber = Get64(p + 8); | ||
377 | UInt64 numSectors = Get64(p + 0x10); | ||
378 | |||
379 | StartPos = Get64(p + 0x18); | ||
380 | |||
381 | // UInt32 decompressedBufRequested = Get32(p + 0x20); // ??? | ||
382 | // UInt32 blocksDescriptor = Get32(p + 0x24); // number starting from -1? | ||
383 | // char Reserved1[24]; | ||
384 | |||
385 | Checksum.Parse(p + 0x40); | ||
386 | PRF(printf("\n\nChecksum Type = %2d", Checksum.Type)); | ||
387 | |||
388 | UInt32 numBlocks = Get32(p + 0xC8); | ||
389 | if (numBlocks > ((UInt32)1 << 28)) | ||
390 | return S_FALSE; | ||
391 | |||
392 | const UInt32 kRecordSize = 40; | ||
393 | if (numBlocks * kRecordSize + kHeadSize != size) | ||
394 | return S_FALSE; | ||
395 | |||
396 | PackSize = 0; | ||
397 | Size = 0; | ||
398 | Blocks.ClearAndReserve(numBlocks); | ||
399 | FullFileChecksum = true; | ||
400 | |||
401 | p += kHeadSize; | ||
402 | UInt32 i; | ||
403 | |||
404 | for (i = 0; i < numBlocks; i++, p += kRecordSize) | ||
405 | { | ||
406 | CBlock b; | ||
407 | b.Type = Get32(p); | ||
408 | b.UnpPos = Get64(p + 0x08) << 9; | ||
409 | b.UnpSize = Get64(p + 0x10) << 9; | ||
410 | b.PackPos = Get64(p + 0x18); | ||
411 | b.PackSize = Get64(p + 0x20); | ||
412 | |||
413 | // b.PackPos can be 0 for some types. So we don't check it | ||
414 | if (!Blocks.IsEmpty()) | ||
415 | if (b.UnpPos != Blocks.Back().GetNextUnpPos()) | ||
416 | return S_FALSE; | ||
417 | |||
418 | PRF(printf("\nType=%8x m[1]=%8x uPos=%8x uSize=%7x pPos=%8x pSize=%7x", | ||
419 | b.Type, Get32(p + 4), (UInt32)b.UnpPos, (UInt32)b.UnpSize, (UInt32)b.PackPos, (UInt32)b.PackSize)); | ||
420 | |||
421 | if (b.Type == METHOD_COMMENT) | ||
422 | continue; | ||
423 | if (b.Type == METHOD_END) | ||
424 | break; | ||
425 | PackSize += b.PackSize; | ||
426 | |||
427 | if (b.UnpSize != 0) | ||
428 | { | ||
429 | if (b.Type == METHOD_ZERO_2) | ||
430 | FullFileChecksum = false; | ||
431 | Blocks.AddInReserved(b); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | if (i != numBlocks - 1) | ||
436 | return S_FALSE; | ||
437 | if (!Blocks.IsEmpty()) | ||
438 | Size = Blocks.Back().GetNextUnpPos(); | ||
439 | if (Size != (numSectors << 9)) | ||
440 | return S_FALSE; | ||
441 | |||
442 | return S_OK; | ||
443 | } | ||
444 | |||
445 | static int FindKeyPair(const CXmlItem &item, const char *key, const char *nextTag) | ||
446 | { | ||
447 | for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++) | ||
448 | { | ||
449 | const CXmlItem &si = item.SubItems[i]; | ||
450 | if (si.IsTagged("key") && si.GetSubString() == key && item.SubItems[i + 1].IsTagged(nextTag)) | ||
451 | return i + 1; | ||
452 | } | ||
453 | return -1; | ||
454 | } | ||
455 | |||
456 | static const AString *GetStringFromKeyPair(const CXmlItem &item, const char *key, const char *nextTag) | ||
457 | { | ||
458 | int index = FindKeyPair(item, key, nextTag); | ||
459 | if (index >= 0) | ||
460 | return item.SubItems[index].GetSubStringPtr(); | ||
461 | return NULL; | ||
462 | } | ||
463 | |||
464 | static const unsigned HEADER_SIZE = 0x200; | ||
465 | |||
466 | static const Byte k_Signature[] = { 'k','o','l','y', 0, 0, 0, 4, 0, 0, 2, 0 }; | ||
467 | |||
468 | static inline bool IsKoly(const Byte *p) | ||
469 | { | ||
470 | return memcmp(p, k_Signature, ARRAY_SIZE(k_Signature)) == 0; | ||
471 | /* | ||
472 | if (Get32(p) != 0x6B6F6C79) // "koly" signature | ||
473 | return false; | ||
474 | if (Get32(p + 4) != 4) // version | ||
475 | return false; | ||
476 | if (Get32(p + 8) != HEADER_SIZE) | ||
477 | return false; | ||
478 | return true; | ||
479 | */ | ||
480 | } | ||
481 | |||
482 | |||
483 | HRESULT CHandler::ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf) | ||
484 | { | ||
485 | size_t size = (size_t)pair.Len; | ||
486 | if (size != pair.Len) | ||
487 | return E_OUTOFMEMORY; | ||
488 | buf.Alloc(size); | ||
489 | RINOK(stream->Seek(_startPos + pair.Offset, STREAM_SEEK_SET, NULL)); | ||
490 | return ReadStream_FALSE(stream, buf, size); | ||
491 | } | ||
492 | |||
493 | |||
494 | bool CHandler::ParseBlob(const CByteBuffer &data) | ||
495 | { | ||
496 | if (data.Size() < 12) | ||
497 | return false; | ||
498 | const Byte *p = (const Byte *)data; | ||
499 | if (Get32(p) != 0xFADE0CC0) | ||
500 | return true; | ||
501 | const UInt32 size = Get32(p + 4); | ||
502 | if (size != data.Size()) | ||
503 | return false; | ||
504 | const UInt32 num = Get32(p + 8); | ||
505 | if (num > (size - 12) / 8) | ||
506 | return false; | ||
507 | |||
508 | for (UInt32 i = 0; i < num; i++) | ||
509 | { | ||
510 | // UInt32 type = Get32(p + i * 8 + 12); | ||
511 | UInt32 offset = Get32(p + i * 8 + 12 + 4); | ||
512 | if (size - offset < 8) | ||
513 | return false; | ||
514 | const Byte *p2 = (const Byte *)data + offset; | ||
515 | const UInt32 magic = Get32(p2); | ||
516 | const UInt32 len = Get32(p2 + 4); | ||
517 | if (size - offset < len || len < 8) | ||
518 | return false; | ||
519 | |||
520 | #ifdef DMG_SHOW_RAW | ||
521 | CExtraFile &extra = _extras.AddNew(); | ||
522 | extra.Name = "_blob_"; | ||
523 | extra.Data.CopyFrom(p2, len); | ||
524 | #endif | ||
525 | |||
526 | if (magic == 0xFADE0C02) | ||
527 | { | ||
528 | #ifdef DMG_SHOW_RAW | ||
529 | extra.Name += "codedir"; | ||
530 | #endif | ||
531 | |||
532 | if (len < 11 * 4) | ||
533 | return false; | ||
534 | UInt32 idOffset = Get32(p2 + 0x14); | ||
535 | if (idOffset >= len) | ||
536 | return false; | ||
537 | UInt32 len2 = len - idOffset; | ||
538 | if (len2 < (1 << 10)) | ||
539 | _name.SetFrom_CalcLen((const char *)(p2 + idOffset), len2); | ||
540 | } | ||
541 | #ifdef DMG_SHOW_RAW | ||
542 | else if (magic == 0xFADE0C01) | ||
543 | extra.Name += "requirements"; | ||
544 | else if (magic == 0xFADE0B01) | ||
545 | extra.Name += "signed"; | ||
546 | else | ||
547 | { | ||
548 | char temp[16]; | ||
549 | ConvertUInt32ToHex8Digits(magic, temp); | ||
550 | extra.Name += temp; | ||
551 | } | ||
552 | #endif | ||
553 | } | ||
554 | |||
555 | return true; | ||
556 | } | ||
557 | |||
558 | |||
559 | HRESULT CHandler::Open2(IInStream *stream) | ||
560 | { | ||
561 | /* | ||
562 | - usual dmg contains Koly Header at the end: | ||
563 | - rare case dmg contains Koly Header at the start. | ||
564 | */ | ||
565 | |||
566 | _dataStartOffset = 0; | ||
567 | RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPos)); | ||
568 | |||
569 | UInt64 fileSize = 0; | ||
570 | RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); | ||
571 | RINOK(stream->Seek(_startPos, STREAM_SEEK_SET, NULL)); | ||
572 | |||
573 | Byte buf[HEADER_SIZE]; | ||
574 | RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE)); | ||
575 | |||
576 | UInt64 headerPos; | ||
577 | bool startKolyMode = false; | ||
578 | |||
579 | if (IsKoly(buf)) | ||
580 | { | ||
581 | // it can be normal koly-at-the-end or koly-at-the-start | ||
582 | headerPos = _startPos; | ||
583 | if (_startPos <= (1 << 8)) | ||
584 | { | ||
585 | // we want to support startKolyMode, even if there is | ||
586 | // some data before dmg file, like 128 bytes MacBin header | ||
587 | _dataStartOffset = HEADER_SIZE; | ||
588 | startKolyMode = true; | ||
589 | } | ||
590 | } | ||
591 | else | ||
592 | { | ||
593 | // we check only koly-at-the-end | ||
594 | headerPos = fileSize; | ||
595 | if (headerPos < HEADER_SIZE) | ||
596 | return S_FALSE; | ||
597 | headerPos -= HEADER_SIZE; | ||
598 | RINOK(stream->Seek(headerPos, STREAM_SEEK_SET, NULL)); | ||
599 | RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE)); | ||
600 | if (!IsKoly(buf)) | ||
601 | return S_FALSE; | ||
602 | } | ||
603 | |||
604 | // UInt32 flags = Get32(buf + 12); | ||
605 | // UInt64 runningDataForkOffset = Get64(buf + 0x10); | ||
606 | |||
607 | CForkPair dataForkPair, rsrcPair, xmlPair, blobPair; | ||
608 | |||
609 | dataForkPair.Parse(buf + 0x18); | ||
610 | rsrcPair.Parse(buf + 0x28); | ||
611 | xmlPair.Parse(buf + 0xD8); | ||
612 | blobPair.Parse(buf + 0x128); | ||
613 | |||
614 | // UInt32 segmentNumber = Get32(buf + 0x38); | ||
615 | // UInt32 segmentCount = Get32(buf + 0x3C); | ||
616 | // Byte segmentGUID[16]; | ||
617 | // CChecksum dataForkChecksum; | ||
618 | // dataForkChecksum.Parse(buf + 0x50); | ||
619 | |||
620 | UInt64 top = 0; | ||
621 | UInt64 limit = startKolyMode ? fileSize : headerPos; | ||
622 | |||
623 | if (!dataForkPair.UpdateTop(limit, top)) return S_FALSE; | ||
624 | if (!xmlPair.UpdateTop(limit, top)) return S_FALSE; | ||
625 | if (!rsrcPair.UpdateTop(limit, top)) return S_FALSE; | ||
626 | |||
627 | /* Some old dmg files contain garbage data in blobPair field. | ||
628 | So we need to ignore such garbage case; | ||
629 | And we still need to detect offset of start of archive for "parser" mode. */ | ||
630 | |||
631 | bool useBlob = blobPair.UpdateTop(limit, top); | ||
632 | |||
633 | |||
634 | if (startKolyMode) | ||
635 | _phySize = top; | ||
636 | else | ||
637 | { | ||
638 | _phySize = headerPos + HEADER_SIZE; | ||
639 | _startPos = 0; | ||
640 | if (top != headerPos) | ||
641 | { | ||
642 | /* | ||
643 | if expected absolute offset is not equal to real header offset, | ||
644 | 2 cases are possible: | ||
645 | - additional (unknown) headers | ||
646 | - archive with offset. | ||
647 | So we try to read XML with absolute offset to select from these two ways. | ||
648 | */ | ||
649 | CForkPair xmlPair2 = xmlPair; | ||
650 | const char *sz = "<?xml version"; | ||
651 | const unsigned len = (unsigned)strlen(sz); | ||
652 | if (xmlPair2.Len > len) | ||
653 | xmlPair2.Len = len; | ||
654 | CByteBuffer buf2; | ||
655 | if (xmlPair2.Len < len | ||
656 | || ReadData(stream, xmlPair2, buf2) != S_OK | ||
657 | || memcmp(buf2, sz, len) != 0) | ||
658 | { | ||
659 | // if absolute offset is not OK, probably it's archive with offset | ||
660 | _startPos = headerPos - top; | ||
661 | _phySize = top + HEADER_SIZE; | ||
662 | } | ||
663 | } | ||
664 | } | ||
665 | |||
666 | // Byte reserved[0x78] | ||
667 | |||
668 | if (useBlob && blobPair.Len != 0) | ||
669 | { | ||
670 | #ifdef DMG_SHOW_RAW | ||
671 | CExtraFile &extra = _extras.AddNew(); | ||
672 | extra.Name = "_blob.bin"; | ||
673 | CByteBuffer &blobBuf = extra.Data; | ||
674 | #else | ||
675 | CByteBuffer blobBuf; | ||
676 | #endif | ||
677 | RINOK(ReadData(stream, blobPair, blobBuf)); | ||
678 | if (!ParseBlob(blobBuf)) | ||
679 | _headersError = true; | ||
680 | } | ||
681 | |||
682 | |||
683 | CChecksum masterChecksum; | ||
684 | masterChecksum.Parse(buf + 0x160); | ||
685 | |||
686 | // UInt32 imageVariant = Get32(buf + 0x1E8); | ||
687 | // UInt64 numSectors = Get64(buf + 0x1EC); | ||
688 | // Byte reserved[0x12] | ||
689 | |||
690 | const UInt32 RSRC_HEAD_SIZE = 0x100; | ||
691 | |||
692 | // We don't know the size of the field "offset" in rsrc. | ||
693 | // We suppose that it uses 24 bits. So we use Rsrc, only if the rsrcLen < (1 << 24). | ||
694 | bool useRsrc = (rsrcPair.Len > RSRC_HEAD_SIZE && rsrcPair.Len < ((UInt32)1 << 24)); | ||
695 | // useRsrc = false; | ||
696 | |||
697 | if (useRsrc) | ||
698 | { | ||
699 | #ifdef DMG_SHOW_RAW | ||
700 | CExtraFile &extra = _extras.AddNew(); | ||
701 | extra.Name = "rsrc.bin"; | ||
702 | CByteBuffer &rsrcBuf = extra.Data; | ||
703 | #else | ||
704 | CByteBuffer rsrcBuf; | ||
705 | #endif | ||
706 | |||
707 | RINOK(ReadData(stream, rsrcPair, rsrcBuf)); | ||
708 | |||
709 | const Byte *p = rsrcBuf; | ||
710 | UInt32 headSize = Get32(p + 0); | ||
711 | UInt32 footerOffset = Get32(p + 4); | ||
712 | UInt32 mainDataSize = Get32(p + 8); | ||
713 | UInt32 footerSize = Get32(p + 12); | ||
714 | if (headSize != RSRC_HEAD_SIZE | ||
715 | || footerOffset >= rsrcPair.Len | ||
716 | || mainDataSize >= rsrcPair.Len | ||
717 | || footerOffset < mainDataSize | ||
718 | || footerOffset != headSize + mainDataSize) | ||
719 | return S_FALSE; | ||
720 | |||
721 | const UInt32 footerEnd = footerOffset + footerSize; | ||
722 | if (footerEnd != rsrcPair.Len) | ||
723 | { | ||
724 | // there is rare case dmg example, where there are 4 additional bytes | ||
725 | UInt64 rem = rsrcPair.Len - footerOffset; | ||
726 | if (rem < footerSize | ||
727 | || rem - footerSize != 4 | ||
728 | || Get32(p + footerEnd) != 0) | ||
729 | return S_FALSE; | ||
730 | } | ||
731 | |||
732 | if (footerSize < 16) | ||
733 | return S_FALSE; | ||
734 | if (memcmp(p, p + footerOffset, 16) != 0) | ||
735 | return S_FALSE; | ||
736 | |||
737 | p += footerOffset; | ||
738 | |||
739 | if ((UInt32)Get16(p + 0x18) != 0x1C) | ||
740 | return S_FALSE; | ||
741 | const UInt32 namesOffset = Get16(p + 0x1A); | ||
742 | if (namesOffset > footerSize) | ||
743 | return S_FALSE; | ||
744 | |||
745 | UInt32 numItems = (UInt32)Get16(p + 0x1C) + 1; | ||
746 | if (numItems * 8 + 0x1E > namesOffset) | ||
747 | return S_FALSE; | ||
748 | |||
749 | for (UInt32 i = 0; i < numItems; i++) | ||
750 | { | ||
751 | const Byte *p2 = p + 0x1E + i * 8; | ||
752 | |||
753 | const UInt32 typeId = Get32(p2); | ||
754 | |||
755 | #ifndef DMG_SHOW_RAW | ||
756 | if (typeId != 0x626C6B78) // blkx | ||
757 | continue; | ||
758 | #endif | ||
759 | |||
760 | const UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1; | ||
761 | const UInt32 offs = Get16(p2 + 6); | ||
762 | if (0x1C + offs + 12 * numFiles > namesOffset) | ||
763 | return S_FALSE; | ||
764 | |||
765 | for (UInt32 k = 0; k < numFiles; k++) | ||
766 | { | ||
767 | const Byte *p3 = p + 0x1C + offs + k * 12; | ||
768 | // UInt32 id = Get16(p3); | ||
769 | const UInt32 namePos = Get16(p3 + 2); | ||
770 | // Byte attributes = p3[4]; // = 0x50 for blkx | ||
771 | // we don't know how many bits we can use. So we use 24 bits only | ||
772 | UInt32 blockOffset = Get32(p3 + 4); | ||
773 | blockOffset &= (((UInt32)1 << 24) - 1); | ||
774 | // UInt32 unknown2 = Get32(p3 + 8); // ??? | ||
775 | if (blockOffset + 4 >= mainDataSize) | ||
776 | return S_FALSE; | ||
777 | const Byte *pBlock = rsrcBuf + headSize + blockOffset; | ||
778 | const UInt32 blockSize = Get32(pBlock); | ||
779 | if (mainDataSize - (blockOffset + 4) < blockSize) | ||
780 | return S_FALSE; | ||
781 | |||
782 | AString name; | ||
783 | |||
784 | if (namePos != 0xFFFF) | ||
785 | { | ||
786 | UInt32 namesBlockSize = footerSize - namesOffset; | ||
787 | if (namePos >= namesBlockSize) | ||
788 | return S_FALSE; | ||
789 | const Byte *namePtr = p + namesOffset + namePos; | ||
790 | UInt32 nameLen = *namePtr; | ||
791 | if (namesBlockSize - namePos <= nameLen) | ||
792 | return S_FALSE; | ||
793 | for (UInt32 r = 1; r <= nameLen; r++) | ||
794 | { | ||
795 | Byte c = namePtr[r]; | ||
796 | if (c < 0x20 || c >= 0x80) | ||
797 | break; | ||
798 | name += (char)c; | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if (typeId == 0x626C6B78) // blkx | ||
803 | { | ||
804 | CFile &file = _files.AddNew(); | ||
805 | file.Name = name; | ||
806 | RINOK(file.Parse(pBlock + 4, blockSize)); | ||
807 | } | ||
808 | |||
809 | #ifdef DMG_SHOW_RAW | ||
810 | { | ||
811 | AString name2; | ||
812 | |||
813 | name2.Add_UInt32(i); | ||
814 | name2 += '_'; | ||
815 | |||
816 | { | ||
817 | char temp[4 + 1] = { 0 }; | ||
818 | memcpy(temp, p2, 4); | ||
819 | name2 += temp; | ||
820 | } | ||
821 | name2.Trim(); | ||
822 | name2 += '_'; | ||
823 | name2.Add_UInt32(k); | ||
824 | |||
825 | if (!name.IsEmpty()) | ||
826 | { | ||
827 | name2 += '_'; | ||
828 | name2 += name; | ||
829 | } | ||
830 | |||
831 | CExtraFile &extra = _extras.AddNew(); | ||
832 | extra.Name = name2; | ||
833 | extra.Data.CopyFrom(pBlock + 4, blockSize); | ||
834 | } | ||
835 | #endif | ||
836 | } | ||
837 | } | ||
838 | } | ||
839 | else | ||
840 | { | ||
841 | if (xmlPair.Len >= kXmlSizeMax || xmlPair.Len == 0) | ||
842 | return S_FALSE; | ||
843 | size_t size = (size_t)xmlPair.Len; | ||
844 | if (size != xmlPair.Len) | ||
845 | return S_FALSE; | ||
846 | |||
847 | RINOK(stream->Seek(_startPos + xmlPair.Offset, STREAM_SEEK_SET, NULL)); | ||
848 | |||
849 | CXml xml; | ||
850 | { | ||
851 | CObjArray<char> xmlStr(size + 1); | ||
852 | RINOK(ReadStream_FALSE(stream, xmlStr, size)); | ||
853 | xmlStr[size] = 0; | ||
854 | // if (strlen(xmlStr) != size) return S_FALSE; | ||
855 | if (!xml.Parse(xmlStr)) | ||
856 | return S_FALSE; | ||
857 | |||
858 | #ifdef DMG_SHOW_RAW | ||
859 | CExtraFile &extra = _extras.AddNew(); | ||
860 | extra.Name = "a.xml"; | ||
861 | extra.Data.CopyFrom((const Byte *)(const char *)xmlStr, size); | ||
862 | #endif | ||
863 | } | ||
864 | |||
865 | if (xml.Root.Name != "plist") | ||
866 | return S_FALSE; | ||
867 | |||
868 | int dictIndex = xml.Root.FindSubTag("dict"); | ||
869 | if (dictIndex < 0) | ||
870 | return S_FALSE; | ||
871 | |||
872 | const CXmlItem &dictItem = xml.Root.SubItems[dictIndex]; | ||
873 | int rfDictIndex = FindKeyPair(dictItem, "resource-fork", "dict"); | ||
874 | if (rfDictIndex < 0) | ||
875 | return S_FALSE; | ||
876 | |||
877 | const CXmlItem &rfDictItem = dictItem.SubItems[rfDictIndex]; | ||
878 | int arrIndex = FindKeyPair(rfDictItem, "blkx", "array"); | ||
879 | if (arrIndex < 0) | ||
880 | return S_FALSE; | ||
881 | |||
882 | const CXmlItem &arrItem = rfDictItem.SubItems[arrIndex]; | ||
883 | |||
884 | FOR_VECTOR (i, arrItem.SubItems) | ||
885 | { | ||
886 | const CXmlItem &item = arrItem.SubItems[i]; | ||
887 | if (!item.IsTagged("dict")) | ||
888 | continue; | ||
889 | |||
890 | CByteBuffer rawBuf; | ||
891 | unsigned destLen = 0; | ||
892 | { | ||
893 | const AString *dataString = GetStringFromKeyPair(item, "Data", "data"); | ||
894 | if (!dataString) | ||
895 | return S_FALSE; | ||
896 | destLen = dataString->Len() / 4 * 3 + 4; | ||
897 | rawBuf.Alloc(destLen); | ||
898 | { | ||
899 | const Byte *endPtr = Base64ToBin(rawBuf, *dataString); | ||
900 | if (!endPtr) | ||
901 | return S_FALSE; | ||
902 | destLen = (unsigned)(endPtr - (const Byte *)rawBuf); | ||
903 | } | ||
904 | |||
905 | #ifdef DMG_SHOW_RAW | ||
906 | CExtraFile &extra = _extras.AddNew(); | ||
907 | extra.Name.Add_UInt32(_files.Size()); | ||
908 | extra.Data.CopyFrom(rawBuf, destLen); | ||
909 | #endif | ||
910 | } | ||
911 | CFile &file = _files.AddNew(); | ||
912 | { | ||
913 | const AString *name = GetStringFromKeyPair(item, "Name", "string"); | ||
914 | if (!name || name->IsEmpty()) | ||
915 | name = GetStringFromKeyPair(item, "CFName", "string"); | ||
916 | if (name) | ||
917 | file.Name = *name; | ||
918 | } | ||
919 | RINOK(file.Parse(rawBuf, destLen)); | ||
920 | } | ||
921 | } | ||
922 | |||
923 | if (masterChecksum.IsCrc32()) | ||
924 | { | ||
925 | UInt32 crc = CRC_INIT_VAL; | ||
926 | unsigned i; | ||
927 | for (i = 0; i < _files.Size(); i++) | ||
928 | { | ||
929 | const CChecksum &cs = _files[i].Checksum; | ||
930 | if ((cs.NumBits & 0x7) != 0) | ||
931 | break; | ||
932 | UInt32 len = cs.NumBits >> 3; | ||
933 | if (len > kChecksumSize_Max) | ||
934 | break; | ||
935 | crc = CrcUpdate(crc, cs.Data, (size_t)len); | ||
936 | } | ||
937 | if (i == _files.Size()) | ||
938 | _masterCrcError = (CRC_GET_DIGEST(crc) != masterChecksum.GetCrc32()); | ||
939 | } | ||
940 | |||
941 | return S_OK; | ||
942 | } | ||
943 | |||
944 | STDMETHODIMP CHandler::Open(IInStream *stream, | ||
945 | const UInt64 * /* maxCheckStartPosition */, | ||
946 | IArchiveOpenCallback * /* openArchiveCallback */) | ||
947 | { | ||
948 | COM_TRY_BEGIN | ||
949 | { | ||
950 | Close(); | ||
951 | if (Open2(stream) != S_OK) | ||
952 | return S_FALSE; | ||
953 | _inStream = stream; | ||
954 | } | ||
955 | return S_OK; | ||
956 | COM_TRY_END | ||
957 | } | ||
958 | |||
959 | STDMETHODIMP CHandler::Close() | ||
960 | { | ||
961 | _phySize = 0; | ||
962 | _inStream.Release(); | ||
963 | _files.Clear(); | ||
964 | _masterCrcError = false; | ||
965 | _headersError = false; | ||
966 | _name.Empty(); | ||
967 | #ifdef DMG_SHOW_RAW | ||
968 | _extras.Clear(); | ||
969 | #endif | ||
970 | return S_OK; | ||
971 | } | ||
972 | |||
973 | STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
974 | { | ||
975 | *numItems = _files.Size() | ||
976 | #ifdef DMG_SHOW_RAW | ||
977 | + _extras.Size() | ||
978 | #endif | ||
979 | ; | ||
980 | return S_OK; | ||
981 | } | ||
982 | |||
983 | #ifdef DMG_SHOW_RAW | ||
984 | #define RAW_PREFIX "raw" STRING_PATH_SEPARATOR | ||
985 | #endif | ||
986 | |||
987 | STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) | ||
988 | { | ||
989 | COM_TRY_BEGIN | ||
990 | NWindows::NCOM::CPropVariant prop; | ||
991 | |||
992 | #ifdef DMG_SHOW_RAW | ||
993 | if (index >= _files.Size()) | ||
994 | { | ||
995 | const CExtraFile &extra = _extras[index - _files.Size()]; | ||
996 | switch (propID) | ||
997 | { | ||
998 | case kpidPath: | ||
999 | prop = (AString)RAW_PREFIX + extra.Name; | ||
1000 | break; | ||
1001 | case kpidSize: | ||
1002 | case kpidPackSize: | ||
1003 | prop = (UInt64)extra.Data.Size(); | ||
1004 | break; | ||
1005 | } | ||
1006 | } | ||
1007 | else | ||
1008 | #endif | ||
1009 | { | ||
1010 | const CFile &item = _files[index]; | ||
1011 | switch (propID) | ||
1012 | { | ||
1013 | case kpidSize: prop = item.Size; break; | ||
1014 | case kpidPackSize: prop = item.PackSize; break; | ||
1015 | case kpidCRC: | ||
1016 | { | ||
1017 | if (item.Checksum.IsCrc32() && item.FullFileChecksum) | ||
1018 | prop = item.Checksum.GetCrc32(); | ||
1019 | break; | ||
1020 | } | ||
1021 | |||
1022 | /* | ||
1023 | case kpidOffset: | ||
1024 | { | ||
1025 | prop = item.StartPos; | ||
1026 | break; | ||
1027 | } | ||
1028 | */ | ||
1029 | |||
1030 | case kpidMethod: | ||
1031 | { | ||
1032 | CMethods m; | ||
1033 | m.Update(item); | ||
1034 | AString s; | ||
1035 | m.GetString(s); | ||
1036 | if (!s.IsEmpty()) | ||
1037 | prop = s; | ||
1038 | break; | ||
1039 | } | ||
1040 | |||
1041 | case kpidPath: | ||
1042 | { | ||
1043 | UString name; | ||
1044 | name.Add_UInt32(index); | ||
1045 | unsigned num = 10; | ||
1046 | unsigned numDigits; | ||
1047 | for (numDigits = 1; num < _files.Size(); numDigits++) | ||
1048 | num *= 10; | ||
1049 | while (name.Len() < numDigits) | ||
1050 | name.InsertAtFront(L'0'); | ||
1051 | |||
1052 | AString subName; | ||
1053 | int pos1 = item.Name.Find('('); | ||
1054 | if (pos1 >= 0) | ||
1055 | { | ||
1056 | pos1++; | ||
1057 | int pos2 = item.Name.Find(')', pos1); | ||
1058 | if (pos2 >= 0) | ||
1059 | { | ||
1060 | subName.SetFrom(item.Name.Ptr(pos1), pos2 - pos1); | ||
1061 | pos1 = subName.Find(':'); | ||
1062 | if (pos1 >= 0) | ||
1063 | subName.DeleteFrom(pos1); | ||
1064 | } | ||
1065 | } | ||
1066 | subName.Trim(); | ||
1067 | if (!subName.IsEmpty()) | ||
1068 | { | ||
1069 | for (unsigned n = 0; n < kNumAppleNames; n++) | ||
1070 | { | ||
1071 | const CAppleName &appleName = k_Names[n]; | ||
1072 | if (appleName.Ext) | ||
1073 | { | ||
1074 | if (subName == appleName.AppleName) | ||
1075 | { | ||
1076 | subName = appleName.Ext; | ||
1077 | break; | ||
1078 | } | ||
1079 | } | ||
1080 | } | ||
1081 | UString name2; | ||
1082 | ConvertUTF8ToUnicode(subName, name2); | ||
1083 | name += '.'; | ||
1084 | name += name2; | ||
1085 | } | ||
1086 | else | ||
1087 | { | ||
1088 | UString name2; | ||
1089 | ConvertUTF8ToUnicode(item.Name, name2); | ||
1090 | if (!name2.IsEmpty()) | ||
1091 | name += "_"; | ||
1092 | name += name2; | ||
1093 | } | ||
1094 | prop = name; | ||
1095 | break; | ||
1096 | } | ||
1097 | |||
1098 | case kpidComment: | ||
1099 | { | ||
1100 | UString name; | ||
1101 | ConvertUTF8ToUnicode(item.Name, name); | ||
1102 | prop = name; | ||
1103 | break; | ||
1104 | } | ||
1105 | } | ||
1106 | } | ||
1107 | prop.Detach(value); | ||
1108 | return S_OK; | ||
1109 | COM_TRY_END | ||
1110 | } | ||
1111 | |||
1112 | class CAdcDecoder: | ||
1113 | public ICompressCoder, | ||
1114 | public CMyUnknownImp | ||
1115 | { | ||
1116 | CLzOutWindow m_OutWindowStream; | ||
1117 | CInBuffer m_InStream; | ||
1118 | |||
1119 | /* | ||
1120 | void ReleaseStreams() | ||
1121 | { | ||
1122 | m_OutWindowStream.ReleaseStream(); | ||
1123 | m_InStream.ReleaseStream(); | ||
1124 | } | ||
1125 | */ | ||
1126 | |||
1127 | class CCoderReleaser | ||
1128 | { | ||
1129 | CAdcDecoder *m_Coder; | ||
1130 | public: | ||
1131 | bool NeedFlush; | ||
1132 | CCoderReleaser(CAdcDecoder *coder): m_Coder(coder), NeedFlush(true) {} | ||
1133 | ~CCoderReleaser() | ||
1134 | { | ||
1135 | if (NeedFlush) | ||
1136 | m_Coder->m_OutWindowStream.Flush(); | ||
1137 | // m_Coder->ReleaseStreams(); | ||
1138 | } | ||
1139 | }; | ||
1140 | friend class CCoderReleaser; | ||
1141 | |||
1142 | public: | ||
1143 | MY_UNKNOWN_IMP | ||
1144 | |||
1145 | STDMETHOD(CodeReal)(ISequentialInStream *inStream, | ||
1146 | ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, | ||
1147 | ICompressProgressInfo *progress); | ||
1148 | |||
1149 | STDMETHOD(Code)(ISequentialInStream *inStream, | ||
1150 | ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, | ||
1151 | ICompressProgressInfo *progress); | ||
1152 | }; | ||
1153 | |||
1154 | STDMETHODIMP CAdcDecoder::CodeReal(ISequentialInStream *inStream, | ||
1155 | ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, | ||
1156 | ICompressProgressInfo *progress) | ||
1157 | { | ||
1158 | if (!m_OutWindowStream.Create(1 << 18)) | ||
1159 | return E_OUTOFMEMORY; | ||
1160 | if (!m_InStream.Create(1 << 18)) | ||
1161 | return E_OUTOFMEMORY; | ||
1162 | |||
1163 | m_OutWindowStream.SetStream(outStream); | ||
1164 | m_OutWindowStream.Init(false); | ||
1165 | m_InStream.SetStream(inStream); | ||
1166 | m_InStream.Init(); | ||
1167 | |||
1168 | CCoderReleaser coderReleaser(this); | ||
1169 | |||
1170 | const UInt32 kStep = (1 << 20); | ||
1171 | UInt64 nextLimit = kStep; | ||
1172 | |||
1173 | UInt64 pos = 0; | ||
1174 | while (pos < *outSize) | ||
1175 | { | ||
1176 | if (pos > nextLimit && progress) | ||
1177 | { | ||
1178 | UInt64 packSize = m_InStream.GetProcessedSize(); | ||
1179 | RINOK(progress->SetRatioInfo(&packSize, &pos)); | ||
1180 | nextLimit += kStep; | ||
1181 | } | ||
1182 | Byte b; | ||
1183 | if (!m_InStream.ReadByte(b)) | ||
1184 | return S_FALSE; | ||
1185 | UInt64 rem = *outSize - pos; | ||
1186 | if (b & 0x80) | ||
1187 | { | ||
1188 | unsigned num = (b & 0x7F) + 1; | ||
1189 | if (num > rem) | ||
1190 | return S_FALSE; | ||
1191 | for (unsigned i = 0; i < num; i++) | ||
1192 | { | ||
1193 | if (!m_InStream.ReadByte(b)) | ||
1194 | return S_FALSE; | ||
1195 | m_OutWindowStream.PutByte(b); | ||
1196 | } | ||
1197 | pos += num; | ||
1198 | continue; | ||
1199 | } | ||
1200 | Byte b1; | ||
1201 | if (!m_InStream.ReadByte(b1)) | ||
1202 | return S_FALSE; | ||
1203 | |||
1204 | UInt32 len, distance; | ||
1205 | |||
1206 | if (b & 0x40) | ||
1207 | { | ||
1208 | len = ((UInt32)b & 0x3F) + 4; | ||
1209 | Byte b2; | ||
1210 | if (!m_InStream.ReadByte(b2)) | ||
1211 | return S_FALSE; | ||
1212 | distance = ((UInt32)b1 << 8) + b2; | ||
1213 | } | ||
1214 | else | ||
1215 | { | ||
1216 | b &= 0x3F; | ||
1217 | len = ((UInt32)b >> 2) + 3; | ||
1218 | distance = (((UInt32)b & 3) << 8) + b1; | ||
1219 | } | ||
1220 | |||
1221 | if (distance >= pos || len > rem) | ||
1222 | return S_FALSE; | ||
1223 | m_OutWindowStream.CopyBlock(distance, len); | ||
1224 | pos += len; | ||
1225 | } | ||
1226 | if (*inSize != m_InStream.GetProcessedSize()) | ||
1227 | return S_FALSE; | ||
1228 | coderReleaser.NeedFlush = false; | ||
1229 | return m_OutWindowStream.Flush(); | ||
1230 | } | ||
1231 | |||
1232 | STDMETHODIMP CAdcDecoder::Code(ISequentialInStream *inStream, | ||
1233 | ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, | ||
1234 | ICompressProgressInfo *progress) | ||
1235 | { | ||
1236 | try { return CodeReal(inStream, outStream, inSize, outSize, progress);} | ||
1237 | catch(const CInBufferException &e) { return e.ErrorCode; } | ||
1238 | catch(const CLzOutWindowException &e) { return e.ErrorCode; } | ||
1239 | catch(...) { return S_FALSE; } | ||
1240 | } | ||
1241 | |||
1242 | |||
1243 | |||
1244 | |||
1245 | |||
1246 | |||
1247 | |||
1248 | STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, | ||
1249 | Int32 testMode, IArchiveExtractCallback *extractCallback) | ||
1250 | { | ||
1251 | COM_TRY_BEGIN | ||
1252 | bool allFilesMode = (numItems == (UInt32)(Int32)-1); | ||
1253 | if (allFilesMode) | ||
1254 | numItems = _files.Size(); | ||
1255 | if (numItems == 0) | ||
1256 | return S_OK; | ||
1257 | UInt64 totalSize = 0; | ||
1258 | UInt32 i; | ||
1259 | |||
1260 | for (i = 0; i < numItems; i++) | ||
1261 | { | ||
1262 | UInt32 index = (allFilesMode ? i : indices[i]); | ||
1263 | #ifdef DMG_SHOW_RAW | ||
1264 | if (index >= _files.Size()) | ||
1265 | totalSize += _extras[index - _files.Size()].Data.Size(); | ||
1266 | else | ||
1267 | #endif | ||
1268 | totalSize += _files[index].Size; | ||
1269 | } | ||
1270 | extractCallback->SetTotal(totalSize); | ||
1271 | |||
1272 | UInt64 currentPackTotal = 0; | ||
1273 | UInt64 currentUnpTotal = 0; | ||
1274 | UInt64 currentPackSize = 0; | ||
1275 | UInt64 currentUnpSize = 0; | ||
1276 | |||
1277 | const UInt32 kZeroBufSize = (1 << 14); | ||
1278 | CByteBuffer zeroBuf(kZeroBufSize); | ||
1279 | memset(zeroBuf, 0, kZeroBufSize); | ||
1280 | |||
1281 | NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); | ||
1282 | CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; | ||
1283 | |||
1284 | NCompress::NBZip2::CDecoder *bzip2CoderSpec = new NCompress::NBZip2::CDecoder(); | ||
1285 | CMyComPtr<ICompressCoder> bzip2Coder = bzip2CoderSpec; | ||
1286 | |||
1287 | NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder(); | ||
1288 | CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec; | ||
1289 | |||
1290 | CAdcDecoder *adcCoderSpec = new CAdcDecoder(); | ||
1291 | CMyComPtr<ICompressCoder> adcCoder = adcCoderSpec; | ||
1292 | |||
1293 | NCompress::NLzfse::CDecoder *lzfseCoderSpec = new NCompress::NLzfse::CDecoder(); | ||
1294 | CMyComPtr<ICompressCoder> lzfseCoder = lzfseCoderSpec; | ||
1295 | |||
1296 | CLocalProgress *lps = new CLocalProgress; | ||
1297 | CMyComPtr<ICompressProgressInfo> progress = lps; | ||
1298 | lps->Init(extractCallback, false); | ||
1299 | |||
1300 | CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; | ||
1301 | CMyComPtr<ISequentialInStream> inStream(streamSpec); | ||
1302 | streamSpec->SetStream(_inStream); | ||
1303 | |||
1304 | for (i = 0; i < numItems; i++, currentPackTotal += currentPackSize, currentUnpTotal += currentUnpSize) | ||
1305 | { | ||
1306 | lps->InSize = currentPackTotal; | ||
1307 | lps->OutSize = currentUnpTotal; | ||
1308 | currentPackSize = 0; | ||
1309 | currentUnpSize = 0; | ||
1310 | RINOK(lps->SetCur()); | ||
1311 | CMyComPtr<ISequentialOutStream> realOutStream; | ||
1312 | Int32 askMode = testMode ? | ||
1313 | NExtract::NAskMode::kTest : | ||
1314 | NExtract::NAskMode::kExtract; | ||
1315 | UInt32 index = allFilesMode ? i : indices[i]; | ||
1316 | RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); | ||
1317 | |||
1318 | if (!testMode && !realOutStream) | ||
1319 | continue; | ||
1320 | RINOK(extractCallback->PrepareOperation(askMode)); | ||
1321 | |||
1322 | |||
1323 | COutStreamWithCRC *outCrcStreamSpec = new COutStreamWithCRC; | ||
1324 | CMyComPtr<ISequentialOutStream> outCrcStream = outCrcStreamSpec; | ||
1325 | outCrcStreamSpec->SetStream(realOutStream); | ||
1326 | bool needCrc = false; | ||
1327 | outCrcStreamSpec->Init(needCrc); | ||
1328 | |||
1329 | CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; | ||
1330 | CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); | ||
1331 | outStreamSpec->SetStream(outCrcStream); | ||
1332 | |||
1333 | realOutStream.Release(); | ||
1334 | |||
1335 | Int32 opRes = NExtract::NOperationResult::kOK; | ||
1336 | #ifdef DMG_SHOW_RAW | ||
1337 | if (index >= _files.Size()) | ||
1338 | { | ||
1339 | const CByteBuffer &buf = _extras[index - _files.Size()].Data; | ||
1340 | outStreamSpec->Init(buf.Size()); | ||
1341 | RINOK(WriteStream(outStream, buf, buf.Size())); | ||
1342 | currentPackSize = currentUnpSize = buf.Size(); | ||
1343 | } | ||
1344 | else | ||
1345 | #endif | ||
1346 | { | ||
1347 | const CFile &item = _files[index]; | ||
1348 | currentPackSize = item.PackSize; | ||
1349 | currentUnpSize = item.Size; | ||
1350 | |||
1351 | needCrc = item.Checksum.IsCrc32(); | ||
1352 | |||
1353 | UInt64 unpPos = 0; | ||
1354 | UInt64 packPos = 0; | ||
1355 | { | ||
1356 | FOR_VECTOR (j, item.Blocks) | ||
1357 | { | ||
1358 | lps->InSize = currentPackTotal + packPos; | ||
1359 | lps->OutSize = currentUnpTotal + unpPos; | ||
1360 | RINOK(lps->SetCur()); | ||
1361 | |||
1362 | const CBlock &block = item.Blocks[j]; | ||
1363 | if (!block.ThereAreDataInBlock()) | ||
1364 | continue; | ||
1365 | |||
1366 | packPos += block.PackSize; | ||
1367 | if (block.UnpPos != unpPos) | ||
1368 | { | ||
1369 | opRes = NExtract::NOperationResult::kDataError; | ||
1370 | break; | ||
1371 | } | ||
1372 | |||
1373 | RINOK(_inStream->Seek(_startPos + _dataStartOffset + item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL)); | ||
1374 | streamSpec->Init(block.PackSize); | ||
1375 | bool realMethod = true; | ||
1376 | outStreamSpec->Init(block.UnpSize); | ||
1377 | HRESULT res = S_OK; | ||
1378 | |||
1379 | outCrcStreamSpec->EnableCalc(needCrc); | ||
1380 | |||
1381 | switch (block.Type) | ||
1382 | { | ||
1383 | case METHOD_ZERO_0: | ||
1384 | case METHOD_ZERO_2: | ||
1385 | realMethod = false; | ||
1386 | if (block.PackSize != 0) | ||
1387 | opRes = NExtract::NOperationResult::kUnsupportedMethod; | ||
1388 | outCrcStreamSpec->EnableCalc(block.Type == METHOD_ZERO_0); | ||
1389 | break; | ||
1390 | |||
1391 | case METHOD_COPY: | ||
1392 | if (block.UnpSize != block.PackSize) | ||
1393 | { | ||
1394 | opRes = NExtract::NOperationResult::kUnsupportedMethod; | ||
1395 | break; | ||
1396 | } | ||
1397 | res = copyCoder->Code(inStream, outStream, NULL, NULL, progress); | ||
1398 | break; | ||
1399 | |||
1400 | case METHOD_ADC: | ||
1401 | { | ||
1402 | res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, progress); | ||
1403 | break; | ||
1404 | } | ||
1405 | |||
1406 | case METHOD_ZLIB: | ||
1407 | { | ||
1408 | res = zlibCoder->Code(inStream, outStream, NULL, NULL, progress); | ||
1409 | if (res == S_OK) | ||
1410 | if (zlibCoderSpec->GetInputProcessedSize() != block.PackSize) | ||
1411 | opRes = NExtract::NOperationResult::kDataError; | ||
1412 | break; | ||
1413 | } | ||
1414 | |||
1415 | case METHOD_BZIP2: | ||
1416 | { | ||
1417 | res = bzip2Coder->Code(inStream, outStream, NULL, NULL, progress); | ||
1418 | if (res == S_OK) | ||
1419 | if (bzip2CoderSpec->GetInputProcessedSize() != block.PackSize) | ||
1420 | opRes = NExtract::NOperationResult::kDataError; | ||
1421 | break; | ||
1422 | } | ||
1423 | |||
1424 | case METHOD_LZFSE: | ||
1425 | { | ||
1426 | res = lzfseCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, progress); | ||
1427 | break; | ||
1428 | } | ||
1429 | |||
1430 | default: | ||
1431 | opRes = NExtract::NOperationResult::kUnsupportedMethod; | ||
1432 | break; | ||
1433 | } | ||
1434 | |||
1435 | if (res != S_OK) | ||
1436 | { | ||
1437 | if (res != S_FALSE) | ||
1438 | return res; | ||
1439 | if (opRes == NExtract::NOperationResult::kOK) | ||
1440 | opRes = NExtract::NOperationResult::kDataError; | ||
1441 | } | ||
1442 | |||
1443 | unpPos += block.UnpSize; | ||
1444 | |||
1445 | if (!outStreamSpec->IsFinishedOK()) | ||
1446 | { | ||
1447 | if (realMethod && opRes == NExtract::NOperationResult::kOK) | ||
1448 | opRes = NExtract::NOperationResult::kDataError; | ||
1449 | |||
1450 | while (outStreamSpec->GetRem() != 0) | ||
1451 | { | ||
1452 | UInt64 rem = outStreamSpec->GetRem(); | ||
1453 | UInt32 size = (UInt32)MyMin(rem, (UInt64)kZeroBufSize); | ||
1454 | RINOK(WriteStream(outStream, zeroBuf, size)); | ||
1455 | } | ||
1456 | } | ||
1457 | } | ||
1458 | } | ||
1459 | |||
1460 | if (needCrc && opRes == NExtract::NOperationResult::kOK) | ||
1461 | { | ||
1462 | if (outCrcStreamSpec->GetCRC() != item.Checksum.GetCrc32()) | ||
1463 | opRes = NExtract::NOperationResult::kCRCError; | ||
1464 | } | ||
1465 | } | ||
1466 | outStream.Release(); | ||
1467 | RINOK(extractCallback->SetOperationResult(opRes)); | ||
1468 | } | ||
1469 | |||
1470 | return S_OK; | ||
1471 | COM_TRY_END | ||
1472 | } | ||
1473 | |||
1474 | struct CChunk | ||
1475 | { | ||
1476 | int BlockIndex; | ||
1477 | UInt64 AccessMark; | ||
1478 | CByteBuffer Buf; | ||
1479 | }; | ||
1480 | |||
1481 | class CInStream: | ||
1482 | public IInStream, | ||
1483 | public CMyUnknownImp | ||
1484 | { | ||
1485 | UInt64 _virtPos; | ||
1486 | int _latestChunk; | ||
1487 | int _latestBlock; | ||
1488 | UInt64 _accessMark; | ||
1489 | CObjectVector<CChunk> _chunks; | ||
1490 | |||
1491 | NCompress::NBZip2::CDecoder *bzip2CoderSpec; | ||
1492 | CMyComPtr<ICompressCoder> bzip2Coder; | ||
1493 | |||
1494 | NCompress::NZlib::CDecoder *zlibCoderSpec; | ||
1495 | CMyComPtr<ICompressCoder> zlibCoder; | ||
1496 | |||
1497 | CAdcDecoder *adcCoderSpec; | ||
1498 | CMyComPtr<ICompressCoder> adcCoder; | ||
1499 | |||
1500 | NCompress::NLzfse::CDecoder *lzfseCoderSpec; | ||
1501 | CMyComPtr<ICompressCoder> lzfseCoder; | ||
1502 | |||
1503 | CBufPtrSeqOutStream *outStreamSpec; | ||
1504 | CMyComPtr<ISequentialOutStream> outStream; | ||
1505 | |||
1506 | CLimitedSequentialInStream *limitedStreamSpec; | ||
1507 | CMyComPtr<ISequentialInStream> inStream; | ||
1508 | |||
1509 | public: | ||
1510 | CMyComPtr<IInStream> Stream; | ||
1511 | UInt64 Size; | ||
1512 | const CFile *File; | ||
1513 | UInt64 _startPos; | ||
1514 | |||
1515 | HRESULT InitAndSeek(UInt64 startPos) | ||
1516 | { | ||
1517 | _startPos = startPos; | ||
1518 | _virtPos = 0; | ||
1519 | _latestChunk = -1; | ||
1520 | _latestBlock = -1; | ||
1521 | _accessMark = 0; | ||
1522 | |||
1523 | limitedStreamSpec = new CLimitedSequentialInStream; | ||
1524 | inStream = limitedStreamSpec; | ||
1525 | limitedStreamSpec->SetStream(Stream); | ||
1526 | |||
1527 | outStreamSpec = new CBufPtrSeqOutStream; | ||
1528 | outStream = outStreamSpec; | ||
1529 | return S_OK; | ||
1530 | } | ||
1531 | |||
1532 | MY_UNKNOWN_IMP1(IInStream) | ||
1533 | |||
1534 | STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); | ||
1535 | STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); | ||
1536 | }; | ||
1537 | |||
1538 | |||
1539 | static unsigned FindBlock(const CRecordVector<CBlock> &blocks, UInt64 pos) | ||
1540 | { | ||
1541 | unsigned left = 0, right = blocks.Size(); | ||
1542 | for (;;) | ||
1543 | { | ||
1544 | unsigned mid = (left + right) / 2; | ||
1545 | if (mid == left) | ||
1546 | return left; | ||
1547 | if (pos < blocks[mid].UnpPos) | ||
1548 | right = mid; | ||
1549 | else | ||
1550 | left = mid; | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1554 | STDMETHODIMP CInStream::Read(void *data, UInt32 size, UInt32 *processedSize) | ||
1555 | { | ||
1556 | COM_TRY_BEGIN | ||
1557 | |||
1558 | if (processedSize) | ||
1559 | *processedSize = 0; | ||
1560 | if (size == 0) | ||
1561 | return S_OK; | ||
1562 | if (_virtPos >= Size) | ||
1563 | return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL; | ||
1564 | { | ||
1565 | UInt64 rem = Size - _virtPos; | ||
1566 | if (size > rem) | ||
1567 | size = (UInt32)rem; | ||
1568 | } | ||
1569 | |||
1570 | if (_latestBlock >= 0) | ||
1571 | { | ||
1572 | const CBlock &block = File->Blocks[_latestBlock]; | ||
1573 | if (_virtPos < block.UnpPos || (_virtPos - block.UnpPos) >= block.UnpSize) | ||
1574 | _latestBlock = -1; | ||
1575 | } | ||
1576 | |||
1577 | if (_latestBlock < 0) | ||
1578 | { | ||
1579 | _latestChunk = -1; | ||
1580 | unsigned blockIndex = FindBlock(File->Blocks, _virtPos); | ||
1581 | const CBlock &block = File->Blocks[blockIndex]; | ||
1582 | |||
1583 | if (!block.IsZeroMethod() && block.Type != METHOD_COPY) | ||
1584 | { | ||
1585 | unsigned i; | ||
1586 | for (i = 0; i < _chunks.Size(); i++) | ||
1587 | if (_chunks[i].BlockIndex == (int)blockIndex) | ||
1588 | break; | ||
1589 | |||
1590 | if (i != _chunks.Size()) | ||
1591 | _latestChunk = i; | ||
1592 | else | ||
1593 | { | ||
1594 | const unsigned kNumChunksMax = 128; | ||
1595 | unsigned chunkIndex; | ||
1596 | |||
1597 | if (_chunks.Size() != kNumChunksMax) | ||
1598 | chunkIndex = _chunks.Add(CChunk()); | ||
1599 | else | ||
1600 | { | ||
1601 | chunkIndex = 0; | ||
1602 | for (i = 0; i < _chunks.Size(); i++) | ||
1603 | if (_chunks[i].AccessMark < _chunks[chunkIndex].AccessMark) | ||
1604 | chunkIndex = i; | ||
1605 | } | ||
1606 | |||
1607 | CChunk &chunk = _chunks[chunkIndex]; | ||
1608 | chunk.BlockIndex = -1; | ||
1609 | chunk.AccessMark = 0; | ||
1610 | |||
1611 | if (chunk.Buf.Size() < block.UnpSize) | ||
1612 | { | ||
1613 | chunk.Buf.Free(); | ||
1614 | if (block.UnpSize > ((UInt32)1 << 31)) | ||
1615 | return E_FAIL; | ||
1616 | chunk.Buf.Alloc((size_t)block.UnpSize); | ||
1617 | } | ||
1618 | |||
1619 | outStreamSpec->Init(chunk.Buf, (size_t)block.UnpSize); | ||
1620 | |||
1621 | RINOK(Stream->Seek(_startPos + File->StartPos + block.PackPos, STREAM_SEEK_SET, NULL)); | ||
1622 | |||
1623 | limitedStreamSpec->Init(block.PackSize); | ||
1624 | HRESULT res = S_OK; | ||
1625 | |||
1626 | switch (block.Type) | ||
1627 | { | ||
1628 | case METHOD_COPY: | ||
1629 | if (block.PackSize != block.UnpSize) | ||
1630 | return E_FAIL; | ||
1631 | res = ReadStream_FAIL(inStream, chunk.Buf, (size_t)block.UnpSize); | ||
1632 | break; | ||
1633 | |||
1634 | case METHOD_ADC: | ||
1635 | if (!adcCoder) | ||
1636 | { | ||
1637 | adcCoderSpec = new CAdcDecoder(); | ||
1638 | adcCoder = adcCoderSpec; | ||
1639 | } | ||
1640 | res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, NULL); | ||
1641 | break; | ||
1642 | |||
1643 | case METHOD_ZLIB: | ||
1644 | if (!zlibCoder) | ||
1645 | { | ||
1646 | zlibCoderSpec = new NCompress::NZlib::CDecoder(); | ||
1647 | zlibCoder = zlibCoderSpec; | ||
1648 | } | ||
1649 | res = zlibCoder->Code(inStream, outStream, NULL, NULL, NULL); | ||
1650 | if (res == S_OK && zlibCoderSpec->GetInputProcessedSize() != block.PackSize) | ||
1651 | res = S_FALSE; | ||
1652 | break; | ||
1653 | |||
1654 | case METHOD_BZIP2: | ||
1655 | if (!bzip2Coder) | ||
1656 | { | ||
1657 | bzip2CoderSpec = new NCompress::NBZip2::CDecoder(); | ||
1658 | bzip2Coder = bzip2CoderSpec; | ||
1659 | } | ||
1660 | res = bzip2Coder->Code(inStream, outStream, NULL, NULL, NULL); | ||
1661 | if (res == S_OK && bzip2CoderSpec->GetInputProcessedSize() != block.PackSize) | ||
1662 | res = S_FALSE; | ||
1663 | break; | ||
1664 | |||
1665 | case METHOD_LZFSE: | ||
1666 | if (!lzfseCoder) | ||
1667 | { | ||
1668 | lzfseCoderSpec = new NCompress::NLzfse::CDecoder(); | ||
1669 | lzfseCoder = lzfseCoderSpec; | ||
1670 | } | ||
1671 | res = lzfseCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, NULL); | ||
1672 | break; | ||
1673 | |||
1674 | default: | ||
1675 | return E_FAIL; | ||
1676 | } | ||
1677 | |||
1678 | if (res != S_OK) | ||
1679 | return res; | ||
1680 | if (block.Type != METHOD_COPY && outStreamSpec->GetPos() != block.UnpSize) | ||
1681 | return E_FAIL; | ||
1682 | chunk.BlockIndex = blockIndex; | ||
1683 | _latestChunk = chunkIndex; | ||
1684 | } | ||
1685 | |||
1686 | _chunks[_latestChunk].AccessMark = _accessMark++; | ||
1687 | } | ||
1688 | |||
1689 | _latestBlock = blockIndex; | ||
1690 | } | ||
1691 | |||
1692 | const CBlock &block = File->Blocks[_latestBlock]; | ||
1693 | const UInt64 offset = _virtPos - block.UnpPos; | ||
1694 | const UInt64 rem = block.UnpSize - offset; | ||
1695 | if (size > rem) | ||
1696 | size = (UInt32)rem; | ||
1697 | |||
1698 | HRESULT res = S_OK; | ||
1699 | |||
1700 | if (block.Type == METHOD_COPY) | ||
1701 | { | ||
1702 | RINOK(Stream->Seek(_startPos + File->StartPos + block.PackPos + offset, STREAM_SEEK_SET, NULL)); | ||
1703 | res = Stream->Read(data, size, &size); | ||
1704 | } | ||
1705 | else if (block.IsZeroMethod()) | ||
1706 | memset(data, 0, size); | ||
1707 | else if (size != 0) | ||
1708 | memcpy(data, _chunks[_latestChunk].Buf + (size_t)offset, size); | ||
1709 | |||
1710 | _virtPos += size; | ||
1711 | if (processedSize) | ||
1712 | *processedSize = size; | ||
1713 | |||
1714 | return res; | ||
1715 | COM_TRY_END | ||
1716 | } | ||
1717 | |||
1718 | STDMETHODIMP CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) | ||
1719 | { | ||
1720 | switch (seekOrigin) | ||
1721 | { | ||
1722 | case STREAM_SEEK_SET: break; | ||
1723 | case STREAM_SEEK_CUR: offset += _virtPos; break; | ||
1724 | case STREAM_SEEK_END: offset += Size; break; | ||
1725 | default: return STG_E_INVALIDFUNCTION; | ||
1726 | } | ||
1727 | if (offset < 0) | ||
1728 | return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; | ||
1729 | _virtPos = offset; | ||
1730 | if (newPosition) | ||
1731 | *newPosition = offset; | ||
1732 | return S_OK; | ||
1733 | } | ||
1734 | |||
1735 | STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) | ||
1736 | { | ||
1737 | COM_TRY_BEGIN | ||
1738 | |||
1739 | #ifdef DMG_SHOW_RAW | ||
1740 | if (index >= (UInt32)_files.Size()) | ||
1741 | return S_FALSE; | ||
1742 | #endif | ||
1743 | |||
1744 | CInStream *spec = new CInStream; | ||
1745 | CMyComPtr<ISequentialInStream> specStream = spec; | ||
1746 | spec->File = &_files[index]; | ||
1747 | const CFile &file = *spec->File; | ||
1748 | |||
1749 | FOR_VECTOR (i, file.Blocks) | ||
1750 | { | ||
1751 | const CBlock &block = file.Blocks[i]; | ||
1752 | switch (block.Type) | ||
1753 | { | ||
1754 | case METHOD_ZERO_0: | ||
1755 | case METHOD_ZERO_2: | ||
1756 | case METHOD_COPY: | ||
1757 | case METHOD_ADC: | ||
1758 | case METHOD_ZLIB: | ||
1759 | case METHOD_BZIP2: | ||
1760 | case METHOD_LZFSE: | ||
1761 | case METHOD_END: | ||
1762 | break; | ||
1763 | default: | ||
1764 | return S_FALSE; | ||
1765 | } | ||
1766 | } | ||
1767 | |||
1768 | spec->Stream = _inStream; | ||
1769 | spec->Size = spec->File->Size; | ||
1770 | RINOK(spec->InitAndSeek(_startPos + _dataStartOffset)); | ||
1771 | *stream = specStream.Detach(); | ||
1772 | return S_OK; | ||
1773 | |||
1774 | COM_TRY_END | ||
1775 | } | ||
1776 | |||
1777 | REGISTER_ARC_I( | ||
1778 | "Dmg", "dmg", 0, 0xE4, | ||
1779 | k_Signature, | ||
1780 | 0, | ||
1781 | NArcInfoFlags::kBackwardOpen | | ||
1782 | NArcInfoFlags::kUseGlobalOffset, | ||
1783 | NULL) | ||
1784 | |||
1785 | }} | ||