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