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