1 // ArjHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6
7 #include "../../Common/AutoPtr.h"
8 #include "../../Common/ComTry.h"
9 #include "../../Common/StringConvert.h"
10
11 #include "../../Windows/PropVariant.h"
12 #include "../../Windows/PropVariantUtils.h"
13 #include "../../Windows/TimeUtils.h"
14
15 #include "../Common/LimitedStreams.h"
16 #include "../Common/ProgressUtils.h"
17 #include "../Common/RegisterArc.h"
18 #include "../Common/StreamObjects.h"
19 #include "../Common/StreamUtils.h"
20
21 #include "../Compress/CopyCoder.h"
22 #include "../Compress/LzhDecoder.h"
23
24 #include "Common/ItemNameUtils.h"
25 #include "Common/OutStreamWithCRC.h"
26
27 namespace NCompress {
28 namespace NArj {
29 namespace NDecoder {
30
31 static const unsigned kMatchMinLen = 3;
32 static const UInt32 kWindowSize = 1 << 15; // must be >= (1 << 14)
33
34 class CCoder
35 {
36 CLzOutWindow _outWindow;
37 NBitm::CDecoder<CInBuffer> _inBitStream;
38 // bool FinishMode;
39
40 class CCoderReleaser
41 {
42 CCoder *_coder;
43 public:
CCoderReleaser(CCoder * coder)44 CCoderReleaser(CCoder *coder): _coder(coder) {}
Disable()45 void Disable() { _coder = NULL; }
~CCoderReleaser()46 ~CCoderReleaser() { if (_coder) _coder->_outWindow.Flush(); }
47 };
48 friend class CCoderReleaser;
49
50 HRESULT CodeReal(UInt32 outSize, ICompressProgressInfo *progress);
51 public:
52
53 // CCoder(): FinishMode(true) {}
GetInputProcessedSize() const54 UInt64 GetInputProcessedSize() const { return _inBitStream.GetProcessedSize(); }
55 HRESULT Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
56 UInt32 outSize, ICompressProgressInfo *progress);
57 };
58
59
CodeReal(UInt32 rem,ICompressProgressInfo * progress)60 HRESULT CCoder::CodeReal(UInt32 rem, ICompressProgressInfo *progress)
61 {
62 const UInt32 kStep = 1 << 20;
63 UInt32 next = 0;
64 if (rem > kStep && progress)
65 next = rem - kStep;
66
67 while (rem != 0)
68 {
69 if (rem <= next)
70 {
71 if (_inBitStream.ExtraBitsWereRead())
72 return S_FALSE;
73 const UInt64 packSize = _inBitStream.GetProcessedSize();
74 const UInt64 pos = _outWindow.GetProcessedSize();
75 RINOK(progress->SetRatioInfo(&packSize, &pos))
76 next = 0;
77 if (rem > kStep)
78 next = rem - kStep;
79 }
80
81 UInt32 len;
82 {
83 const unsigned kNumBits = 7 + 7;
84 const UInt32 val = _inBitStream.GetValue(kNumBits);
85
86 if ((val & (1u << (kNumBits - 1))) == 0)
87 {
88 _outWindow.PutByte((Byte)(val >> 5));
89 _inBitStream.MovePos(1 + 8);
90 rem--;
91 continue;
92 }
93
94 unsigned w;
95 {
96 UInt32 flag = (UInt32)1 << (kNumBits - 2);
97 for (w = 1; w < 7; w++, flag >>= 1)
98 if ((val & flag) == 0)
99 break;
100 }
101 const unsigned readBits = (w != 7 ? 1 : 0) + w * 2;
102 const UInt32 mask = ((UInt32)1 << w) - 1;
103 len = mask + kMatchMinLen - 1 +
104 ((val >> (kNumBits - readBits)) & mask);
105 _inBitStream.MovePos(readBits);
106 }
107 {
108 const unsigned kNumBits = 4 + 13;
109 const UInt32 val = _inBitStream.GetValue(kNumBits);
110 unsigned readBits = 1;
111 unsigned w;
112 if ((val & ((UInt32)1 << 16)) == 0) w = 9;
113 else if ((val & ((UInt32)1 << 15)) == 0) w = 10;
114 else if ((val & ((UInt32)1 << 14)) == 0) w = 11;
115 else if ((val & ((UInt32)1 << 13)) == 0) w = 12;
116 else { w = 13; readBits = 0; }
117
118 readBits += w + w - 9;
119 const UInt32 dist = ((UInt32)1 << w) - (1 << 9) +
120 (((val >> (kNumBits - readBits)) & ((1 << w) - 1)));
121 _inBitStream.MovePos(readBits);
122 if (len > rem)
123 {
124 // if (FinishMode)
125 return S_FALSE;
126 // else len = (UInt32)rem;
127 }
128 if (!_outWindow.CopyBlock(dist, len))
129 return S_FALSE;
130 rem -= len;
131 }
132 }
133
134 // if (FinishMode)
135 {
136 if (_inBitStream.ReadAlignBits() != 0)
137 return S_FALSE;
138 }
139 if (_inBitStream.ExtraBitsWereRead())
140 return S_FALSE;
141 return S_OK;
142 }
143
144
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,UInt32 outSize,ICompressProgressInfo * progress)145 HRESULT CCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
146 UInt32 outSize, ICompressProgressInfo *progress)
147 {
148 try
149 {
150 if (!_outWindow.Create(kWindowSize))
151 return E_OUTOFMEMORY;
152 if (!_inBitStream.Create(1 << 17))
153 return E_OUTOFMEMORY;
154 _outWindow.SetStream(outStream);
155 _outWindow.Init(false);
156 _inBitStream.SetStream(inStream);
157 _inBitStream.Init();
158 {
159 CCoderReleaser coderReleaser(this);
160 RINOK(CodeReal(outSize, progress))
161 coderReleaser.Disable();
162 }
163 return _outWindow.Flush();
164 }
165 catch(const CInBufferException &e) { return e.ErrorCode; }
166 catch(const CLzOutWindowException &e) { return e.ErrorCode; }
167 catch(...) { return S_FALSE; }
168 }
169
170 }}}
171
172
173
174
175 using namespace NWindows;
176
177 #define Get16(p) GetUi16(p)
178 #define Get32(p) GetUi32(p)
179
180 namespace NArchive {
181 namespace NArj {
182
183 static const unsigned kBlockSizeMin = 30;
184 static const unsigned kBlockSizeMax = 2600;
185
186 static const Byte kSig0 = 0x60;
187 static const Byte kSig1 = 0xEA;
188
189 namespace NCompressionMethod
190 {
191 enum
192 {
193 kStored = 0,
194 kCompressed1a = 1,
195 kCompressed1b = 2,
196 kCompressed1c = 3,
197 kCompressed2 = 4,
198 kNoDataNoCRC = 8,
199 kNoData = 9
200 };
201 }
202
203 namespace NFileType
204 {
205 enum
206 {
207 kBinary = 0,
208 k7BitText,
209 kArchiveHeader,
210 kDirectory,
211 kVolumeLablel,
212 kChapterLabel
213 };
214 }
215
216 namespace NFlags
217 {
218 const Byte kGarbled = 1 << 0;
219 // const Byte kAnsiPage = 1 << 1; // or (OLD_SECURED_FLAG) obsolete
220 const Byte kVolume = 1 << 2;
221 const Byte kExtFile = 1 << 3;
222 // const Byte kPathSym = 1 << 4;
223 // const Byte kBackup = 1 << 5; // obsolete
224 // const Byte kSecured = 1 << 6;
225 // const Byte kDualName = 1 << 7;
226 }
227
228 namespace NHostOS
229 {
230 enum EEnum
231 {
232 kMSDOS = 0, // MS-DOS, OS/2, Win32, pkarj 2.50 (FAT / VFAT / FAT32)
233 kPRIMOS,
234 kUnix,
235 kAMIGA,
236 kMac,
237 kOS_2,
238 kAPPLE_GS,
239 kAtari_ST,
240 kNext,
241 kVAX_VMS,
242 kWIN95
243 };
244 }
245
246 static const char * const kHostOS[] =
247 {
248 "MSDOS"
249 , "PRIMOS"
250 , "UNIX"
251 , "AMIGA"
252 , "MAC"
253 , "OS/2"
254 , "APPLE GS"
255 , "ATARI ST"
256 , "NEXT"
257 , "VAX VMS"
258 , "WIN95"
259 };
260
261 struct CArcHeader
262 {
263 // Byte ArchiverVersion;
264 // Byte ExtractVersion;
265 Byte HostOS;
266 // Byte Flags;
267 // Byte SecuryVersion;
268 // Byte FileType;
269 // Byte Reserved;
270 UInt32 CTime;
271 UInt32 MTime;
272 UInt32 ArchiveSize;
273 // UInt32 SecurPos;
274 // UInt16 FilespecPosInFilename;
275 UInt16 SecurSize;
276 // Byte EncryptionVersion;
277 // Byte LastChapter;
278 AString Name;
279 AString Comment;
280
281 HRESULT Parse(const Byte *p, unsigned size);
282 };
283
IsArc_Arj(const Byte * p,size_t size)284 API_FUNC_static_IsArc IsArc_Arj(const Byte *p, size_t size)
285 {
286 if (size < kBlockSizeMin + 4)
287 return k_IsArc_Res_NEED_MORE;
288 if (p[0] != kSig0 || p[1] != kSig1)
289 return k_IsArc_Res_NO;
290 UInt32 blockSize = Get16(p + 2);
291 if (blockSize < kBlockSizeMin ||
292 blockSize > kBlockSizeMax)
293 return k_IsArc_Res_NO;
294
295 p += 4;
296 size -= 4;
297
298 Byte headerSize = p[0];
299 if (headerSize < kBlockSizeMin ||
300 headerSize > blockSize ||
301 p[6] != NFileType::kArchiveHeader ||
302 p[28] > 8) // EncryptionVersion
303 return k_IsArc_Res_NO;
304
305 if (blockSize + 4 <= size)
306 if (Get32(p + blockSize) != CrcCalc(p, blockSize))
307 return k_IsArc_Res_NO;
308
309 return k_IsArc_Res_YES;
310 }
311 }
312
ReadString(const Byte * p,unsigned & size,AString & res)313 static HRESULT ReadString(const Byte *p, unsigned &size, AString &res)
314 {
315 unsigned num = size;
316 for (unsigned i = 0; i < num;)
317 {
318 if (p[i++] == 0)
319 {
320 size = i;
321 res = (const char *)p;
322 return S_OK;
323 }
324 }
325 return S_FALSE;
326 }
327
Parse(const Byte * p,unsigned size)328 HRESULT CArcHeader::Parse(const Byte *p, unsigned size)
329 {
330 Byte headerSize = p[0];
331 if (headerSize < kBlockSizeMin || headerSize > size)
332 return S_FALSE;
333 // ArchiverVersion = p[1];
334 // ExtractVersion = p[2];
335 HostOS = p[3];
336 // Flags = p[4];
337 // SecuryVersion = p[5];
338 if (p[6] != NFileType::kArchiveHeader)
339 return S_FALSE;
340 // Reserved = p[7];
341 CTime = Get32(p + 8);
342 MTime = Get32(p + 12);
343 ArchiveSize = Get32(p + 16); // it can be zero. (currently used only for secured archives)
344 // SecurPos = Get32(p + 20);
345 // UInt16 filespecPositionInFilename = Get16(p + 24);
346 SecurSize = Get16(p + 26);
347 // EncryptionVersion = p[28];
348 // LastChapter = p[29];
349 unsigned pos = headerSize;
350 unsigned size1 = size - pos;
351 RINOK(ReadString(p + pos, size1, Name))
352 pos += size1;
353 size1 = size - pos;
354 RINOK(ReadString(p + pos, size1, Comment))
355 pos += size1;
356 return S_OK;
357 }
358
359
360 struct CExtendedInfo
361 {
362 UInt64 Size;
363 bool CrcError;
364
ClearNArchive::CExtendedInfo365 void Clear()
366 {
367 Size = 0;
368 CrcError = false;
369 }
ParseToPropVarNArchive::CExtendedInfo370 void ParseToPropVar(NCOM::CPropVariant &prop) const
371 {
372 if (Size != 0)
373 {
374 AString s;
375 s += "Extended:";
376 s.Add_UInt32((UInt32)Size);
377 if (CrcError)
378 s += ":CRC_ERROR";
379 prop = s;
380 }
381 }
382 };
383
384
385 struct CItem
386 {
387 AString Name;
388 AString Comment;
389
390 UInt32 MTime;
391 UInt32 PackSize;
392 UInt32 Size;
393 UInt32 FileCRC;
394 UInt32 SplitPos;
395
396 Byte Version;
397 Byte ExtractVersion;
398 Byte HostOS;
399 Byte Flags;
400 Byte Method;
401 Byte FileType;
402
403 // UInt16 FilespecPosInFilename;
404 UInt16 FileAccessMode;
405 // Byte FirstChapter;
406 // Byte LastChapter;
407
408 UInt64 DataPosition;
409
410 CExtendedInfo ExtendedInfo;
411
IsEncryptedNArchive::CItem412 bool IsEncrypted() const { return (Flags & NFlags::kGarbled) != 0; }
IsDirNArchive::CItem413 bool IsDir() const { return (FileType == NFileType::kDirectory); }
IsSplitAfterNArchive::CItem414 bool IsSplitAfter() const { return (Flags & NFlags::kVolume) != 0; }
IsSplitBeforeNArchive::CItem415 bool IsSplitBefore() const { return (Flags & NFlags::kExtFile) != 0; }
GetWinAttribNArchive::CItem416 UInt32 GetWinAttrib() const
417 {
418 UInt32 atrrib = 0;
419 switch (HostOS)
420 {
421 case NHostOS::kMSDOS:
422 case NHostOS::kWIN95:
423 atrrib = FileAccessMode;
424 break;
425 }
426 if (IsDir())
427 atrrib |= FILE_ATTRIBUTE_DIRECTORY;
428 return atrrib;
429 }
430
431 HRESULT Parse(const Byte *p, unsigned size);
432 };
433
Parse(const Byte * p,unsigned size)434 HRESULT CItem::Parse(const Byte *p, unsigned size)
435 {
436 Byte headerSize = p[0];
437 if (headerSize < kBlockSizeMin || headerSize > size)
438 return S_FALSE;
439 Version = p[1];
440 ExtractVersion = p[2];
441 HostOS = p[3];
442 Flags = p[4];
443 Method = p[5];
444 FileType = p[6];
445 // Reserved = p[7];
446 MTime = Get32(p + 8);
447 PackSize = Get32(p + 12);
448 Size = Get32(p + 16);
449 FileCRC = Get32(p + 20);
450 // FilespecPosInFilename = Get16(p + 24);
451 FileAccessMode = Get16(p + 26);
452 // FirstChapter = p[28];
453 // FirstChapter = p[29];
454
455 SplitPos = 0;
456 if (IsSplitBefore() && headerSize >= 34)
457 SplitPos = Get32(p + 30);
458
459 unsigned pos = headerSize;
460 unsigned size1 = size - pos;
461 RINOK(ReadString(p + pos, size1, Name))
462 pos += size1;
463 size1 = size - pos;
464 RINOK(ReadString(p + pos, size1, Comment))
465 pos += size1;
466
467 return S_OK;
468 }
469
470 enum EErrorType
471 {
472 k_ErrorType_OK,
473 k_ErrorType_Corrupted,
474 k_ErrorType_UnexpectedEnd
475 };
476
477 class CArc
478 {
479 public:
480 UInt64 Processed;
481 EErrorType Error;
482 bool IsArc;
483 IInStream *Stream;
484 IArchiveOpenCallback *Callback;
485 UInt64 NumFiles;
486 CArcHeader Header;
487
488 CExtendedInfo ExtendedInfo;
489
490 HRESULT Open();
491 HRESULT GetNextItem(CItem &item, bool &filled);
Close()492 void Close()
493 {
494 IsArc = false;
495 Error = k_ErrorType_OK;
496 ExtendedInfo.Clear();
497 }
498 private:
499 unsigned _blockSize;
500 CByteBuffer _block;
501
502 HRESULT ReadBlock(bool &filled, CExtendedInfo *extendedInfo);
503 HRESULT SkipExtendedHeaders(CExtendedInfo &extendedInfo);
504 HRESULT Read(void *data, size_t *size);
505 };
506
Read(void * data,size_t * size)507 HRESULT CArc::Read(void *data, size_t *size)
508 {
509 HRESULT res = ReadStream(Stream, data, size);
510 Processed += *size;
511 return res;
512 }
513
514 #define READ_STREAM(_dest_, _size_) \
515 { size_t _processed_ = (_size_); RINOK(Read(_dest_, &_processed_)); \
516 if (_processed_ != (_size_)) { Error = k_ErrorType_UnexpectedEnd; return S_OK; } }
517
ReadBlock(bool & filled,CExtendedInfo * extendedInfo)518 HRESULT CArc::ReadBlock(bool &filled, CExtendedInfo *extendedInfo)
519 {
520 Error = k_ErrorType_OK;
521 filled = false;
522 Byte buf[4];
523 const unsigned signSize = extendedInfo ? 0 : 2;
524 READ_STREAM(buf, signSize + 2)
525 if (!extendedInfo)
526 if (buf[0] != kSig0 || buf[1] != kSig1)
527 {
528 Error = k_ErrorType_Corrupted;
529 return S_OK;
530 }
531 _blockSize = Get16(buf + signSize);
532 if (_blockSize == 0) // end of archive
533 return S_OK;
534
535 if (!extendedInfo)
536 if (_blockSize < kBlockSizeMin || _blockSize > kBlockSizeMax)
537 {
538 Error = k_ErrorType_Corrupted;
539 return S_OK;
540 }
541
542 const size_t readSize = _blockSize + 4;
543 if (readSize > _block.Size())
544 {
545 // extended data size is limited by (64 KB)
546 // _blockSize is less than 64 KB
547 const size_t upSize = (_blockSize > kBlockSizeMax ? (1 << 16) : kBlockSizeMax);
548 _block.Alloc(upSize + 4);
549 }
550
551 if (extendedInfo)
552 extendedInfo->Size += _blockSize;
553
554 READ_STREAM(_block, readSize)
555 if (Get32(_block + _blockSize) != CrcCalc(_block, _blockSize))
556 {
557 if (extendedInfo)
558 extendedInfo->CrcError = true;
559 else
560 {
561 Error = k_ErrorType_Corrupted;
562 return S_OK;
563 }
564 }
565 filled = true;
566 return S_OK;
567 }
568
SkipExtendedHeaders(CExtendedInfo & extendedInfo)569 HRESULT CArc::SkipExtendedHeaders(CExtendedInfo &extendedInfo)
570 {
571 extendedInfo.Clear();
572 for (UInt32 i = 0;; i++)
573 {
574 bool filled;
575 RINOK(ReadBlock(filled, &extendedInfo))
576 if (!filled)
577 return S_OK;
578 if (Callback && (i & 0xFF) == 0)
579 RINOK(Callback->SetCompleted(&NumFiles, &Processed))
580 }
581 }
582
Open()583 HRESULT CArc::Open()
584 {
585 bool filled;
586 RINOK(ReadBlock(filled, NULL)) // (extendedInfo = NULL)
587 if (!filled)
588 return S_FALSE;
589 RINOK(Header.Parse(_block, _blockSize))
590 IsArc = true;
591 return SkipExtendedHeaders(ExtendedInfo);
592 }
593
GetNextItem(CItem & item,bool & filled)594 HRESULT CArc::GetNextItem(CItem &item, bool &filled)
595 {
596 RINOK(ReadBlock(filled, NULL)) // (extendedInfo = NULL)
597 if (!filled)
598 return S_OK;
599 filled = false;
600 if (item.Parse(_block, _blockSize) != S_OK)
601 {
602 Error = k_ErrorType_Corrupted;
603 return S_OK;
604 }
605 /*
606 UInt32 extraData;
607 if ((header.Flags & NFlags::kExtFile) != 0)
608 extraData = GetUi32(_block + pos);
609 */
610
611 RINOK(SkipExtendedHeaders(item.ExtendedInfo))
612 filled = true;
613 return S_OK;
614 }
615
616
617 Z7_CLASS_IMP_CHandler_IInArchive_0
618
619 CObjectVector<CItem> _items;
620 CMyComPtr<IInStream> _stream;
621 UInt64 _phySize;
622 CArc _arc;
623
624 HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback);
625 };
626
627 static const Byte kArcProps[] =
628 {
629 kpidName,
630 kpidCTime,
631 kpidMTime,
632 kpidHostOS,
633 kpidComment,
634 kpidCharacts
635 };
636
637 static const Byte kProps[] =
638 {
639 kpidPath,
640 kpidIsDir,
641 kpidSize,
642 kpidPosition,
643 kpidPackSize,
644 kpidMTime,
645 kpidAttrib,
646 kpidEncrypted,
647 kpidCRC,
648 kpidMethod,
649 kpidHostOS,
650 kpidComment,
651 kpidCharacts
652 };
653
654 IMP_IInArchive_Props
655 IMP_IInArchive_ArcProps
656
SetTime(UInt32 dosTime,NCOM::CPropVariant & prop)657 static void SetTime(UInt32 dosTime, NCOM::CPropVariant &prop)
658 {
659 if (dosTime == 0)
660 return;
661 PropVariant_SetFrom_DosTime(prop, dosTime);
662 }
663
SetHostOS(Byte hostOS,NCOM::CPropVariant & prop)664 static void SetHostOS(Byte hostOS, NCOM::CPropVariant &prop)
665 {
666 TYPE_TO_PROP(kHostOS, hostOS, prop);
667 }
668
SetUnicodeString(const AString & s,NCOM::CPropVariant & prop)669 static void SetUnicodeString(const AString &s, NCOM::CPropVariant &prop)
670 {
671 if (!s.IsEmpty())
672 prop = MultiByteToUnicodeString(s, CP_OEMCP);
673 }
674
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))675 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
676 {
677 COM_TRY_BEGIN
678 NCOM::CPropVariant prop;
679 switch (propID)
680 {
681 case kpidPhySize: prop = _phySize; break;
682 case kpidName: SetUnicodeString(_arc.Header.Name, prop); break;
683 case kpidCTime: SetTime(_arc.Header.CTime, prop); break;
684 case kpidMTime: SetTime(_arc.Header.MTime, prop); break;
685 case kpidHostOS: SetHostOS(_arc.Header.HostOS, prop); break;
686 case kpidComment: SetUnicodeString(_arc.Header.Comment, prop); break;
687 case kpidErrorFlags:
688 {
689 UInt32 v = 0;
690 if (!_arc.IsArc) v |= kpv_ErrorFlags_IsNotArc;
691 switch (_arc.Error)
692 {
693 case k_ErrorType_UnexpectedEnd: v |= kpv_ErrorFlags_UnexpectedEnd; break;
694 case k_ErrorType_Corrupted: v |= kpv_ErrorFlags_HeadersError; break;
695 case k_ErrorType_OK:
696 // default:
697 break;
698 }
699 prop = v;
700 break;
701 }
702 case kpidCharacts: _arc.ExtendedInfo.ParseToPropVar(prop); break;
703 }
704 prop.Detach(value);
705 return S_OK;
706 COM_TRY_END
707 }
708
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))709 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
710 {
711 *numItems = _items.Size();
712 return S_OK;
713 }
714
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))715 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
716 {
717 COM_TRY_BEGIN
718 NCOM::CPropVariant prop;
719 const CItem &item = _items[index];
720 switch (propID)
721 {
722 case kpidPath: prop = NItemName::GetOsPath(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break;
723 case kpidIsDir: prop = item.IsDir(); break;
724 case kpidSize: prop = item.Size; break;
725 case kpidPackSize: prop = item.PackSize; break;
726 case kpidPosition: if (item.IsSplitBefore() || item.IsSplitAfter()) prop = (UInt64)item.SplitPos; break;
727 case kpidAttrib: prop = item.GetWinAttrib(); break;
728 case kpidEncrypted: prop = item.IsEncrypted(); break;
729 case kpidCRC: prop = item.FileCRC; break;
730 case kpidMethod: prop = item.Method; break;
731 case kpidHostOS: SetHostOS(item.HostOS, prop); break;
732 case kpidMTime: SetTime(item.MTime, prop); break;
733 case kpidComment: SetUnicodeString(item.Comment, prop); break;
734 case kpidCharacts: item.ExtendedInfo.ParseToPropVar(prop); break;
735 }
736 prop.Detach(value);
737 return S_OK;
738 COM_TRY_END
739 }
740
Open2(IInStream * inStream,IArchiveOpenCallback * callback)741 HRESULT CHandler::Open2(IInStream *inStream, IArchiveOpenCallback *callback)
742 {
743 Close();
744
745 UInt64 endPos;
746 RINOK(InStream_AtBegin_GetSize(inStream, endPos))
747
748 _arc.Stream = inStream;
749 _arc.Callback = callback;
750 _arc.NumFiles = 0;
751 _arc.Processed = 0;
752
753 RINOK(_arc.Open())
754
755 _phySize = _arc.Processed;
756 if (_arc.Header.ArchiveSize != 0)
757 _phySize = (UInt64)_arc.Header.ArchiveSize + _arc.Header.SecurSize;
758
759 for (;;)
760 {
761 CItem item;
762 bool filled;
763
764 _arc.Error = k_ErrorType_OK;
765 RINOK(_arc.GetNextItem(item, filled))
766
767 if (_arc.Error != k_ErrorType_OK)
768 break;
769
770 if (!filled)
771 {
772 if (_arc.Error == k_ErrorType_OK)
773 if (_arc.Header.ArchiveSize == 0)
774 _phySize = _arc.Processed;
775 break;
776 }
777 item.DataPosition = _arc.Processed;
778 _items.Add(item);
779
780 UInt64 pos = item.DataPosition + item.PackSize;
781 if (_arc.Header.ArchiveSize == 0)
782 _phySize = pos;
783 if (pos > endPos)
784 {
785 _arc.Error = k_ErrorType_UnexpectedEnd;
786 break;
787 }
788
789 RINOK(InStream_SeekSet(inStream, pos))
790 _arc.NumFiles = _items.Size();
791 _arc.Processed = pos;
792
793 if (callback && (_items.Size() & 0xFF) == 0)
794 {
795 RINOK(callback->SetCompleted(&_arc.NumFiles, &_arc.Processed))
796 }
797 }
798 return S_OK;
799 }
800
Z7_COM7F_IMF(CHandler::Open (IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback))801 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
802 const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback))
803 {
804 COM_TRY_BEGIN
805 HRESULT res;
806 {
807 res = Open2(inStream, callback);
808 if (res == S_OK)
809 {
810 _stream = inStream;
811 return S_OK;
812 }
813 }
814 return res;
815 COM_TRY_END
816 }
817
Z7_COM7F_IMF(CHandler::Close ())818 Z7_COM7F_IMF(CHandler::Close())
819 {
820 _arc.Close();
821 _phySize = 0;
822 _items.Clear();
823 _stream.Release();
824 return S_OK;
825 }
826
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))827 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
828 Int32 testMode, IArchiveExtractCallback *extractCallback))
829 {
830 COM_TRY_BEGIN
831 UInt64 totalUnpacked = 0, totalPacked = 0;
832 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
833 if (allFilesMode)
834 numItems = _items.Size();
835 if (numItems == 0)
836 return S_OK;
837 UInt32 i;
838 for (i = 0; i < numItems; i++)
839 {
840 const CItem &item = _items[allFilesMode ? i : indices[i]];
841 totalUnpacked += item.Size;
842 // totalPacked += item.PackSize;
843 }
844 RINOK(extractCallback->SetTotal(totalUnpacked))
845
846 totalUnpacked = totalPacked = 0;
847 UInt32 curUnpacked, curPacked;
848
849 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
850 lps->Init(extractCallback, false);
851 CMyUniquePtr<NCompress::NLzh::NDecoder::CCoder> lzhDecoder;
852 CMyUniquePtr<NCompress::NArj::NDecoder::CCoder> arjDecoder;
853 CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
854 CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
855 inStream->SetStream(_stream);
856
857 for (i = 0;; i++,
858 totalUnpacked += curUnpacked,
859 totalPacked += curPacked)
860 {
861 lps->InSize = totalPacked;
862 lps->OutSize = totalUnpacked;
863 RINOK(lps->SetCur())
864 if (i >= numItems)
865 break;
866
867 curUnpacked = curPacked = 0;
868
869 Int32 opRes;
870 {
871 CMyComPtr<ISequentialOutStream> realOutStream;
872 const Int32 askMode = testMode ?
873 NExtract::NAskMode::kTest :
874 NExtract::NAskMode::kExtract;
875 const UInt32 index = allFilesMode ? i : indices[i];
876 const CItem &item = _items[index];
877 RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
878
879 if (item.IsDir())
880 {
881 // if (!testMode)
882 {
883 RINOK(extractCallback->PrepareOperation(askMode))
884 // realOutStream.Release();
885 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
886 }
887 continue;
888 }
889
890 if (!testMode && !realOutStream)
891 continue;
892
893 RINOK(extractCallback->PrepareOperation(askMode))
894 curUnpacked = item.Size;
895 curPacked = item.PackSize;
896
897 CMyComPtr2_Create<ISequentialOutStream, COutStreamWithCRC> outStream;
898 outStream->SetStream(realOutStream);
899 // realOutStream.Release();
900 outStream->Init();
901
902 inStream->Init(item.PackSize);
903
904 RINOK(InStream_SeekSet(_stream, item.DataPosition))
905
906 HRESULT result = S_OK;
907 opRes = NExtract::NOperationResult::kOK;
908
909 if (item.IsEncrypted())
910 opRes = NExtract::NOperationResult::kUnsupportedMethod;
911 else
912 {
913 switch (item.Method)
914 {
915 case NCompressionMethod::kStored:
916 {
917 result = copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps);
918 if (result == S_OK && copyCoder->TotalSize != item.PackSize)
919 result = S_FALSE;
920 break;
921 }
922 case NCompressionMethod::kCompressed1a:
923 case NCompressionMethod::kCompressed1b:
924 case NCompressionMethod::kCompressed1c:
925 {
926 lzhDecoder.Create_if_Empty();
927 // lzhDecoder->FinishMode = true;
928 const UInt32 kHistorySize = 26624;
929 lzhDecoder->SetDictSize(kHistorySize);
930 result = lzhDecoder->Code(inStream, outStream, curUnpacked, lps);
931 if (result == S_OK && lzhDecoder->GetInputProcessedSize() != item.PackSize)
932 result = S_FALSE;
933 break;
934 }
935 case NCompressionMethod::kCompressed2:
936 {
937 arjDecoder.Create_if_Empty();
938 // arjDecoderSpec->FinishMode = true;
939 result = arjDecoder->Code(inStream, outStream, curUnpacked, lps);
940 if (result == S_OK && arjDecoder->GetInputProcessedSize() != item.PackSize)
941 result = S_FALSE;
942 break;
943 }
944 default:
945 opRes = NExtract::NOperationResult::kUnsupportedMethod;
946 }
947 }
948
949 if (opRes == NExtract::NOperationResult::kOK)
950 {
951 if (result == S_FALSE)
952 opRes = NExtract::NOperationResult::kDataError;
953 else
954 {
955 RINOK(result)
956 opRes = (outStream->GetCRC() == item.FileCRC) ?
957 NExtract::NOperationResult::kOK:
958 NExtract::NOperationResult::kCRCError;
959 }
960 }
961 }
962 RINOK(extractCallback->SetOperationResult(opRes))
963 }
964
965 return S_OK;
966 COM_TRY_END
967 }
968
969 static const Byte k_Signature[] = { kSig0, kSig1 };
970
971 REGISTER_ARC_I(
972 "Arj", "arj", NULL, 4,
973 k_Signature,
974 0,
975 0,
976 IsArc_Arj)
977
978 }}
979