1 // OpenArchive.cpp
2
3 #include "StdAfx.h"
4
5 // #define SHOW_DEBUG_INFO
6
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #endif
10
11 #include "../../../../C/CpuArch.h"
12
13 #include "../../../Common/ComTry.h"
14 #include "../../../Common/IntToString.h"
15 #include "../../../Common/StringConvert.h"
16 #include "../../../Common/StringToInt.h"
17 #include "../../../Common/Wildcard.h"
18
19 #include "../../../Windows/FileDir.h"
20
21 #include "../../Common/FileStreams.h"
22 #include "../../Common/LimitedStreams.h"
23 #include "../../Common/ProgressUtils.h"
24 #include "../../Common/StreamUtils.h"
25
26 #include "../../Compress/CopyCoder.h"
27
28 #include "DefaultName.h"
29 #include "OpenArchive.h"
30
31 #ifndef _SFX
32 #include "SetProperties.h"
33 #endif
34
35 #ifdef SHOW_DEBUG_INFO
36 #define PRF(x) x
37 #else
38 #define PRF(x)
39 #endif
40
41 // increase it, if you need to support larger SFX stubs
42 static const UInt64 kMaxCheckStartPosition = 1 << 23;
43
44 /*
45 Open:
46 - formatIndex >= 0 (exact Format)
47 1) Open with main type. Archive handler is allowed to use archive start finder.
48 Warning, if there is tail.
49
50 - formatIndex = -1 (Parser:0) (default)
51 - same as #1 but doesn't return Parser
52
53 - formatIndex = -2 (#1)
54 - file has supported extension (like a.7z)
55 Open with that main type (only starting from start of file).
56 - open OK:
57 - if there is no tail - return OK
58 - if there is tail:
59 - archive is not "Self Exe" - return OK with Warning, that there is tail
60 - archive is "Self Exe"
61 ignore "Self Exe" stub, and tries to open tail
62 - tail can be open as archive - shows that archive and stub size property.
63 - tail can't be open as archive - shows Parser ???
64 - open FAIL:
65 Try to open with all other types from offset 0 only.
66 If some open type is OK and physical archive size is uequal or larger
67 than file size, then return that archive with warning that can not be open as [extension type].
68 If extension was EXE, it will try to open as unknown_extension case
69 - file has unknown extension (like a.hhh)
70 It tries to open via parser code.
71 - if there is full archive or tail archive and unknown block or "Self Exe"
72 at front, it shows tail archive and stub size property.
73 - in another cases, if there is some archive inside file, it returns parser/
74 - in another cases, it retuens S_FALSE
75
76
77 - formatIndex = -3 (#2)
78 - same as #1, but
79 - stub (EXE) + archive is open in Parser
80
81 - formatIndex = -4 (#3)
82 - returns only Parser. skip full file archive. And show other sub-archives
83
84 - formatIndex = -5 (#4)
85 - returns only Parser. skip full file archive. And show other sub-archives for each byte pos
86
87 */
88
89
90
91
92 using namespace NWindows;
93
94 /*
95 #ifdef _SFX
96 #define OPEN_PROPS_PARAM
97 #else
98 #define OPEN_PROPS_PARAM , props
99 #endif
100 */
101
102 /*
103 CArc::~CArc()
104 {
105 GetRawProps.Release();
106 Archive.Release();
107 printf("\nCArc::~CArc()\n");
108 }
109 */
110
111 #ifndef _SFX
112
113 namespace NArchive {
114 namespace NParser {
115
116 struct CParseItem
117 {
118 UInt64 Offset;
119 UInt64 Size;
120 // UInt64 OkSize;
121 UString Name;
122 UString Extension;
123 FILETIME FileTime;
124 UString Comment;
125 UString ArcType;
126
127 bool FileTime_Defined;
128 bool UnpackSize_Defined;
129 bool NumSubDirs_Defined;
130 bool NumSubFiles_Defined;
131
132 bool IsSelfExe;
133 bool IsNotArcType;
134
135 UInt64 UnpackSize;
136 UInt64 NumSubDirs;
137 UInt64 NumSubFiles;
138
139 int FormatIndex;
140
141 bool LenIsUnknown;
142
CParseItemNArchive::NParser::CParseItem143 CParseItem():
144 LenIsUnknown(false),
145 FileTime_Defined(false),
146 UnpackSize_Defined(false),
147 NumSubFiles_Defined(false),
148 NumSubDirs_Defined(false),
149 IsSelfExe(false),
150 IsNotArcType(false)
151 // OkSize(0)
152 {}
153
154 /*
155 bool IsEqualTo(const CParseItem &item) const
156 {
157 return Offset == item.Offset && Size == item.Size;
158 }
159 */
160
NormalizeOffsetNArchive::NParser::CParseItem161 void NormalizeOffset()
162 {
163 if ((Int64)Offset < 0)
164 {
165 Size += Offset;
166 // OkSize += Offset;
167 Offset = 0;
168 }
169 }
170 };
171
172 class CHandler:
173 public IInArchive,
174 public IInArchiveGetStream,
175 public CMyUnknownImp
176 {
177 public:
178 CObjectVector<CParseItem> _items;
179 UInt64 _maxEndOffset;
180 CMyComPtr<IInStream> _stream;
181
182 MY_UNKNOWN_IMP2(
183 IInArchive,
184 IInArchiveGetStream)
185
186 INTERFACE_IInArchive(;)
187 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
188
GetLastEnd() const189 UInt64 GetLastEnd() const
190 {
191 if (_items.IsEmpty())
192 return 0;
193 const CParseItem &back = _items.Back();
194 return back.Offset + back.Size;
195 }
196
197 void AddUnknownItem(UInt64 next);
198 int FindInsertPos(const CParseItem &item) const;
199 void AddItem(const CParseItem &item);
200
CHandler()201 CHandler(): _maxEndOffset(0) {}
202 };
203
FindInsertPos(const CParseItem & item) const204 int CHandler::FindInsertPos(const CParseItem &item) const
205 {
206 unsigned left = 0, right = _items.Size();
207 while (left != right)
208 {
209 unsigned mid = (left + right) / 2;
210 const CParseItem & midItem = _items[mid];
211 if (item.Offset < midItem.Offset)
212 right = mid;
213 else if (item.Offset > midItem.Offset)
214 left = mid + 1;
215 else if (item.Size < midItem.Size)
216 right = mid;
217 else if (item.Size > midItem.Size)
218 left = mid + 1;
219 else
220 {
221 left = mid + 1;
222 // return -1;
223 }
224 }
225 return left;
226 }
227
AddUnknownItem(UInt64 next)228 void CHandler::AddUnknownItem(UInt64 next)
229 {
230 /*
231 UInt64 prevEnd = 0;
232 if (!_items.IsEmpty())
233 {
234 const CParseItem &back = _items.Back();
235 prevEnd = back.Offset + back.Size;
236 }
237 */
238 if (_maxEndOffset < next)
239 {
240 CParseItem item2;
241 item2.Offset = _maxEndOffset;
242 item2.Size = next - _maxEndOffset;
243 _maxEndOffset = next;
244 _items.Add(item2);
245 }
246 else if (_maxEndOffset > next && !_items.IsEmpty())
247 {
248 CParseItem &back = _items.Back();
249 if (back.LenIsUnknown)
250 {
251 back.Size = next - back.Offset;
252 _maxEndOffset = next;
253 }
254 }
255 }
256
AddItem(const CParseItem & item)257 void CHandler::AddItem(const CParseItem &item)
258 {
259 AddUnknownItem(item.Offset);
260 int pos = FindInsertPos(item);
261 if (pos >= 0)
262 {
263 _items.Insert(pos, item);
264 UInt64 next = item.Offset + item.Size;
265 if (_maxEndOffset < next)
266 _maxEndOffset = next;
267 }
268 }
269
270 /*
271 static const CStatProp kProps[] =
272 {
273 { NULL, kpidPath, VT_BSTR},
274 { NULL, kpidSize, VT_UI8},
275 { NULL, kpidMTime, VT_FILETIME},
276 { NULL, kpidType, VT_BSTR},
277 { NULL, kpidComment, VT_BSTR},
278 { NULL, kpidOffset, VT_UI8},
279 { NULL, kpidUnpackSize, VT_UI8},
280 // { NULL, kpidNumSubDirs, VT_UI8},
281 };
282 */
283
284 static const Byte kProps[] =
285 {
286 kpidPath,
287 kpidSize,
288 kpidMTime,
289 kpidType,
290 kpidComment,
291 kpidOffset,
292 kpidUnpackSize
293 };
294
295 IMP_IInArchive_Props
296 IMP_IInArchive_ArcProps_NO
297
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)298 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */)
299 {
300 COM_TRY_BEGIN
301 {
302 Close();
303 _stream = stream;
304 }
305 return S_OK;
306 COM_TRY_END
307 }
308
Close()309 STDMETHODIMP CHandler::Close()
310 {
311 _items.Clear();
312 _stream.Release();
313 return S_OK;
314 }
315
GetNumberOfItems(UInt32 * numItems)316 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
317 {
318 *numItems = _items.Size();
319 return S_OK;
320 }
321
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)322 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
323 {
324 COM_TRY_BEGIN
325 NCOM::CPropVariant prop;
326
327 const CParseItem &item = _items[index];
328
329 switch (propID)
330 {
331 case kpidPath:
332 {
333 wchar_t sz[32];
334 ConvertUInt32ToString(index + 1, sz);
335 UString s = sz;
336 if (!item.Name.IsEmpty())
337 {
338 s += L'.';
339 s += item.Name;
340 }
341 if (!item.Extension.IsEmpty())
342 {
343 s += L'.';
344 s += item.Extension;
345 }
346 prop = s; break;
347 }
348 case kpidSize:
349 case kpidPackSize: prop = item.Size; break;
350 case kpidOffset: prop = item.Offset; break;
351 case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break;
352 case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break;
353 case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break;
354 case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break;
355 case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break;
356 case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break;
357 }
358 prop.Detach(value);
359 return S_OK;
360 COM_TRY_END
361 }
362
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)363 HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
364 Int32 testMode, IArchiveExtractCallback *extractCallback)
365 {
366 COM_TRY_BEGIN
367
368 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
369 if (allFilesMode)
370 numItems = _items.Size();
371 if (_stream && numItems == 0)
372 return S_OK;
373 UInt64 totalSize = 0;
374 UInt32 i;
375 for (i = 0; i < numItems; i++)
376 totalSize += _items[allFilesMode ? i : indices[i]].Size;
377 extractCallback->SetTotal(totalSize);
378
379 totalSize = 0;
380
381 CLocalProgress *lps = new CLocalProgress;
382 CMyComPtr<ICompressProgressInfo> progress = lps;
383 lps->Init(extractCallback, false);
384
385 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
386 CMyComPtr<ISequentialInStream> inStream(streamSpec);
387 streamSpec->SetStream(_stream);
388
389 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
390 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
391
392 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
393 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
394
395 for (i = 0; i < numItems; i++)
396 {
397 lps->InSize = totalSize;
398 lps->OutSize = totalSize;
399 RINOK(lps->SetCur());
400 CMyComPtr<ISequentialOutStream> realOutStream;
401 Int32 askMode = testMode ?
402 NExtract::NAskMode::kTest :
403 NExtract::NAskMode::kExtract;
404 Int32 index = allFilesMode ? i : indices[i];
405 const CParseItem &item = _items[index];
406
407 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
408 UInt64 unpackSize = item.Size;
409 totalSize += unpackSize;
410 bool skipMode = false;
411 if (!testMode && !realOutStream)
412 continue;
413 RINOK(extractCallback->PrepareOperation(askMode));
414
415 outStreamSpec->SetStream(realOutStream);
416 realOutStream.Release();
417 outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
418
419 Int32 opRes = NExtract::NOperationResult::kOK;
420 RINOK(_stream->Seek(item.Offset, STREAM_SEEK_SET, NULL));
421 streamSpec->Init(unpackSize);
422 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
423
424 if (outStreamSpec->GetRem() != 0)
425 opRes = NExtract::NOperationResult::kDataError;
426 outStreamSpec->ReleaseStream();
427 RINOK(extractCallback->SetOperationResult(opRes));
428 }
429
430 return S_OK;
431
432 COM_TRY_END
433 }
434
435
GetStream(UInt32 index,ISequentialInStream ** stream)436 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
437 {
438 COM_TRY_BEGIN
439 const CParseItem &item = _items[index];
440 return CreateLimitedInStream(_stream, item.Offset, item.Size, stream);
441 COM_TRY_END
442 }
443
444 }}
445
446 #endif
447
Archive_GetItemBoolProp(IInArchive * arc,UInt32 index,PROPID propID,bool & result)448 HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw()
449 {
450 NCOM::CPropVariant prop;
451 result = false;
452 RINOK(arc->GetProperty(index, propID, &prop));
453 if (prop.vt == VT_BOOL)
454 result = VARIANT_BOOLToBool(prop.boolVal);
455 else if (prop.vt != VT_EMPTY)
456 return E_FAIL;
457 return S_OK;
458 }
459
Archive_IsItem_Dir(IInArchive * arc,UInt32 index,bool & result)460 HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw()
461 {
462 return Archive_GetItemBoolProp(arc, index, kpidIsDir, result);
463 }
464
Archive_IsItem_Aux(IInArchive * arc,UInt32 index,bool & result)465 HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw()
466 {
467 return Archive_GetItemBoolProp(arc, index, kpidIsAux, result);
468 }
469
Archive_IsItem_AltStream(IInArchive * arc,UInt32 index,bool & result)470 HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw()
471 {
472 return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result);
473 }
474
Archive_IsItem_Deleted(IInArchive * arc,UInt32 index,bool & result)475 HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw()
476 {
477 return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result);
478 }
479
Archive_GetArcBoolProp(IInArchive * arc,PROPID propid,bool & result)480 static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) throw()
481 {
482 NCOM::CPropVariant prop;
483 result = false;
484 RINOK(arc->GetArchiveProperty(propid, &prop));
485 if (prop.vt == VT_BOOL)
486 result = VARIANT_BOOLToBool(prop.boolVal);
487 else if (prop.vt != VT_EMPTY)
488 return E_FAIL;
489 return S_OK;
490 }
491
Archive_GetArcProp_UInt(IInArchive * arc,PROPID propid,UInt64 & result,bool & defined)492 static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined)
493 {
494 defined = false;
495 NCOM::CPropVariant prop;
496 RINOK(arc->GetArchiveProperty(propid, &prop));
497 switch (prop.vt)
498 {
499 case VT_UI4: result = prop.ulVal; defined = true; break;
500 case VT_I4: result = (Int64)prop.lVal; defined = true; break;
501 case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; defined = true; break;
502 case VT_I8: result = (UInt64)prop.hVal.QuadPart; defined = true; break;
503 case VT_EMPTY: break;
504 default: return E_FAIL;
505 }
506 return S_OK;
507 }
508
Archive_GetArcProp_Int(IInArchive * arc,PROPID propid,Int64 & result,bool & defined)509 static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined)
510 {
511 defined = false;
512 NCOM::CPropVariant prop;
513 RINOK(arc->GetArchiveProperty(propid, &prop));
514 switch (prop.vt)
515 {
516 case VT_UI4: result = prop.ulVal; defined = true; break;
517 case VT_I4: result = prop.lVal; defined = true; break;
518 case VT_UI8: result = (Int64)prop.uhVal.QuadPart; defined = true; break;
519 case VT_I8: result = (Int64)prop.hVal.QuadPart; defined = true; break;
520 case VT_EMPTY: break;
521 default: return E_FAIL;
522 }
523 return S_OK;
524 }
525
526 #ifndef _SFX
527
GetItemPathToParent(UInt32 index,UInt32 parent,UStringVector & parts) const528 HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const
529 {
530 if (!GetRawProps)
531 return E_FAIL;
532 if (index == parent)
533 return S_OK;
534 UInt32 curIndex = index;
535
536 UString s;
537
538 bool prevWasAltStream = false;
539
540 for (;;)
541 {
542 #ifdef MY_CPU_LE
543 const void *p;
544 UInt32 size;
545 UInt32 propType;
546 RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType));
547 if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE)
548 s = (const wchar_t *)p;
549 else
550 #endif
551 {
552 NCOM::CPropVariant prop;
553 RINOK(Archive->GetProperty(curIndex, kpidName, &prop));
554 if (prop.vt == VT_BSTR && prop.bstrVal)
555 s.SetFromBstr(prop.bstrVal);
556 else if (prop.vt == VT_EMPTY)
557 s.Empty();
558 else
559 return E_FAIL;
560 }
561
562 UInt32 curParent = (UInt32)(Int32)-1;
563 UInt32 parentType = 0;
564 RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType));
565
566 if (parentType != NParentType::kAltStream)
567 {
568 for (;;)
569 {
570 int pos = s.ReverseFind_PathSepar();
571 if (pos < 0)
572 {
573 break;
574 }
575 parts.Insert(0, s.Ptr(pos + 1));
576 s.DeleteFrom(pos);
577 }
578 }
579
580 parts.Insert(0, s);
581
582 if (prevWasAltStream)
583 {
584 {
585 UString &s2 = parts[parts.Size() - 2];
586 s2 += L':';
587 s2 += parts.Back();
588 }
589 parts.DeleteBack();
590 }
591
592 if (parent == curParent)
593 return S_OK;
594
595 prevWasAltStream = false;
596 if (parentType == NParentType::kAltStream)
597 prevWasAltStream = true;
598
599 if (curParent == (UInt32)(Int32)-1)
600 return E_FAIL;
601 curIndex = curParent;
602 }
603 }
604
605 #endif
606
GetItemPath(UInt32 index,UString & result) const607 HRESULT CArc::GetItemPath(UInt32 index, UString &result) const
608 {
609 #ifdef MY_CPU_LE
610 if (GetRawProps)
611 {
612 const void *p;
613 UInt32 size;
614 UInt32 propType;
615 if (!IsTree)
616 {
617 if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK &&
618 propType == NPropDataType::kUtf16z)
619 {
620 unsigned len = size / 2 - 1;
621 wchar_t *s = result.GetBuf(len);
622 for (unsigned i = 0; i < len; i++)
623 {
624 wchar_t c = GetUi16(p);
625 p = (const void *)((const Byte *)p + 2);
626 #if WCHAR_PATH_SEPARATOR != L'/'
627 if (c == L'/')
628 c = WCHAR_PATH_SEPARATOR;
629 #endif
630 *s++ = c;
631 }
632 *s = 0;
633 result.ReleaseBuf_SetLen(len);
634 if (len != 0)
635 return S_OK;
636 }
637 }
638 /*
639 else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK &&
640 p && propType == NPropDataType::kUtf16z)
641 {
642 size -= 2;
643 UInt32 totalSize = size;
644 bool isOK = false;
645
646 {
647 UInt32 index2 = index;
648 for (;;)
649 {
650 UInt32 parent = (UInt32)(Int32)-1;
651 UInt32 parentType = 0;
652 if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK)
653 break;
654 if (parent == (UInt32)(Int32)-1)
655 {
656 if (parentType != 0)
657 totalSize += 2;
658 isOK = true;
659 break;
660 }
661 index2 = parent;
662 UInt32 size2;
663 const void *p2;
664 if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK &&
665 p2 && propType == NPropDataType::kUtf16z)
666 break;
667 totalSize += size2;
668 }
669 }
670
671 if (isOK)
672 {
673 wchar_t *sz = result.GetBuf_SetEnd(totalSize / 2);
674 UInt32 pos = totalSize - size;
675 memcpy((Byte *)sz + pos, p, size);
676 UInt32 index2 = index;
677 for (;;)
678 {
679 UInt32 parent = (UInt32)(Int32)-1;
680 UInt32 parentType = 0;
681 if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK)
682 break;
683 if (parent == (UInt32)(Int32)-1)
684 {
685 if (parentType != 0)
686 sz[pos / 2 - 1] = L':';
687 break;
688 }
689 index2 = parent;
690 UInt32 size2;
691 const void *p2;
692 if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK)
693 break;
694 pos -= size2;
695 memcpy((Byte *)sz + pos, p2, size2);
696 sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':';
697 }
698 #ifdef _WIN32
699 // result.Replace(L'/', WCHAR_PATH_SEPARATOR);
700 #endif
701 return S_OK;
702 }
703 }
704 */
705 }
706 #endif
707
708 {
709 NCOM::CPropVariant prop;
710 RINOK(Archive->GetProperty(index, kpidPath, &prop));
711 if (prop.vt == VT_BSTR && prop.bstrVal)
712 result.SetFromBstr(prop.bstrVal);
713 else if (prop.vt == VT_EMPTY)
714 result.Empty();
715 else
716 return E_FAIL;
717 }
718
719 if (result.IsEmpty())
720 return GetDefaultItemPath(index, result);
721 return S_OK;
722 }
723
GetDefaultItemPath(UInt32 index,UString & result) const724 HRESULT CArc::GetDefaultItemPath(UInt32 index, UString &result) const
725 {
726 result.Empty();
727 bool isDir;
728 RINOK(Archive_IsItem_Dir(Archive, index, isDir));
729 if (!isDir)
730 {
731 result = DefaultName;
732 NCOM::CPropVariant prop;
733 RINOK(Archive->GetProperty(index, kpidExtension, &prop));
734 if (prop.vt == VT_BSTR)
735 {
736 result += L'.';
737 result += prop.bstrVal;
738 }
739 else if (prop.vt != VT_EMPTY)
740 return E_FAIL;
741 }
742 return S_OK;
743 }
744
GetItemPath2(UInt32 index,UString & result) const745 HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const
746 {
747 RINOK(GetItemPath(index, result));
748 if (Ask_Deleted)
749 {
750 bool isDeleted = false;
751 RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted));
752 if (isDeleted)
753 result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR);
754 }
755 return S_OK;
756 }
757
758 #ifdef SUPPORT_ALT_STREAMS
759
FindAltStreamColon_in_Path(const wchar_t * path)760 int FindAltStreamColon_in_Path(const wchar_t *path)
761 {
762 unsigned i = 0;
763 int colonPos = -1;
764 for (;; i++)
765 {
766 wchar_t c = path[i];
767 if (c == 0)
768 return colonPos;
769 if (c == ':')
770 {
771 if (colonPos < 0)
772 colonPos = i;
773 continue;
774 }
775 if (c == WCHAR_PATH_SEPARATOR)
776 colonPos = -1;
777 }
778 }
779
780 #endif
781
GetItem(UInt32 index,CReadArcItem & item) const782 HRESULT CArc::GetItem(UInt32 index, CReadArcItem &item) const
783 {
784 #ifdef SUPPORT_ALT_STREAMS
785 item.IsAltStream = false;
786 item.AltStreamName.Empty();
787 item.MainPath.Empty();
788 #endif
789
790 item.IsDir = false;
791 item.Path.Empty();
792 item.ParentIndex = (UInt32)(Int32)-1;
793
794 item.PathParts.Clear();
795
796 RINOK(Archive_IsItem_Dir(Archive, index, item.IsDir));
797 item.MainIsDir = item.IsDir;
798
799 RINOK(GetItemPath2(index, item.Path));
800
801 #ifndef _SFX
802 UInt32 mainIndex = index;
803 #endif
804
805 #ifdef SUPPORT_ALT_STREAMS
806
807 item.MainPath = item.Path;
808 if (Ask_AltStream)
809 {
810 RINOK(Archive_IsItem_AltStream(Archive, index, item.IsAltStream));
811 }
812
813 bool needFindAltStream = false;
814
815 if (item.IsAltStream)
816 {
817 needFindAltStream = true;
818 if (GetRawProps)
819 {
820 UInt32 parentType = 0;
821 UInt32 parentIndex;
822 RINOK(GetRawProps->GetParent(index, &parentIndex, &parentType));
823 if (parentType == NParentType::kAltStream)
824 {
825 NCOM::CPropVariant prop;
826 RINOK(Archive->GetProperty(index, kpidName, &prop));
827 if (prop.vt == VT_BSTR && prop.bstrVal)
828 item.AltStreamName.SetFromBstr(prop.bstrVal);
829 else if (prop.vt != VT_EMPTY)
830 return E_FAIL;
831 else
832 {
833 // item.IsAltStream = false;
834 }
835 /*
836 if (item.AltStreamName.IsEmpty())
837 item.IsAltStream = false;
838 */
839
840 needFindAltStream = false;
841 item.ParentIndex = parentIndex;
842 mainIndex = parentIndex;
843
844 if (parentIndex == (UInt32)(Int32)-1)
845 {
846 item.MainPath.Empty();
847 item.MainIsDir = true;
848 }
849 else
850 {
851 RINOK(GetItemPath2(parentIndex, item.MainPath));
852 RINOK(Archive_IsItem_Dir(Archive, parentIndex, item.MainIsDir));
853 }
854 }
855 }
856 }
857
858 if (item.WriteToAltStreamIfColon || needFindAltStream)
859 {
860 /* Good handler must support GetRawProps::GetParent for alt streams./
861 So the following code currently is not used */
862 int colon = FindAltStreamColon_in_Path(item.Path);
863 if (colon >= 0)
864 {
865 item.MainPath.DeleteFrom(colon);
866 item.AltStreamName = item.Path.Ptr(colon + 1);
867 item.MainIsDir = (colon == 0 || IsPathSepar(item.Path[(unsigned)colon - 1]));
868 item.IsAltStream = true;
869 }
870 }
871
872 #endif
873
874 #ifndef _SFX
875 if (item._use_baseParentFolder_mode)
876 {
877 RINOK(GetItemPathToParent(mainIndex, item._baseParentFolder, item.PathParts));
878
879 #ifdef SUPPORT_ALT_STREAMS
880 if ((item.WriteToAltStreamIfColon || needFindAltStream) && !item.PathParts.IsEmpty())
881 {
882 int colon;
883 {
884 UString &s = item.PathParts.Back();
885 colon = FindAltStreamColon_in_Path(s);
886 if (colon >= 0)
887 {
888 item.AltStreamName = s.Ptr(colon + 1);
889 item.MainIsDir = (colon == 0 || IsPathSepar(s[(unsigned)colon - 1]));
890 item.IsAltStream = true;
891 s.DeleteFrom(colon);
892 }
893 }
894 if (colon == 0)
895 item.PathParts.DeleteBack();
896 }
897 #endif
898
899 }
900 else
901 #endif
902 SplitPathToParts(
903 #ifdef SUPPORT_ALT_STREAMS
904 item.MainPath
905 #else
906 item.Path
907 #endif
908 , item.PathParts);
909
910 return S_OK;
911 }
912
913 #ifndef _SFX
914
Archive_GetItem_Size(IInArchive * archive,UInt32 index,UInt64 & size,bool & defined)915 static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined)
916 {
917 NCOM::CPropVariant prop;
918 defined = false;
919 size = 0;
920 RINOK(archive->GetProperty(index, kpidSize, &prop));
921 switch (prop.vt)
922 {
923 case VT_UI1: size = prop.bVal; break;
924 case VT_UI2: size = prop.uiVal; break;
925 case VT_UI4: size = prop.ulVal; break;
926 case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break;
927 case VT_EMPTY: return S_OK;
928 default: return E_FAIL;
929 }
930 defined = true;
931 return S_OK;
932 }
933
934 #endif
935
GetItemSize(UInt32 index,UInt64 & size,bool & defined) const936 HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const
937 {
938 NCOM::CPropVariant prop;
939 defined = false;
940 size = 0;
941 RINOK(Archive->GetProperty(index, kpidSize, &prop));
942 switch (prop.vt)
943 {
944 case VT_UI1: size = prop.bVal; break;
945 case VT_UI2: size = prop.uiVal; break;
946 case VT_UI4: size = prop.ulVal; break;
947 case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break;
948 case VT_EMPTY: return S_OK;
949 default: return E_FAIL;
950 }
951 defined = true;
952 return S_OK;
953 }
954
GetItemMTime(UInt32 index,FILETIME & ft,bool & defined) const955 HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const
956 {
957 NCOM::CPropVariant prop;
958 defined = false;
959 ft.dwHighDateTime = ft.dwLowDateTime = 0;
960 RINOK(Archive->GetProperty(index, kpidMTime, &prop));
961 if (prop.vt == VT_FILETIME)
962 {
963 ft = prop.filetime;
964 defined = true;
965 }
966 else if (prop.vt != VT_EMPTY)
967 return E_FAIL;
968 else if (MTimeDefined)
969 {
970 ft = MTime;
971 defined = true;
972 }
973 return S_OK;
974 }
975
976 #ifndef _SFX
977
TestSignature(const Byte * p1,const Byte * p2,size_t size)978 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size)
979 {
980 for (size_t i = 0; i < size; i++)
981 if (p1[i] != p2[i])
982 return false;
983 return true;
984 }
985
MakeCheckOrder(CCodecs * codecs,CIntVector & orderIndices,unsigned numTypes,CIntVector & orderIndices2,const Byte * data,size_t dataSize)986 static void MakeCheckOrder(CCodecs *codecs,
987 CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2,
988 const Byte *data, size_t dataSize)
989 {
990 for (unsigned i = 0; i < numTypes; i++)
991 {
992 int index = orderIndices[i];
993 if (index < 0)
994 continue;
995 const CArcInfoEx &ai = codecs->Formats[index];
996 if (ai.SignatureOffset != 0)
997 {
998 orderIndices2.Add(index);
999 orderIndices[i] = -1;
1000 continue;
1001 }
1002
1003 const CObjectVector<CByteBuffer> &sigs = ai.Signatures;
1004 FOR_VECTOR (k, sigs)
1005 {
1006 const CByteBuffer &sig = sigs[k];
1007 if (sig.Size() == 0 && dataSize == 0 ||
1008 sig.Size() != 0 && sig.Size() <= dataSize &&
1009 TestSignature(data, sig, sig.Size()))
1010 {
1011 orderIndices2.Add(index);
1012 orderIndices[i] = -1;
1013 break;
1014 }
1015 }
1016 }
1017 }
1018
1019 #endif
1020
1021 #ifdef UNDER_CE
1022 static const unsigned kNumHashBytes = 1;
1023 #define HASH_VAL(buf, pos) ((buf)[pos])
1024 #else
1025 static const unsigned kNumHashBytes = 2;
1026 #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8))
1027 #endif
1028
1029
1030 #ifndef _SFX
1031
IsExeExt(const UString & ext)1032 static bool IsExeExt(const UString &ext)
1033 {
1034 return ext.IsEqualTo_Ascii_NoCase("exe");
1035 }
1036
1037 static const char * const k_PreArcFormats[] =
1038 {
1039 "pe"
1040 , "elf"
1041 , "macho"
1042 , "mub"
1043 , "te"
1044 };
1045
IsNameFromList(const UString & s,const char * const names[],size_t num)1046 static bool IsNameFromList(const UString &s, const char * const names[], size_t num)
1047 {
1048 for (unsigned i = 0; i < num; i++)
1049 if (StringsAreEqualNoCase_Ascii(s, names[i]))
1050 return true;
1051 return false;
1052 }
1053
1054
IsPreArcFormat(const CArcInfoEx & ai)1055 static bool IsPreArcFormat(const CArcInfoEx &ai)
1056 {
1057 if (ai.Flags_PreArc())
1058 return true;
1059 return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats));
1060 }
1061
1062 static const char * const k_Formats_with_simple_signuature[] =
1063 {
1064 "7z"
1065 , "xz"
1066 , "rar"
1067 , "bzip2"
1068 , "gzip"
1069 , "cab"
1070 , "wim"
1071 , "rpm"
1072 , "vhd"
1073 , "xar"
1074 };
1075
IsNewStyleSignature(const CArcInfoEx & ai)1076 static bool IsNewStyleSignature(const CArcInfoEx &ai)
1077 {
1078 // if (ai.Version >= 0x91F)
1079 if (ai.NewInterface)
1080 return true;
1081 return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature));
1082 }
1083
1084 class CArchiveOpenCallback_Offset:
1085 public IArchiveOpenCallback,
1086 public IArchiveOpenVolumeCallback,
1087 #ifndef _NO_CRYPTO
1088 public ICryptoGetTextPassword,
1089 #endif
1090 public CMyUnknownImp
1091 {
1092 public:
1093 CMyComPtr<IArchiveOpenCallback> Callback;
1094 CMyComPtr<IArchiveOpenVolumeCallback> OpenVolumeCallback;
1095 UInt64 Files;
1096 UInt64 Offset;
1097
1098 #ifndef _NO_CRYPTO
1099 CMyComPtr<ICryptoGetTextPassword> GetTextPassword;
1100 #endif
1101
1102 MY_QUERYINTERFACE_BEGIN2(IArchiveOpenCallback)
1103 MY_QUERYINTERFACE_ENTRY(IArchiveOpenVolumeCallback)
1104 #ifndef _NO_CRYPTO
1105 MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword)
1106 #endif
1107 MY_QUERYINTERFACE_END
1108 MY_ADDREF_RELEASE
1109
1110 INTERFACE_IArchiveOpenCallback(;)
1111 INTERFACE_IArchiveOpenVolumeCallback(;)
1112 #ifndef _NO_CRYPTO
1113 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
1114 #endif
1115 };
1116
1117 #ifndef _NO_CRYPTO
CryptoGetTextPassword(BSTR * password)1118 STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password)
1119 {
1120 COM_TRY_BEGIN
1121 if (GetTextPassword)
1122 return GetTextPassword->CryptoGetTextPassword(password);
1123 return E_NOTIMPL;
1124 COM_TRY_END
1125 }
1126 #endif
1127
SetTotal(const UInt64 *,const UInt64 *)1128 STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 *, const UInt64 *)
1129 {
1130 return S_OK;
1131 }
1132
SetCompleted(const UInt64 *,const UInt64 * bytes)1133 STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 *, const UInt64 *bytes)
1134 {
1135 if (!Callback)
1136 return S_OK;
1137 UInt64 value = Offset;
1138 if (bytes)
1139 value += *bytes;
1140 return Callback->SetCompleted(&Files, &value);
1141 }
1142
GetProperty(PROPID propID,PROPVARIANT * value)1143 STDMETHODIMP CArchiveOpenCallback_Offset::GetProperty(PROPID propID, PROPVARIANT *value)
1144 {
1145 if (OpenVolumeCallback)
1146 return OpenVolumeCallback->GetProperty(propID, value);
1147 NCOM::PropVariant_Clear(value);
1148 return S_OK;
1149 // return E_NOTIMPL;
1150 }
1151
GetStream(const wchar_t * name,IInStream ** inStream)1152 STDMETHODIMP CArchiveOpenCallback_Offset::GetStream(const wchar_t *name, IInStream **inStream)
1153 {
1154 if (OpenVolumeCallback)
1155 return OpenVolumeCallback->GetStream(name, inStream);
1156 return S_FALSE;
1157 }
1158
1159 #endif
1160
1161
GetOpenArcErrorFlags(const NCOM::CPropVariant & prop,bool * isDefinedProp)1162 UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp)
1163 {
1164 if (isDefinedProp != NULL)
1165 *isDefinedProp = false;
1166
1167 switch (prop.vt)
1168 {
1169 case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart;
1170 case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal;
1171 case VT_EMPTY: return 0;
1172 default: throw 151199;
1173 }
1174 }
1175
ClearErrors()1176 void CArcErrorInfo::ClearErrors()
1177 {
1178 // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!!
1179
1180 ThereIsTail = false;
1181 UnexpecedEnd = false;
1182 IgnoreTail = false;
1183 // NonZerosTail = false;
1184 ErrorFlags_Defined = false;
1185 ErrorFlags = 0;
1186 WarningFlags = 0;
1187 TailSize = 0;
1188
1189 ErrorMessage.Empty();
1190 WarningMessage.Empty();
1191 }
1192
ReadBasicProps(IInArchive * archive,UInt64 startPos,HRESULT openRes)1193 HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes)
1194 {
1195 // OkPhySize_Defined = false;
1196 PhySizeDefined = false;
1197 PhySize = 0;
1198 Offset = 0;
1199 AvailPhySize = FileSize - startPos;
1200
1201 ErrorInfo.ClearErrors();
1202 {
1203 NCOM::CPropVariant prop;
1204 RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop));
1205 ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined);
1206 }
1207 {
1208 NCOM::CPropVariant prop;
1209 RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop));
1210 ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop);
1211 }
1212
1213 {
1214 NCOM::CPropVariant prop;
1215 RINOK(archive->GetArchiveProperty(kpidError, &prop));
1216 if (prop.vt != VT_EMPTY)
1217 ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown error");
1218 }
1219
1220 {
1221 NCOM::CPropVariant prop;
1222 RINOK(archive->GetArchiveProperty(kpidWarning, &prop));
1223 if (prop.vt != VT_EMPTY)
1224 ErrorInfo.WarningMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown warning");
1225 }
1226
1227 if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen())
1228 {
1229 RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined));
1230 /*
1231 RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined));
1232 if (!OkPhySize_Defined)
1233 {
1234 OkPhySize_Defined = PhySizeDefined;
1235 OkPhySize = PhySize;
1236 }
1237 */
1238
1239 bool offsetDefined;
1240 RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined));
1241
1242 Int64 globalOffset = startPos + Offset;
1243 AvailPhySize = FileSize - globalOffset;
1244 if (PhySizeDefined)
1245 {
1246 UInt64 endPos = globalOffset + PhySize;
1247 if (endPos < FileSize)
1248 {
1249 AvailPhySize = PhySize;
1250 ErrorInfo.ThereIsTail = true;
1251 ErrorInfo.TailSize = FileSize - endPos;
1252 }
1253 else if (endPos > FileSize)
1254 ErrorInfo.UnexpecedEnd = true;
1255 }
1256 }
1257
1258 return S_OK;
1259 }
1260
1261 /*
1262 static PrintNumber(const char *s, int n)
1263 {
1264 char temp[100];
1265 sprintf(temp, "%s %d", s, n);
1266 OutputDebugStringA(temp);
1267 }
1268 */
1269
PrepareToOpen(const COpenOptions & op,unsigned formatIndex,CMyComPtr<IInArchive> & archive)1270 HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive)
1271 {
1272 // OutputDebugStringA("a1");
1273 // PrintNumber("formatIndex", formatIndex);
1274
1275 RINOK(op.codecs->CreateInArchive(formatIndex, archive));
1276 // OutputDebugStringA("a2");
1277 if (!archive)
1278 return S_OK;
1279
1280 #ifdef EXTERNAL_CODECS
1281 if (op.codecs->NeedSetLibCodecs)
1282 {
1283 const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
1284 if (ai.LibIndex >= 0 ?
1285 !op.codecs->Libs[ai.LibIndex].SetCodecs :
1286 !op.codecs->Libs.IsEmpty())
1287 {
1288 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
1289 archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
1290 if (setCompressCodecsInfo)
1291 {
1292 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs));
1293 }
1294 }
1295 }
1296 #endif
1297
1298
1299 #ifndef _SFX
1300
1301 const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
1302
1303 // OutputDebugStringW(ai.Name);
1304 // OutputDebugStringA("a3");
1305
1306 if (ai.Flags_PreArc())
1307 {
1308 /* we notify parsers that extract executables, that they don't need
1309 to open archive, if there is tail after executable (for SFX cases) */
1310 CMyComPtr<IArchiveAllowTail> allowTail;
1311 archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail);
1312 if (allowTail)
1313 allowTail->AllowTail(BoolToInt(true));
1314 }
1315
1316 if (op.props)
1317 {
1318 /*
1319 FOR_VECTOR (y, op.props)
1320 {
1321 const COptionalOpenProperties &optProps = (*op.props)[y];
1322 if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0)
1323 {
1324 RINOK(SetProperties(archive, optProps.Props));
1325 break;
1326 }
1327 }
1328 */
1329 RINOK(SetProperties(archive, *op.props));
1330 }
1331
1332 #endif
1333 return S_OK;
1334 }
1335
1336 #ifndef _SFX
1337
ReadParseItemProps(IInArchive * archive,const CArcInfoEx & ai,NArchive::NParser::CParseItem & pi)1338 static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi)
1339 {
1340 pi.Extension = ai.GetMainExt();
1341 pi.FileTime_Defined = false;
1342 pi.ArcType = ai.Name;
1343
1344 RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType));
1345
1346 // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe));
1347 pi.IsSelfExe = ai.Flags_PreArc();
1348
1349 {
1350 NCOM::CPropVariant prop;
1351 RINOK(archive->GetArchiveProperty(kpidMTime, &prop));
1352 if (prop.vt == VT_FILETIME)
1353 {
1354 pi.FileTime_Defined = true;
1355 pi.FileTime = prop.filetime;
1356 }
1357 }
1358
1359 if (!pi.FileTime_Defined)
1360 {
1361 NCOM::CPropVariant prop;
1362 RINOK(archive->GetArchiveProperty(kpidCTime, &prop));
1363 if (prop.vt == VT_FILETIME)
1364 {
1365 pi.FileTime_Defined = true;
1366 pi.FileTime = prop.filetime;
1367 }
1368 }
1369
1370 {
1371 NCOM::CPropVariant prop;
1372 RINOK(archive->GetArchiveProperty(kpidName, &prop));
1373 if (prop.vt == VT_BSTR)
1374 {
1375 pi.Name.SetFromBstr(prop.bstrVal);
1376 pi.Extension.Empty();
1377 }
1378 else
1379 {
1380 RINOK(archive->GetArchiveProperty(kpidExtension, &prop));
1381 if (prop.vt == VT_BSTR)
1382 pi.Extension.SetFromBstr(prop.bstrVal);
1383 }
1384 }
1385
1386 {
1387 NCOM::CPropVariant prop;
1388 RINOK(archive->GetArchiveProperty(kpidShortComment, &prop));
1389 if (prop.vt == VT_BSTR)
1390 pi.Comment.SetFromBstr(prop.bstrVal);
1391 }
1392
1393
1394 UInt32 numItems;
1395 RINOK(archive->GetNumberOfItems(&numItems));
1396
1397 // pi.NumSubFiles = numItems;
1398 // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined));
1399 // if (!pi.UnpackSize_Defined)
1400 {
1401 pi.NumSubFiles = 0;
1402 pi.NumSubDirs = 0;
1403 pi.UnpackSize = 0;
1404 for (UInt32 i = 0; i < numItems; i++)
1405 {
1406 UInt64 size = 0;
1407 bool defined = false;
1408 Archive_GetItem_Size(archive, i, size, defined);
1409 if (defined)
1410 {
1411 pi.UnpackSize_Defined = true;
1412 pi.UnpackSize += size;
1413 }
1414
1415 bool isDir = false;
1416 Archive_IsItem_Dir(archive, i, isDir);
1417 if (isDir)
1418 pi.NumSubDirs++;
1419 else
1420 pi.NumSubFiles++;
1421 }
1422 if (pi.NumSubDirs != 0)
1423 pi.NumSubDirs_Defined = true;
1424 pi.NumSubFiles_Defined = true;
1425 }
1426
1427 return S_OK;
1428 }
1429
1430 #endif
1431
CheckZerosTail(const COpenOptions & op,UInt64 offset)1432 HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset)
1433 {
1434 if (!op.stream)
1435 return S_OK;
1436 RINOK(op.stream->Seek(offset, STREAM_SEEK_SET, NULL));
1437 const UInt32 kBufSize = 1 << 11;
1438 Byte buf[kBufSize];
1439
1440 for (;;)
1441 {
1442 UInt32 processed = 0;
1443 RINOK(op.stream->Read(buf, kBufSize, &processed));
1444 if (processed == 0)
1445 {
1446 // ErrorInfo.NonZerosTail = false;
1447 ErrorInfo.IgnoreTail = true;
1448 return S_OK;
1449 }
1450 for (size_t i = 0; i < processed; i++)
1451 {
1452 if (buf[i] != 0)
1453 {
1454 // ErrorInfo.IgnoreTail = false;
1455 // ErrorInfo.NonZerosTail = true;
1456 return S_OK;
1457 }
1458 }
1459 }
1460 }
1461
1462 #ifndef _SFX
1463
1464 class CExtractCallback_To_OpenCallback:
1465 public IArchiveExtractCallback,
1466 public ICompressProgressInfo,
1467 public CMyUnknownImp
1468 {
1469 public:
1470 CMyComPtr<IArchiveOpenCallback> Callback;
1471 UInt64 Files;
1472 UInt64 Offset;
1473
1474 MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo)
1475 INTERFACE_IArchiveExtractCallback(;)
1476 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
Init(IArchiveOpenCallback * callback)1477 void Init(IArchiveOpenCallback *callback)
1478 {
1479 Callback = callback;
1480 Files = 0;
1481 Offset = 0;
1482 }
1483 };
1484
SetTotal(UInt64)1485 STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */)
1486 {
1487 return S_OK;
1488 }
1489
SetCompleted(const UInt64 *)1490 STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */)
1491 {
1492 return S_OK;
1493 }
1494
SetRatioInfo(const UInt64 * inSize,const UInt64 *)1495 STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */)
1496 {
1497 if (Callback)
1498 {
1499 UInt64 value = Offset;
1500 if (inSize)
1501 value += *inSize;
1502 return Callback->SetCompleted(&Files, &value);
1503 }
1504 return S_OK;
1505 }
1506
GetStream(UInt32,ISequentialOutStream ** outStream,Int32)1507 STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */)
1508 {
1509 *outStream = 0;
1510 return S_OK;
1511 }
1512
PrepareOperation(Int32)1513 STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */)
1514 {
1515 return S_OK;
1516 }
1517
SetOperationResult(Int32)1518 STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */)
1519 {
1520 return S_OK;
1521 }
1522
OpenArchiveSpec(IInArchive * archive,bool needPhySize,IInStream * stream,const UInt64 * maxCheckStartPosition,IArchiveOpenCallback * openCallback,IArchiveExtractCallback * extractCallback)1523 static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize,
1524 IInStream *stream, const UInt64 *maxCheckStartPosition,
1525 IArchiveOpenCallback *openCallback,
1526 IArchiveExtractCallback *extractCallback)
1527 {
1528 /*
1529 if (needPhySize)
1530 {
1531 CMyComPtr<IArchiveOpen2> open2;
1532 archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2);
1533 if (open2)
1534 return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback);
1535 }
1536 */
1537 RINOK(archive->Open(stream, maxCheckStartPosition, openCallback));
1538 if (needPhySize)
1539 {
1540 bool phySize_Defined = false;
1541 UInt64 phySize = 0;
1542 RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined));
1543 if (phySize_Defined)
1544 return S_OK;
1545
1546 bool phySizeCantBeDetected = false;;
1547 RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected));
1548
1549 if (!phySizeCantBeDetected)
1550 {
1551 RINOK(archive->Extract(0, (UInt32)(Int32)-1, BoolToInt(true), extractCallback));
1552 }
1553 }
1554 return S_OK;
1555 }
1556
FindFormatForArchiveType(CCodecs * codecs,CIntVector orderIndices,const char * name)1557 static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name)
1558 {
1559 FOR_VECTOR (i, orderIndices)
1560 if (StringsAreEqualNoCase_Ascii(codecs->Formats[orderIndices[i]].Name, name))
1561 return i;
1562 return -1;
1563 }
1564
1565 #endif
1566
OpenStream2(const COpenOptions & op)1567 HRESULT CArc::OpenStream2(const COpenOptions &op)
1568 {
1569 // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout);
1570
1571 Archive.Release();
1572 GetRawProps.Release();
1573 GetRootProps.Release();
1574
1575 ErrorInfo.ClearErrors();
1576 ErrorInfo.ErrorFormatIndex = -1;
1577
1578 IsParseArc = false;
1579 ArcStreamOffset = 0;
1580
1581 // OutputDebugStringA("1");
1582 // OutputDebugStringW(Path);
1583
1584 const UString fileName = ExtractFileNameFromPath(Path);
1585 UString extension;
1586 {
1587 int dotPos = fileName.ReverseFind_Dot();
1588 if (dotPos >= 0)
1589 extension = fileName.Ptr(dotPos + 1);
1590 }
1591
1592 CIntVector orderIndices;
1593
1594 bool searchMarkerInHandler = false;
1595 #ifdef _SFX
1596 searchMarkerInHandler = true;
1597 #endif
1598
1599 CBoolArr isMainFormatArr(op.codecs->Formats.Size());
1600 {
1601 FOR_VECTOR(i, op.codecs->Formats)
1602 isMainFormatArr[i] = false;
1603 }
1604
1605 UInt64 maxStartOffset =
1606 op.openType.MaxStartOffset_Defined ?
1607 op.openType.MaxStartOffset :
1608 kMaxCheckStartPosition;
1609
1610 #ifndef _SFX
1611 bool isUnknownExt = false;
1612 #endif
1613
1614 bool isForced = false;
1615 unsigned numMainTypes = 0;
1616 int formatIndex = op.openType.FormatIndex;
1617
1618 if (formatIndex >= 0)
1619 {
1620 isForced = true;
1621 orderIndices.Add(formatIndex);
1622 numMainTypes = 1;
1623 isMainFormatArr[(unsigned)formatIndex] = true;
1624
1625 searchMarkerInHandler = true;
1626 }
1627 else
1628 {
1629 unsigned numFinded = 0;
1630 #ifndef _SFX
1631 bool isPrearcExt = false;
1632 #endif
1633
1634 {
1635 #ifndef _SFX
1636
1637 bool isZip = false;
1638 bool isRar = false;
1639
1640 const wchar_t c = extension[0];
1641 if (c == 'z' || c == 'Z' || c == 'r' || c == 'R')
1642 {
1643 bool isNumber = false;
1644 for (unsigned k = 1;; k++)
1645 {
1646 const wchar_t d = extension[k];
1647 if (d == 0)
1648 break;
1649 if (d < '0' || d > '9')
1650 {
1651 isNumber = false;
1652 break;
1653 }
1654 isNumber = true;
1655 }
1656 if (isNumber)
1657 if (c == 'z' || c == 'Z')
1658 isZip = true;
1659 else
1660 isRar = true;
1661 }
1662
1663 #endif
1664
1665 FOR_VECTOR (i, op.codecs->Formats)
1666 {
1667 const CArcInfoEx &ai = op.codecs->Formats[i];
1668
1669 if (IgnoreSplit || !op.openType.CanReturnArc)
1670 if (ai.IsSplit())
1671 continue;
1672 if (op.excludedFormats->FindInSorted(i) >= 0)
1673 continue;
1674
1675 #ifndef _SFX
1676 if (IsPreArcFormat(ai))
1677 isPrearcExt = true;
1678 #endif
1679
1680 if (ai.FindExtension(extension) >= 0
1681 #ifndef _SFX
1682 || isZip && StringsAreEqualNoCase_Ascii(ai.Name, "zip")
1683 || isRar && StringsAreEqualNoCase_Ascii(ai.Name, "rar")
1684 #endif
1685 )
1686 {
1687 // PrintNumber("orderIndices.Insert", i);
1688 orderIndices.Insert(numFinded++, i);
1689 isMainFormatArr[i] = true;
1690 }
1691 else
1692 orderIndices.Add(i);
1693 }
1694 }
1695
1696 if (!op.stream)
1697 {
1698 if (numFinded != 1)
1699 return E_NOTIMPL;
1700 orderIndices.DeleteFrom(1);
1701 }
1702 // PrintNumber("numFinded", numFinded );
1703
1704 /*
1705 if (op.openOnlySpecifiedByExtension)
1706 {
1707 if (numFinded != 0 && !IsExeExt(extension))
1708 orderIndices.DeleteFrom(numFinded);
1709 }
1710 */
1711
1712 #ifndef _SFX
1713
1714 if (op.stream && orderIndices.Size() >= 2)
1715 {
1716 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
1717 CByteBuffer byteBuffer;
1718 CIntVector orderIndices2;
1719 if (numFinded == 0 || IsExeExt(extension))
1720 {
1721 // signature search was here
1722 }
1723 else if (extension.IsEqualTo("000") || extension.IsEqualTo("001"))
1724 {
1725 int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar");
1726 if (i >= 0)
1727 {
1728 const size_t kBufSize = (1 << 10);
1729 byteBuffer.Alloc(kBufSize);
1730 size_t processedSize = kBufSize;
1731 RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
1732 if (processedSize >= 16)
1733 {
1734 const Byte *buf = byteBuffer;
1735 const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
1736 if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0)
1737 {
1738 orderIndices2.Add(orderIndices[i]);
1739 orderIndices[i] = -1;
1740 if (i >= (int)numFinded)
1741 numFinded++;
1742 }
1743 }
1744 }
1745 }
1746 else
1747 {
1748 const size_t kBufSize = (1 << 10);
1749 byteBuffer.Alloc(kBufSize);
1750 size_t processedSize = kBufSize;
1751 RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
1752 if (processedSize == 0)
1753 return S_FALSE;
1754
1755 /*
1756 check type order:
1757 1) matched extension, no signuature
1758 2) matched extension, matched signuature
1759 // 3) no signuature
1760 // 4) matched signuature
1761 */
1762
1763 MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0);
1764 MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize);
1765 // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0);
1766 // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize);
1767 }
1768
1769 FOR_VECTOR (i, orderIndices)
1770 {
1771 int val = orderIndices[i];
1772 if (val != -1)
1773 orderIndices2.Add(val);
1774 }
1775 orderIndices = orderIndices2;
1776 }
1777
1778 if (orderIndices.Size() >= 2)
1779 {
1780 int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso");
1781 int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf");
1782 if (iUdf > iIso && iIso >= 0)
1783 {
1784 int isoIndex = orderIndices[iIso];
1785 int udfIndex = orderIndices[iUdf];
1786 orderIndices[iUdf] = isoIndex;
1787 orderIndices[iIso] = udfIndex;
1788 }
1789 }
1790
1791 numMainTypes = numFinded;
1792 isUnknownExt = (numMainTypes == 0) || isPrearcExt;
1793
1794 #else // _SFX
1795
1796 numMainTypes = orderIndices.Size();
1797
1798 // we need correct numMainTypes for mutlivolume SFX (if some volume is missing)
1799 if (numFinded != 0)
1800 numMainTypes = numFinded;
1801
1802 #endif
1803 }
1804
1805 UInt64 fileSize = 0;
1806 if (op.stream)
1807 {
1808 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
1809 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
1810 }
1811 FileSize = fileSize;
1812
1813
1814 #ifndef _SFX
1815
1816 CBoolArr skipFrontalFormat(op.codecs->Formats.Size());
1817 {
1818 FOR_VECTOR(i, op.codecs->Formats)
1819 skipFrontalFormat[i] = false;
1820 }
1821
1822 #endif
1823
1824 const COpenType &mode = op.openType;
1825
1826
1827
1828
1829
1830 if (mode.CanReturnArc)
1831 {
1832 // ---------- OPEN main type by extenssion ----------
1833
1834 unsigned numCheckTypes = orderIndices.Size();
1835 if (formatIndex >= 0)
1836 numCheckTypes = numMainTypes;
1837
1838 for (unsigned i = 0; i < numCheckTypes; i++)
1839 {
1840 FormatIndex = orderIndices[i];
1841
1842 bool exactOnly = false;
1843
1844 #ifndef _SFX
1845
1846 const CArcInfoEx &ai = op.codecs->Formats[FormatIndex];
1847 // OutputDebugStringW(ai.Name);
1848 if (i >= numMainTypes)
1849 {
1850 if (!ai.Flags_BackwardOpen()
1851 // && !ai.Flags_PureStartOpen()
1852 )
1853 continue;
1854 exactOnly = true;
1855 }
1856
1857 #endif
1858
1859 // Some handlers do not set total bytes. So we set it here
1860 if (op.callback)
1861 RINOK(op.callback->SetTotal(NULL, &fileSize));
1862
1863 if (op.stream)
1864 {
1865 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
1866 }
1867
1868 CMyComPtr<IInArchive> archive;
1869
1870 RINOK(PrepareToOpen(op, FormatIndex, archive));
1871 if (!archive)
1872 continue;
1873
1874 HRESULT result;
1875 if (op.stream)
1876 {
1877 UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0;
1878 result = archive->Open(op.stream, &searchLimit, op.callback);
1879 }
1880 else
1881 {
1882 CMyComPtr<IArchiveOpenSeq> openSeq;
1883 archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq);
1884 if (!openSeq)
1885 return E_NOTIMPL;
1886 result = openSeq->OpenSeq(op.seqStream);
1887 }
1888
1889 RINOK(ReadBasicProps(archive, 0, result));
1890
1891 if (result == S_FALSE)
1892 {
1893 bool isArc = ErrorInfo.IsArc_After_NonOpen();
1894
1895 #ifndef _SFX
1896 // if it's archive, we allow another open attempt for parser
1897 if (!mode.CanReturnParser || !isArc)
1898 skipFrontalFormat[(unsigned)FormatIndex] = true;
1899 #endif
1900
1901 if (exactOnly)
1902 continue;
1903
1904 if (i == 0 && numMainTypes == 1)
1905 {
1906 // we set NonOpenErrorInfo, only if there is only one main format (defined by extension).
1907 ErrorInfo.ErrorFormatIndex = FormatIndex;
1908 NonOpen_ErrorInfo = ErrorInfo;
1909
1910 if (!mode.CanReturnParser && isArc)
1911 {
1912 // if (formatIndex < 0 && !searchMarkerInHandler)
1913 {
1914 // if bad archive was detected, we don't need additional open attempts
1915 #ifndef _SFX
1916 if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */)
1917 #endif
1918 return S_FALSE;
1919 }
1920 }
1921 }
1922
1923 /*
1924 #ifndef _SFX
1925 if (IsExeExt(extension) || ai.Flags_PreArc())
1926 {
1927 // openOnlyFullArc = false;
1928 // canReturnTailArc = true;
1929 // limitSignatureSearch = true;
1930 }
1931 #endif
1932 */
1933
1934 continue;
1935 }
1936
1937 RINOK(result);
1938
1939 #ifndef _SFX
1940
1941 bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
1942 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
1943
1944 bool thereIsTail = ErrorInfo.ThereIsTail;
1945 if (thereIsTail && mode.ZerosTailIsAllowed)
1946 {
1947 RINOK(CheckZerosTail(op, Offset + PhySize));
1948 if (ErrorInfo.IgnoreTail)
1949 thereIsTail = false;
1950 }
1951
1952 if (Offset > 0)
1953 {
1954 if (exactOnly
1955 || !searchMarkerInHandler
1956 || !specFlags.CanReturn_NonStart()
1957 || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset))
1958 continue;
1959 }
1960 if (thereIsTail)
1961 {
1962 if (Offset > 0)
1963 {
1964 if (!specFlags.CanReturnMid)
1965 continue;
1966 }
1967 else if (!specFlags.CanReturnFrontal)
1968 continue;
1969 }
1970
1971 if (Offset > 0 || thereIsTail)
1972 {
1973 if (formatIndex < 0)
1974 {
1975 if (IsPreArcFormat(ai))
1976 {
1977 // openOnlyFullArc = false;
1978 // canReturnTailArc = true;
1979 /*
1980 if (mode.SkipSfxStub)
1981 limitSignatureSearch = true;
1982 */
1983 // if (mode.SkipSfxStub)
1984 {
1985 // skipFrontalFormat[FormatIndex] = true;
1986 continue;
1987 }
1988 }
1989 }
1990 }
1991
1992 #endif
1993
1994 Archive = archive;
1995 return S_OK;
1996 }
1997 }
1998
1999
2000
2001 #ifndef _SFX
2002
2003 if (!op.stream)
2004 return S_FALSE;
2005
2006 if (formatIndex >= 0 && !mode.CanReturnParser)
2007 {
2008 if (mode.MaxStartOffset_Defined)
2009 {
2010 if (mode.MaxStartOffset == 0)
2011 return S_FALSE;
2012 }
2013 else
2014 {
2015 const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
2016 if (ai.FindExtension(extension) >= 0)
2017 {
2018 if (ai.Flags_FindSignature() && searchMarkerInHandler)
2019 return S_FALSE;
2020 }
2021 }
2022 }
2023
2024 NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler;
2025 CMyComPtr<IInArchive> handler = handlerSpec;
2026
2027 CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback;
2028 CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec;
2029 extractCallback_To_OpenCallback_Spec->Init(op.callback);
2030
2031 {
2032 // ---------- Check all possible START archives ----------
2033 // this code is better for full file archives than Parser's code.
2034
2035 CByteBuffer byteBuffer;
2036 bool endOfFile = false;
2037 size_t processedSize;
2038 {
2039 size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF)
2040 if (bufSize > fileSize)
2041 {
2042 bufSize = (size_t)fileSize;
2043 endOfFile = true;
2044 }
2045 byteBuffer.Alloc(bufSize);
2046 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
2047 processedSize = bufSize;
2048 RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
2049 if (processedSize == 0)
2050 return S_FALSE;
2051 if (processedSize < bufSize)
2052 endOfFile = true;
2053 }
2054 CUIntVector sortedFormats;
2055
2056 unsigned i;
2057
2058 int splitIndex = -1;
2059
2060 for (i = 0; i < orderIndices.Size(); i++)
2061 {
2062 unsigned form = orderIndices[i];
2063 if (skipFrontalFormat[form])
2064 continue;
2065 const CArcInfoEx &ai = op.codecs->Formats[form];
2066 if (ai.IsSplit())
2067 {
2068 splitIndex = form;
2069 continue;
2070 }
2071
2072 if (ai.IsArcFunc)
2073 {
2074 UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize);
2075 if (isArcRes == k_IsArc_Res_NO)
2076 continue;
2077 if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
2078 continue;
2079 // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue;
2080 sortedFormats.Insert(0, form);
2081 continue;
2082 }
2083
2084 bool isNewStyleSignature = IsNewStyleSignature(ai);
2085 bool needCheck = !isNewStyleSignature
2086 || ai.Signatures.IsEmpty()
2087 || ai.Flags_PureStartOpen()
2088 || ai.Flags_StartOpen()
2089 || ai.Flags_BackwardOpen();
2090
2091 if (isNewStyleSignature && !ai.Signatures.IsEmpty())
2092 {
2093 unsigned k;
2094 for (k = 0; k < ai.Signatures.Size(); k++)
2095 {
2096 const CByteBuffer &sig = ai.Signatures[k];
2097 UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size();
2098 if (processedSize < signatureEnd)
2099 {
2100 if (!endOfFile)
2101 needCheck = true;
2102 }
2103 else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0)
2104 break;
2105 }
2106 if (k != ai.Signatures.Size())
2107 {
2108 sortedFormats.Insert(0, form);
2109 continue;
2110 }
2111 }
2112 if (needCheck)
2113 sortedFormats.Add(form);
2114 }
2115
2116 if (splitIndex >= 0)
2117 sortedFormats.Insert(0, splitIndex);
2118
2119 for (i = 0; i < sortedFormats.Size(); i++)
2120 {
2121 FormatIndex = sortedFormats[i];
2122 const CArcInfoEx &ai = op.codecs->Formats[FormatIndex];
2123
2124 if (op.callback)
2125 RINOK(op.callback->SetTotal(NULL, &fileSize));
2126
2127 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
2128
2129 CMyComPtr<IInArchive> archive;
2130 RINOK(PrepareToOpen(op, FormatIndex, archive));
2131 if (!archive)
2132 continue;
2133
2134 PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name));
2135 HRESULT result;
2136 {
2137 UInt64 searchLimit = 0;
2138 /*
2139 if (mode.CanReturnArc)
2140 result = archive->Open(op.stream, &searchLimit, op.callback);
2141 else
2142 */
2143 result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback);
2144 }
2145
2146 if (result == S_FALSE)
2147 {
2148 skipFrontalFormat[(unsigned)FormatIndex] = true;
2149 // FIXME: maybe we must use LenIsUnknown.
2150 // printf(" OpenForSize Error");
2151 continue;
2152 }
2153 RINOK(result);
2154
2155 RINOK(ReadBasicProps(archive, 0, result));
2156
2157 if (Offset > 0)
2158 {
2159 continue; // good handler doesn't return such Offset > 0
2160 // but there are some cases like false prefixed PK00 archive, when
2161 // we can support it?
2162 }
2163
2164 NArchive::NParser::CParseItem pi;
2165 pi.Offset = Offset;
2166 pi.Size = AvailPhySize;
2167
2168 // bool needScan = false;
2169
2170 if (!PhySizeDefined)
2171 {
2172 // it's for Z format
2173 pi.LenIsUnknown = true;
2174 // needScan = true;
2175 // phySize = arcRem;
2176 // nextNeedCheckStartOpen = false;
2177 }
2178
2179 /*
2180 if (OkPhySize_Defined)
2181 pi.OkSize = pi.OkPhySize;
2182 else
2183 pi.OkSize = pi.Size;
2184 */
2185
2186 pi.NormalizeOffset();
2187 // printf(" phySize = %8d", (unsigned)phySize);
2188
2189
2190 if (mode.CanReturnArc)
2191 {
2192 bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
2193 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2194 bool openCur = false;
2195
2196 if (!ErrorInfo.ThereIsTail)
2197 openCur = true;
2198 else
2199 {
2200 if (mode.ZerosTailIsAllowed)
2201 {
2202 RINOK(CheckZerosTail(op, Offset + PhySize));
2203 if (ErrorInfo.IgnoreTail)
2204 openCur = true;
2205 }
2206 if (!openCur)
2207 {
2208 openCur = specFlags.CanReturnFrontal;
2209 if (formatIndex < 0) // format is not forced
2210 {
2211 if (IsPreArcFormat(ai))
2212 {
2213 // if (mode.SkipSfxStub)
2214 {
2215 openCur = false;
2216 }
2217 }
2218 }
2219 }
2220 }
2221
2222 if (openCur)
2223 {
2224 InStream = op.stream;
2225 Archive = archive;
2226 return S_OK;
2227 }
2228 }
2229
2230 skipFrontalFormat[(unsigned)FormatIndex] = true;
2231
2232
2233 // if (!mode.CanReturnArc)
2234 /*
2235 if (!ErrorInfo.ThereIsTail)
2236 continue;
2237 */
2238 if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
2239 continue;
2240
2241 // printf("\nAdd offset = %d", (int)pi.Offset);
2242 RINOK(ReadParseItemProps(archive, ai, pi));
2243 handlerSpec->AddItem(pi);
2244 }
2245 }
2246
2247
2248
2249
2250
2251 // ---------- PARSER ----------
2252
2253 CUIntVector arc2sig; // formatIndex to signatureIndex
2254 CUIntVector sig2arc; // signatureIndex to formatIndex;
2255 {
2256 unsigned sum = 0;
2257 FOR_VECTOR (i, op.codecs->Formats)
2258 {
2259 arc2sig.Add(sum);
2260 const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures;
2261 sum += sigs.Size();
2262 FOR_VECTOR (k, sigs)
2263 sig2arc.Add(i);
2264 }
2265 }
2266
2267 {
2268 const size_t kBeforeSize = 1 << 16;
2269 const size_t kAfterSize = 1 << 20;
2270 const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize
2271
2272 const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8);
2273 CByteArr hashBuffer(kNumVals);
2274 Byte *hash = hashBuffer;
2275 memset(hash, 0xFF, kNumVals);
2276 Byte prevs[256];
2277 memset(prevs, 0xFF, sizeof(prevs));
2278 if (sig2arc.Size() >= 0xFF)
2279 return S_FALSE;
2280
2281 CUIntVector difficultFormats;
2282 CBoolArr difficultBools(256);
2283 {
2284 for (unsigned i = 0; i < 256; i++)
2285 difficultBools[i] = false;
2286 }
2287
2288 bool thereAreHandlersForSearch = false;
2289
2290 // UInt32 maxSignatureEnd = 0;
2291
2292 FOR_VECTOR (i, orderIndices)
2293 {
2294 int index = orderIndices[i];
2295 if (index < 0)
2296 continue;
2297 const CArcInfoEx &ai = op.codecs->Formats[index];
2298 bool isDifficult = false;
2299 // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31)
2300 if (!ai.NewInterface)
2301 isDifficult = true;
2302 else
2303 {
2304 if (ai.Flags_StartOpen())
2305 isDifficult = true;
2306 FOR_VECTOR (k, ai.Signatures)
2307 {
2308 const CByteBuffer &sig = ai.Signatures[k];
2309 /*
2310 UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size();
2311 if (maxSignatureEnd < signatureEnd)
2312 maxSignatureEnd = signatureEnd;
2313 */
2314 if (sig.Size() < kNumHashBytes)
2315 {
2316 isDifficult = true;
2317 continue;
2318 }
2319 thereAreHandlersForSearch = true;
2320 UInt32 v = HASH_VAL(sig, 0);
2321 unsigned sigIndex = arc2sig[(unsigned)index] + k;
2322 prevs[sigIndex] = hash[v];
2323 hash[v] = (Byte)sigIndex;
2324 }
2325 }
2326 if (isDifficult)
2327 {
2328 difficultFormats.Add(index);
2329 difficultBools[index] = true;
2330 }
2331 }
2332
2333 if (!thereAreHandlersForSearch)
2334 {
2335 // openOnlyFullArc = true;
2336 // canReturnTailArc = true;
2337 }
2338
2339 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
2340
2341 CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream;
2342 CMyComPtr<IInStream> limitedStream = limitedStreamSpec;
2343 limitedStreamSpec->SetStream(op.stream);
2344
2345 CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL;
2346 CMyComPtr<IArchiveOpenCallback> openCallback_Offset;
2347 if (op.callback)
2348 {
2349 openCallback_Offset_Spec = new CArchiveOpenCallback_Offset;
2350 openCallback_Offset = openCallback_Offset_Spec;
2351 openCallback_Offset_Spec->Callback = op.callback;
2352 openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback);
2353 #ifndef _NO_CRYPTO
2354 openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword);
2355 #endif
2356 }
2357
2358 if (op.callback)
2359 RINOK(op.callback->SetTotal(NULL, &fileSize));
2360
2361 CByteBuffer &byteBuffer = limitedStreamSpec->Buffer;
2362 byteBuffer.Alloc(kBufSize);
2363
2364 UInt64 callbackPrev = 0;
2365 bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos.
2366
2367 bool endOfFile = false;
2368 UInt64 bufPhyPos = 0;
2369 size_t bytesInBuf = 0;
2370 // UInt64 prevPos = 0;
2371
2372 // ---------- Main Scan Loop ----------
2373
2374 UInt64 pos = 0;
2375
2376 if (!mode.EachPos && handlerSpec->_items.Size() == 1)
2377 {
2378 NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
2379 if (!pi.LenIsUnknown && pi.Offset == 0)
2380 pos = pi.Size;
2381 }
2382
2383 for (;;)
2384 {
2385 // printf("\nPos = %d", (int)pos);
2386 UInt64 posInBuf = pos - bufPhyPos;
2387
2388 // if (pos > ((UInt64)1 << 35)) break;
2389
2390 if (!endOfFile)
2391 {
2392 if (bytesInBuf < kBufSize)
2393 {
2394 size_t processedSize = kBufSize - bytesInBuf;
2395 // printf("\nRead ask = %d", (unsigned)processedSize);
2396 UInt64 seekPos = bufPhyPos + bytesInBuf;
2397 RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL));
2398 RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize));
2399 // printf(" processed = %d", (unsigned)processedSize);
2400 if (processedSize == 0)
2401 {
2402 fileSize = seekPos;
2403 endOfFile = true;
2404 }
2405 else
2406 {
2407 bytesInBuf += processedSize;
2408 limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos);
2409 }
2410 continue;
2411 }
2412
2413 if (bytesInBuf < posInBuf)
2414 {
2415 UInt64 skipSize = posInBuf - bytesInBuf;
2416 if (skipSize <= kBeforeSize)
2417 {
2418 size_t keepSize = (size_t)(kBeforeSize - skipSize);
2419 // printf("\nmemmove skip = %d", (int)keepSize);
2420 memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize);
2421 bytesInBuf = keepSize;
2422 bufPhyPos = pos - keepSize;
2423 continue;
2424 }
2425 // printf("\nSkip %d", (int)(skipSize - kBeforeSize));
2426 // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL));
2427 bytesInBuf = 0;
2428 bufPhyPos = pos - kBeforeSize;
2429 continue;
2430 }
2431
2432 if (bytesInBuf - posInBuf < kAfterSize)
2433 {
2434 size_t beg = (size_t)posInBuf - kBeforeSize;
2435 // printf("\nmemmove for after beg = %d", (int)beg);
2436 memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg);
2437 bufPhyPos += beg;
2438 bytesInBuf -= beg;
2439 continue;
2440 }
2441 }
2442
2443 bool useOffsetCallback = false;
2444 if (openCallback_Offset)
2445 {
2446 openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
2447 openCallback_Offset_Spec->Offset = pos;
2448
2449 useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1);
2450
2451 if (pos >= callbackPrev + (1 << 23))
2452 {
2453 RINOK(openCallback_Offset_Spec->SetCompleted(NULL, NULL));
2454 callbackPrev = pos;
2455 }
2456 }
2457
2458 {
2459 UInt64 endPos = bufPhyPos + bytesInBuf;
2460 if (fileSize < endPos)
2461 {
2462 FileSize = fileSize; // why ????
2463 fileSize = endPos;
2464 }
2465 }
2466
2467 size_t availSize = bytesInBuf - (size_t)posInBuf;
2468 if (availSize < kNumHashBytes)
2469 break;
2470 size_t scanSize = availSize -
2471 ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes);
2472
2473 {
2474 /*
2475 UInt64 scanLimit = openOnlyFullArc ?
2476 maxSignatureEnd :
2477 op.openType.ScanSize + maxSignatureEnd;
2478 */
2479 if (!mode.CanReturnParser)
2480 {
2481 if (pos > maxStartOffset)
2482 break;
2483 UInt64 remScan = maxStartOffset - pos;
2484 if (scanSize > remScan)
2485 scanSize = (size_t)remScan;
2486 }
2487 }
2488
2489 scanSize++;
2490
2491 const Byte *buf = byteBuffer + (size_t)posInBuf;
2492 size_t ppp = 0;
2493
2494 if (!needCheckStartOpen)
2495 {
2496 for (; ppp < scanSize && hash[HASH_VAL(buf, ppp)] == 0xFF; ppp++);
2497 pos += ppp;
2498 if (ppp == scanSize)
2499 continue;
2500 }
2501
2502 UInt32 v = HASH_VAL(buf, ppp);
2503 bool nextNeedCheckStartOpen = true;
2504 unsigned i = hash[v];
2505 unsigned indexOfDifficult = 0;
2506
2507 // ---------- Open Loop for Current Pos ----------
2508 bool wasOpen = false;
2509
2510 for (;;)
2511 {
2512 unsigned index;
2513 bool isDifficult;
2514 if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size())
2515 {
2516 index = difficultFormats[indexOfDifficult++];
2517 isDifficult = true;
2518 }
2519 else
2520 {
2521 if (i == 0xFF)
2522 break;
2523 index = sig2arc[i];
2524 unsigned sigIndex = i - arc2sig[index];
2525 i = prevs[i];
2526 if (needCheckStartOpen && difficultBools[index])
2527 continue;
2528 const CArcInfoEx &ai = op.codecs->Formats[index];
2529
2530 if (pos < ai.SignatureOffset)
2531 continue;
2532
2533 /*
2534 if (openOnlyFullArc)
2535 if (pos != ai.SignatureOffset)
2536 continue;
2537 */
2538
2539 const CByteBuffer &sig = ai.Signatures[sigIndex];
2540
2541 if (ppp + sig.Size() > availSize
2542 || !TestSignature(buf + ppp, sig, sig.Size()))
2543 continue;
2544 // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos));
2545 // prevPos = pos;
2546 isDifficult = false;
2547 }
2548
2549 const CArcInfoEx &ai = op.codecs->Formats[index];
2550
2551
2552 if ((isDifficult && pos == 0) || ai.SignatureOffset == pos)
2553 {
2554 // we don't check same archive second time */
2555 if (skipFrontalFormat[index])
2556 continue;
2557 }
2558
2559 UInt64 startArcPos = pos;
2560 if (!isDifficult)
2561 {
2562 if (pos < ai.SignatureOffset)
2563 continue;
2564 startArcPos = pos - ai.SignatureOffset;
2565 /*
2566 // we don't need the check for Z files
2567 if (startArcPos < handlerSpec->GetLastEnd())
2568 continue;
2569 */
2570 }
2571
2572 if (ai.IsArcFunc && startArcPos >= bufPhyPos)
2573 {
2574 size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos);
2575 if (offsetInBuf < bytesInBuf)
2576 {
2577 UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf);
2578 if (isArcRes == k_IsArc_Res_NO)
2579 continue;
2580 if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
2581 continue;
2582 /*
2583 if (isArcRes == k_IsArc_Res_YES_LOW_PROB)
2584 {
2585 // if (pos != ai.SignatureOffset)
2586 continue;
2587 }
2588 */
2589 }
2590 // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name);
2591 }
2592
2593 /*
2594 if (pos == 67109888)
2595 pos = pos;
2596 */
2597 PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name));
2598
2599 bool isMainFormat = isMainFormatArr[index];
2600 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2601
2602 CMyComPtr<IInArchive> archive;
2603 RINOK(PrepareToOpen(op, index, archive));
2604 if (!archive)
2605 return E_FAIL;
2606
2607 // OutputDebugStringW(ai.Name);
2608
2609 UInt64 rem = fileSize - startArcPos;
2610
2611 UInt64 arcStreamOffset = 0;
2612
2613 if (ai.Flags_UseGlobalOffset())
2614 {
2615 limitedStreamSpec->InitAndSeek(0, fileSize);
2616 limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL);
2617 }
2618 else
2619 {
2620 limitedStreamSpec->InitAndSeek(startArcPos, rem);
2621 arcStreamOffset = startArcPos;
2622 }
2623
2624 UInt64 maxCheckStartPosition = 0;
2625
2626 if (openCallback_Offset)
2627 {
2628 openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
2629 openCallback_Offset_Spec->Offset = startArcPos;
2630 }
2631
2632 // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset);
2633 extractCallback_To_OpenCallback_Spec->Files = 0;
2634 extractCallback_To_OpenCallback_Spec->Offset = startArcPos;
2635
2636 HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition,
2637 useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback,
2638 extractCallback_To_OpenCallback);
2639
2640 RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result));
2641
2642 bool isOpen = false;
2643 if (result == S_FALSE)
2644 {
2645 if (!mode.CanReturnParser)
2646 {
2647 if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen())
2648 {
2649 ErrorInfo.ErrorFormatIndex = index;
2650 NonOpen_ErrorInfo = ErrorInfo;
2651 // if archive was detected, we don't need additional open attempts
2652 return S_FALSE;
2653 }
2654 continue;
2655 }
2656 if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0)
2657 continue;
2658 }
2659 else
2660 {
2661 isOpen = true;
2662 RINOK(result);
2663 PRF(printf(" OK "));
2664 }
2665
2666 // fprintf(stderr, "\n %8X %S", startArcPos, Path);
2667 // printf("\nOpen OK: %S", ai.Name);
2668
2669
2670 NArchive::NParser::CParseItem pi;
2671 pi.Offset = startArcPos;
2672
2673 if (ai.Flags_UseGlobalOffset())
2674 pi.Offset = Offset;
2675 else if (Offset != 0)
2676 return E_FAIL;
2677 UInt64 arcRem = FileSize - pi.Offset;
2678 UInt64 phySize = arcRem;
2679 bool phySizeDefined = PhySizeDefined;
2680 if (phySizeDefined)
2681 {
2682 if (pi.Offset + PhySize > FileSize)
2683 {
2684 // ErrorInfo.ThereIsTail = true;
2685 PhySize = FileSize - pi.Offset;
2686 }
2687 phySize = PhySize;
2688 }
2689 if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63))
2690 return E_FAIL;
2691
2692 /*
2693 if (!ai.UseGlobalOffset)
2694 {
2695 if (phySize > arcRem)
2696 {
2697 ThereIsTail = true;
2698 phySize = arcRem;
2699 }
2700 }
2701 */
2702
2703 bool needScan = false;
2704
2705
2706 if (isOpen && !phySizeDefined)
2707 {
2708 // it's for Z format
2709 pi.LenIsUnknown = true;
2710 needScan = true;
2711 phySize = arcRem;
2712 nextNeedCheckStartOpen = false;
2713 }
2714
2715 pi.Size = phySize;
2716 /*
2717 if (OkPhySize_Defined)
2718 pi.OkSize = OkPhySize;
2719 */
2720 pi.NormalizeOffset();
2721 // printf(" phySize = %8d", (unsigned)phySize);
2722
2723 /*
2724 if (needSkipFullArc)
2725 if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize)
2726 continue;
2727 */
2728 if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
2729 {
2730 // it's possible for dmg archives
2731 if (!mode.CanReturnArc)
2732 continue;
2733 }
2734
2735 if (mode.EachPos)
2736 pos++;
2737 else if (needScan)
2738 {
2739 pos++;
2740 /*
2741 if (!OkPhySize_Defined)
2742 pos++;
2743 else
2744 pos = pi.Offset + pi.OkSize;
2745 */
2746 }
2747 else
2748 pos = pi.Offset + pi.Size;
2749
2750
2751 RINOK(ReadParseItemProps(archive, ai, pi));
2752
2753 if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */)
2754 {
2755 /* It's for DMG format.
2756 This code deletes all previous items that are included to current item */
2757
2758 while (!handlerSpec->_items.IsEmpty())
2759 {
2760 {
2761 const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back();
2762 if (back.Offset < pi.Offset)
2763 break;
2764 if (back.Offset + back.Size > pi.Offset + pi.Size)
2765 break;
2766 }
2767 handlerSpec->_items.DeleteBack();
2768 }
2769 }
2770
2771
2772 if (isOpen && mode.CanReturnArc && phySizeDefined)
2773 {
2774 // if (pi.Offset + pi.Size >= fileSize)
2775 bool openCur = false;
2776
2777 bool thereIsTail = ErrorInfo.ThereIsTail;
2778 if (thereIsTail && mode.ZerosTailIsAllowed)
2779 {
2780 RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize));
2781 if (ErrorInfo.IgnoreTail)
2782 thereIsTail = false;
2783 }
2784
2785 if (pi.Offset != 0)
2786 {
2787 if (!pi.IsNotArcType)
2788 if (thereIsTail)
2789 openCur = specFlags.CanReturnMid;
2790 else
2791 openCur = specFlags.CanReturnTail;
2792 }
2793 else
2794 {
2795 if (!thereIsTail)
2796 openCur = true;
2797 else
2798 openCur = specFlags.CanReturnFrontal;
2799
2800
2801 if (formatIndex >= -2)
2802 openCur = true;
2803 }
2804 if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */)
2805 openCur = false;
2806
2807 // We open file as SFX, if there is front archive or first archive is "Self Executable"
2808 if (!openCur && !pi.IsSelfExe && !thereIsTail &&
2809 (!pi.IsNotArcType || pi.Offset == 0))
2810 {
2811 if (handlerSpec->_items.IsEmpty())
2812 {
2813 if (specFlags.CanReturnTail)
2814 openCur = true;
2815 }
2816 else if (handlerSpec->_items.Size() == 1)
2817 {
2818 if (handlerSpec->_items[0].IsSelfExe)
2819 {
2820 if (mode.SpecUnknownExt.CanReturnTail)
2821 openCur = true;
2822 }
2823 }
2824 }
2825
2826 if (openCur)
2827 {
2828 InStream = op.stream;
2829 Archive = archive;
2830 FormatIndex = index;
2831 ArcStreamOffset = arcStreamOffset;
2832 return S_OK;
2833 }
2834 }
2835
2836 /*
2837 if (openOnlyFullArc)
2838 {
2839 ErrorInfo.ClearErrors();
2840 return S_FALSE;
2841 }
2842 */
2843
2844 pi.FormatIndex = index;
2845
2846 // printf("\nAdd offset = %d", (int)pi.Offset);
2847 handlerSpec->AddItem(pi);
2848 wasOpen = true;
2849 break;
2850 }
2851 // ---------- End of Open Loop for Current Pos ----------
2852
2853 if (!wasOpen)
2854 pos++;
2855 needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen);
2856 }
2857 // ---------- End of Main Scan Loop ----------
2858
2859 /*
2860 if (handlerSpec->_items.Size() == 1)
2861 {
2862 const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
2863 if (pi.Size == fileSize && pi.Offset == 0)
2864 {
2865 Archive = archive;
2866 FormatIndex2 = pi.FormatIndex;
2867 return S_OK;
2868 }
2869 }
2870 */
2871
2872 if (mode.CanReturnParser)
2873 {
2874 bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing
2875 handlerSpec->AddUnknownItem(fileSize);
2876 if (handlerSpec->_items.Size() == 0)
2877 return S_FALSE;
2878 if (returnParser || handlerSpec->_items.Size() != 1)
2879 {
2880 // return S_FALSE;
2881 handlerSpec->_stream = op.stream;
2882 Archive = handler;
2883 ErrorInfo.ClearErrors();
2884 IsParseArc = true;
2885 FormatIndex = -1; // It's parser
2886 Offset = 0;
2887 return S_OK;
2888 }
2889 }
2890 }
2891
2892 #endif
2893
2894 if (!Archive)
2895 return S_FALSE;
2896 return S_OK;
2897 }
2898
OpenStream(const COpenOptions & op)2899 HRESULT CArc::OpenStream(const COpenOptions &op)
2900 {
2901 RINOK(OpenStream2(op));
2902 // PrintNumber("op.formatIndex 3", op.formatIndex);
2903
2904 if (Archive)
2905 {
2906 GetRawProps.Release();
2907 GetRootProps.Release();
2908 Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps);
2909 Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps);
2910
2911 RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree));
2912 RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted));
2913 RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream));
2914 RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux));
2915 RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode));
2916 RINOK(Archive_GetArcBoolProp(Archive, kpidReadOnly, IsReadOnly));
2917
2918 const UString fileName = ExtractFileNameFromPath(Path);
2919 UString extension;
2920 {
2921 int dotPos = fileName.ReverseFind_Dot();
2922 if (dotPos >= 0)
2923 extension = fileName.Ptr(dotPos + 1);
2924 }
2925
2926 DefaultName.Empty();
2927 if (FormatIndex >= 0)
2928 {
2929 const CArcInfoEx &ai = op.codecs->Formats[FormatIndex];
2930 if (ai.Exts.Size() == 0)
2931 DefaultName = GetDefaultName2(fileName, UString(), UString());
2932 else
2933 {
2934 int subExtIndex = ai.FindExtension(extension);
2935 if (subExtIndex < 0)
2936 subExtIndex = 0;
2937 const CArcExtInfo &extInfo = ai.Exts[subExtIndex];
2938 DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt);
2939 }
2940 }
2941 }
2942
2943 return S_OK;
2944 }
2945
2946 #ifdef _SFX
2947
2948 #ifdef _WIN32
2949 static const char *k_ExeExt = ".exe";
2950 static const unsigned k_ExeExt_Len = 4;
2951 #else
2952 static const char *k_ExeExt = "";
2953 static const unsigned k_ExeExt_Len = 0;
2954 #endif
2955
2956 #endif
2957
OpenStreamOrFile(COpenOptions & op)2958 HRESULT CArc::OpenStreamOrFile(COpenOptions &op)
2959 {
2960 CMyComPtr<IInStream> fileStream;
2961 CMyComPtr<ISequentialInStream> seqStream;
2962 CInFileStream *fileStreamSpec = NULL;
2963
2964 if (op.stdInMode)
2965 {
2966 seqStream = new CStdInFileStream;
2967 op.seqStream = seqStream;
2968 }
2969 else if (!op.stream)
2970 {
2971 fileStreamSpec = new CInFileStream;
2972 fileStream = fileStreamSpec;
2973 Path = filePath;
2974 if (!fileStreamSpec->Open(us2fs(Path)))
2975 {
2976 return GetLastError();
2977 }
2978 op.stream = fileStream;
2979 #ifdef _SFX
2980 IgnoreSplit = true;
2981 #endif
2982 }
2983
2984 /*
2985 if (callback)
2986 {
2987 UInt64 fileSize;
2988 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
2989 RINOK(op.callback->SetTotal(NULL, &fileSize))
2990 }
2991 */
2992
2993 HRESULT res = OpenStream(op);
2994 IgnoreSplit = false;
2995
2996 #ifdef _SFX
2997
2998 if (res != S_FALSE
2999 || !fileStreamSpec
3000 || !op.callbackSpec
3001 || NonOpen_ErrorInfo.IsArc_After_NonOpen())
3002 return res;
3003
3004 {
3005 if (filePath.Len() > k_ExeExt_Len
3006 && StringsAreEqualNoCase_Ascii(filePath.RightPtr(k_ExeExt_Len), k_ExeExt))
3007 {
3008 const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len);
3009 FOR_VECTOR (i, op.codecs->Formats)
3010 {
3011 const CArcInfoEx &ai = op.codecs->Formats[i];
3012 if (ai.IsSplit())
3013 continue;
3014 UString path3 = path2;
3015 path3 += L'.';
3016 path3 += ai.GetMainExt(); // "7z" for SFX.
3017 Path = path3;
3018 Path.AddAscii(".001");
3019 bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path));
3020 if (!isOk)
3021 {
3022 Path = path3;
3023 isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path));
3024 }
3025 if (isOk)
3026 {
3027 if (fileStreamSpec->Open(us2fs(Path)))
3028 {
3029 op.stream = fileStream;
3030 NonOpen_ErrorInfo.ClearErrors_Full();
3031 if (OpenStream(op) == S_OK)
3032 return S_OK;
3033 }
3034 }
3035 }
3036 }
3037 }
3038
3039 #endif
3040
3041 return res;
3042 }
3043
KeepModeForNextOpen()3044 void CArchiveLink::KeepModeForNextOpen()
3045 {
3046 for (unsigned i = Arcs.Size(); i != 0;)
3047 {
3048 i--;
3049 CMyComPtr<IArchiveKeepModeForNextOpen> keep;
3050 Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep);
3051 if (keep)
3052 keep->KeepModeForNextOpen();
3053 }
3054 }
3055
Close()3056 HRESULT CArchiveLink::Close()
3057 {
3058 for (unsigned i = Arcs.Size(); i != 0;)
3059 {
3060 i--;
3061 RINOK(Arcs[i].Close());
3062 }
3063 IsOpen = false;
3064 // ErrorsText.Empty();
3065 return S_OK;
3066 }
3067
Release()3068 void CArchiveLink::Release()
3069 {
3070 // NonOpenErrorFormatIndex = -1;
3071 NonOpen_ErrorInfo.ClearErrors();
3072 NonOpen_ArcPath.Empty();
3073 while (!Arcs.IsEmpty())
3074 Arcs.DeleteBack();
3075 }
3076
3077 /*
3078 void CArchiveLink::Set_ErrorsText()
3079 {
3080 FOR_VECTOR(i, Arcs)
3081 {
3082 const CArc &arc = Arcs[i];
3083 if (!arc.ErrorFlagsText.IsEmpty())
3084 {
3085 if (!ErrorsText.IsEmpty())
3086 ErrorsText.Add_LF();
3087 ErrorsText += GetUnicodeString(arc.ErrorFlagsText);
3088 }
3089 if (!arc.ErrorMessage.IsEmpty())
3090 {
3091 if (!ErrorsText.IsEmpty())
3092 ErrorsText.Add_LF();
3093 ErrorsText += arc.ErrorMessage;
3094 }
3095
3096 if (!arc.WarningMessage.IsEmpty())
3097 {
3098 if (!ErrorsText.IsEmpty())
3099 ErrorsText.Add_LF();
3100 ErrorsText += arc.WarningMessage;
3101 }
3102 }
3103 }
3104 */
3105
Open(COpenOptions & op)3106 HRESULT CArchiveLink::Open(COpenOptions &op)
3107 {
3108 Release();
3109 if (op.types->Size() >= 32)
3110 return E_NOTIMPL;
3111
3112 HRESULT resSpec;
3113
3114 for (;;)
3115 {
3116 resSpec = S_OK;
3117
3118 op.openType = COpenType();
3119 if (op.types->Size() >= 1)
3120 {
3121 COpenType latest;
3122 if (Arcs.Size() < op.types->Size())
3123 latest = (*op.types)[op.types->Size() - Arcs.Size() - 1];
3124 else
3125 {
3126 latest = (*op.types)[0];
3127 if (!latest.Recursive)
3128 break;
3129 }
3130 op.openType = latest;
3131 }
3132 else if (Arcs.Size() >= 32)
3133 break;
3134
3135 /*
3136 op.formatIndex = -1;
3137 if (op.types->Size() >= 1)
3138 {
3139 int latest;
3140 if (Arcs.Size() < op.types->Size())
3141 latest = (*op.types)[op.types->Size() - Arcs.Size() - 1];
3142 else
3143 {
3144 latest = (*op.types)[0];
3145 if (latest != -2 && latest != -3)
3146 break;
3147 }
3148 if (latest >= 0)
3149 op.formatIndex = latest;
3150 else if (latest == -1 || latest == -2)
3151 {
3152 // default
3153 }
3154 else if (latest == -3)
3155 op.formatIndex = -2;
3156 else
3157 op.formatIndex = latest + 2;
3158 }
3159 else if (Arcs.Size() >= 32)
3160 break;
3161 */
3162
3163 if (Arcs.IsEmpty())
3164 {
3165 CArc arc;
3166 arc.filePath = op.filePath;
3167 arc.Path = op.filePath;
3168 arc.SubfileIndex = (UInt32)(Int32)-1;
3169 HRESULT result = arc.OpenStreamOrFile(op);
3170 if (result != S_OK)
3171 {
3172 if (result == S_FALSE)
3173 {
3174 NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo;
3175 // NonOpenErrorFormatIndex = arc.ErrorFormatIndex;
3176 NonOpen_ArcPath = arc.Path;
3177 }
3178 return result;
3179 }
3180 Arcs.Add(arc);
3181 continue;
3182 }
3183
3184 // PrintNumber("op.formatIndex 11", op.formatIndex);
3185
3186 const CArc &arc = Arcs.Back();
3187
3188 if (op.types->Size() > Arcs.Size())
3189 resSpec = E_NOTIMPL;
3190
3191 UInt32 mainSubfile;
3192 {
3193 NCOM::CPropVariant prop;
3194 RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop));
3195 if (prop.vt == VT_UI4)
3196 mainSubfile = prop.ulVal;
3197 else
3198 break;
3199 UInt32 numItems;
3200 RINOK(arc.Archive->GetNumberOfItems(&numItems));
3201 if (mainSubfile >= numItems)
3202 break;
3203 }
3204
3205
3206 CMyComPtr<IInArchiveGetStream> getStream;
3207 if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream)
3208 break;
3209
3210 CMyComPtr<ISequentialInStream> subSeqStream;
3211 if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream)
3212 break;
3213
3214 CMyComPtr<IInStream> subStream;
3215 if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream)
3216 break;
3217
3218 CArc arc2;
3219 RINOK(arc.GetItemPath(mainSubfile, arc2.Path));
3220
3221 bool zerosTailIsAllowed;
3222 RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed));
3223
3224
3225 if (op.callback)
3226 {
3227 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
3228 op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
3229 if (setSubArchiveName)
3230 setSubArchiveName->SetSubArchiveName(arc2.Path);
3231 }
3232
3233 arc2.SubfileIndex = mainSubfile;
3234
3235 // CIntVector incl;
3236 CIntVector excl;
3237
3238 COpenOptions op2;
3239 #ifndef _SFX
3240 op2.props = op.props;
3241 #endif
3242 op2.codecs = op.codecs;
3243 // op2.types = &incl;
3244 op2.openType = op.openType;
3245 op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed;
3246 op2.excludedFormats = !
3247 op2.stdInMode = false;
3248 op2.stream = subStream;
3249 op2.filePath = arc2.Path;
3250 op2.callback = op.callback;
3251 op2.callbackSpec = op.callbackSpec;
3252
3253
3254 HRESULT result = arc2.OpenStream(op2);
3255 resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE);
3256 if (result == S_FALSE)
3257 {
3258 NonOpen_ErrorInfo = arc2.ErrorInfo;
3259 NonOpen_ArcPath = arc2.Path;
3260 break;
3261 }
3262 RINOK(result);
3263 RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined));
3264 Arcs.Add(arc2);
3265 }
3266 IsOpen = !Arcs.IsEmpty();
3267 return resSpec;
3268 }
3269
Open2(COpenOptions & op,IOpenCallbackUI * callbackUI)3270 HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI)
3271 {
3272 VolumesSize = 0;
3273 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
3274 CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec;
3275 openCallbackSpec->Callback = callbackUI;
3276
3277 FString prefix, name;
3278
3279 if (!op.stream && !op.stdInMode)
3280 {
3281 NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name);
3282 openCallbackSpec->Init(prefix, name);
3283 }
3284 else
3285 {
3286 openCallbackSpec->SetSubArchiveName(op.filePath);
3287 }
3288
3289 op.callback = callback;
3290 op.callbackSpec = openCallbackSpec;
3291
3292 HRESULT res = Open(op);
3293
3294 PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
3295 // Password = openCallbackSpec->Password;
3296
3297 RINOK(res);
3298 // VolumePaths.Add(fs2us(prefix + name));
3299
3300 FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed)
3301 {
3302 if (openCallbackSpec->FileNames_WasUsed[i])
3303 {
3304 VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]);
3305 VolumesSize += openCallbackSpec->FileSizes[i];
3306 }
3307 }
3308 // VolumesSize = openCallbackSpec->TotalSize;
3309 return S_OK;
3310 }
3311
ReOpen(const COpenOptions & op)3312 HRESULT CArc::ReOpen(const COpenOptions &op)
3313 {
3314 ErrorInfo.ClearErrors();
3315 ErrorInfo.ErrorFormatIndex = -1;
3316
3317 UInt64 fileSize = 0;
3318 if (op.stream)
3319 {
3320 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
3321 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
3322 }
3323 FileSize = fileSize;
3324
3325 CMyComPtr<IInStream> stream2;
3326 Int64 globalOffset = GetGlobalOffset();
3327 if (globalOffset <= 0)
3328 stream2 = op.stream;
3329 else
3330 {
3331 CTailInStream *tailStreamSpec = new CTailInStream;
3332 stream2 = tailStreamSpec;
3333 tailStreamSpec->Stream = op.stream;
3334 tailStreamSpec->Offset = globalOffset;
3335 tailStreamSpec->Init();
3336 RINOK(tailStreamSpec->SeekToStart());
3337 }
3338
3339 // There are archives with embedded STUBs (like ZIP), so we must support signature scanning
3340 // But for another archives we can use 0 here. So the code can be fixed !!!
3341 UInt64 maxStartPosition = kMaxCheckStartPosition;
3342 HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback);
3343
3344 if (res == S_OK)
3345 {
3346 RINOK(ReadBasicProps(Archive, globalOffset, res));
3347 ArcStreamOffset = globalOffset;
3348 if (ArcStreamOffset != 0)
3349 InStream = op.stream;
3350 }
3351 return res;
3352 }
3353
Open3(COpenOptions & op,IOpenCallbackUI * callbackUI)3354 HRESULT CArchiveLink::Open3(COpenOptions &op, IOpenCallbackUI *callbackUI)
3355 {
3356 HRESULT res = Open2(op, callbackUI);
3357 if (callbackUI)
3358 {
3359 RINOK(callbackUI->Open_Finished());
3360 }
3361 return res;
3362 }
3363
ReOpen(COpenOptions & op)3364 HRESULT CArchiveLink::ReOpen(COpenOptions &op)
3365 {
3366 if (Arcs.Size() > 1)
3367 return E_NOTIMPL;
3368
3369 CObjectVector<COpenType> inc;
3370 CIntVector excl;
3371
3372 op.types = &inc;
3373 op.excludedFormats = !
3374 op.stdInMode = false;
3375 op.stream = NULL;
3376 if (Arcs.Size() == 0) // ???
3377 return Open2(op, NULL);
3378
3379 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
3380 CMyComPtr<IArchiveOpenCallback> openCallbackNew = openCallbackSpec;
3381
3382 openCallbackSpec->Callback = NULL;
3383 openCallbackSpec->ReOpenCallback = op.callback;
3384 {
3385 FString dirPrefix, fileName;
3386 NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), dirPrefix, fileName);
3387 openCallbackSpec->Init(dirPrefix, fileName);
3388 }
3389
3390
3391 CInFileStream *fileStreamSpec = new CInFileStream;
3392 CMyComPtr<IInStream> stream(fileStreamSpec);
3393 if (!fileStreamSpec->Open(us2fs(op.filePath)))
3394 return GetLastError();
3395 op.stream = stream;
3396
3397 CArc &arc = Arcs[0];
3398 HRESULT res = arc.ReOpen(op);
3399
3400 PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
3401 // Password = openCallbackSpec->Password;
3402
3403 IsOpen = (res == S_OK);
3404 return res;
3405 }
3406
3407 #ifndef _SFX
3408
ParseComplexSize(const wchar_t * s,UInt64 & result)3409 bool ParseComplexSize(const wchar_t *s, UInt64 &result)
3410 {
3411 result = 0;
3412 const wchar_t *end;
3413 UInt64 number = ConvertStringToUInt64(s, &end);
3414 if (end == s)
3415 return false;
3416 if (*end == 0)
3417 {
3418 result = number;
3419 return true;
3420 }
3421 if (end[1] != 0)
3422 return false;
3423 unsigned numBits;
3424 switch (MyCharLower_Ascii(*end))
3425 {
3426 case 'b': result = number; return true;
3427 case 'k': numBits = 10; break;
3428 case 'm': numBits = 20; break;
3429 case 'g': numBits = 30; break;
3430 case 't': numBits = 40; break;
3431 default: return false;
3432 }
3433 if (number >= ((UInt64)1 << (64 - numBits)))
3434 return false;
3435 result = number << numBits;
3436 return true;
3437 }
3438
ParseTypeParams(const UString & s,COpenType & type)3439 static bool ParseTypeParams(const UString &s, COpenType &type)
3440 {
3441 if (s[0] == 0)
3442 return true;
3443 if (s[1] == 0)
3444 {
3445 switch ((unsigned)(Byte)s[0])
3446 {
3447 case 'e': type.EachPos = true; return true;
3448 case 'a': type.CanReturnArc = true; return true;
3449 case 'r': type.Recursive = true; return true;
3450 }
3451 return false;
3452 }
3453 if (s[0] == 's')
3454 {
3455 UInt64 result;
3456 if (!ParseComplexSize(s.Ptr(1), result))
3457 return false;
3458 type.MaxStartOffset = result;
3459 type.MaxStartOffset_Defined = true;
3460 return true;
3461 }
3462
3463 return false;
3464 }
3465
ParseType(CCodecs & codecs,const UString & s,COpenType & type)3466 bool ParseType(CCodecs &codecs, const UString &s, COpenType &type)
3467 {
3468 int pos2 = s.Find(L':');
3469
3470 {
3471 UString name;
3472 if (pos2 < 0)
3473 {
3474 name = s;
3475 pos2 = s.Len();
3476 }
3477 else
3478 {
3479 name = s.Left(pos2);
3480 pos2++;
3481 }
3482
3483 int index = codecs.FindFormatForArchiveType(name);
3484 type.Recursive = false;
3485
3486 if (index < 0)
3487 {
3488 if (name[0] == '*')
3489 {
3490 if (name[1] != 0)
3491 return false;
3492 }
3493 else if (name[0] == '#')
3494 {
3495 if (name[1] != 0)
3496 return false;
3497 type.CanReturnArc = false;
3498 type.CanReturnParser = true;
3499 }
3500 else
3501 return false;
3502 }
3503
3504 type.FormatIndex = index;
3505
3506 }
3507
3508 for (unsigned i = pos2; i < s.Len();)
3509 {
3510 int next = s.Find(L':', i);
3511 if (next < 0)
3512 next = s.Len();
3513 const UString name = s.Mid(i, next - i);
3514 if (name.IsEmpty())
3515 return false;
3516 if (!ParseTypeParams(name, type))
3517 return false;
3518 i = next + 1;
3519 }
3520
3521 return true;
3522 }
3523
ParseOpenTypes(CCodecs & codecs,const UString & s,CObjectVector<COpenType> & types)3524 bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types)
3525 {
3526 types.Clear();
3527 for (unsigned pos = 0; pos < s.Len();)
3528 {
3529 int pos2 = s.Find(L'.', pos);
3530 if (pos2 < 0)
3531 pos2 = s.Len();
3532 UString name = s.Mid(pos, pos2 - pos);
3533 if (name.IsEmpty())
3534 return false;
3535 COpenType type;
3536 if (!ParseType(codecs, name, type))
3537 return false;
3538 types.Add(type);
3539 pos = pos2 + 1;
3540 }
3541 return true;
3542 }
3543
3544 #endif
3545