1// 7zFolderOutStream.cpp
2
3#include "StdAfx.h"
4
5#include "7zFolderOutStream.h"
6
7namespace NArchive {
8namespace N7z {
9
10CFolderOutStream::CFolderOutStream()
11{
12  _crcStreamSpec = new COutStreamWithCRC;
13  _crcStream = _crcStreamSpec;
14}
15
16HRESULT CFolderOutStream::Init(
17    const CDbEx *db,
18    UInt32 ref2Offset, UInt32 startIndex,
19    const CBoolVector *extractStatuses,
20    IArchiveExtractCallback *extractCallback,
21    bool testMode, bool checkCrc)
22{
23  _db = db;
24  _ref2Offset = ref2Offset;
25  _startIndex = startIndex;
26
27  _extractStatuses = extractStatuses;
28  _extractCallback = extractCallback;
29  _testMode = testMode;
30  _checkCrc = checkCrc;
31
32  _currentIndex = 0;
33  _fileIsOpen = false;
34  return ProcessEmptyFiles();
35}
36
37HRESULT CFolderOutStream::OpenFile()
38{
39  Int32 askMode = ((*_extractStatuses)[_currentIndex]) ? (_testMode ?
40      NExtract::NAskMode::kTest :
41      NExtract::NAskMode::kExtract) :
42      NExtract::NAskMode::kSkip;
43  CMyComPtr<ISequentialOutStream> realOutStream;
44  UInt32 index = _startIndex + _currentIndex;
45  RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode));
46  _crcStreamSpec->SetStream(realOutStream);
47  _crcStreamSpec->Init(_checkCrc);
48  _fileIsOpen = true;
49  const CFileItem &fi = _db->Files[index];
50  _rem = fi.Size;
51  if (askMode == NExtract::NAskMode::kExtract && !realOutStream &&
52      !_db->IsItemAnti(index) && !fi.IsDir)
53    askMode = NExtract::NAskMode::kSkip;
54  return _extractCallback->PrepareOperation(askMode);
55}
56
57HRESULT CFolderOutStream::CloseFileAndSetResult(Int32 res)
58{
59  _crcStreamSpec->ReleaseStream();
60  _fileIsOpen = false;
61  _currentIndex++;
62  return _extractCallback->SetOperationResult(res);
63}
64
65HRESULT CFolderOutStream::CloseFileAndSetResult()
66{
67  const CFileItem &fi = _db->Files[_startIndex + _currentIndex];
68  return CloseFileAndSetResult(
69      (fi.IsDir || !fi.CrcDefined || !_checkCrc || fi.Crc == _crcStreamSpec->GetCRC()) ?
70      NExtract::NOperationResult::kOK :
71      NExtract::NOperationResult::kCRCError);
72}
73
74HRESULT CFolderOutStream::ProcessEmptyFiles()
75{
76  while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0)
77  {
78    RINOK(OpenFile());
79    RINOK(CloseFileAndSetResult());
80  }
81  return S_OK;
82}
83
84STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
85{
86  if (processedSize != NULL)
87    *processedSize = 0;
88  while (size != 0)
89  {
90    if (_fileIsOpen)
91    {
92      UInt32 cur = size < _rem ? size : (UInt32)_rem;
93      RINOK(_crcStream->Write(data, cur, &cur));
94      if (cur == 0)
95        break;
96      data = (const Byte *)data + cur;
97      size -= cur;
98      _rem -= cur;
99      if (processedSize != NULL)
100        *processedSize += cur;
101      if (_rem == 0)
102      {
103        RINOK(CloseFileAndSetResult());
104        RINOK(ProcessEmptyFiles());
105        continue;
106      }
107    }
108    else
109    {
110      RINOK(ProcessEmptyFiles());
111      if (_currentIndex == _extractStatuses->Size())
112      {
113        // we support partial extracting
114        if (processedSize != NULL)
115          *processedSize += size;
116        break;
117      }
118      RINOK(OpenFile());
119    }
120  }
121  return S_OK;
122}
123
124STDMETHODIMP CFolderOutStream::GetSubStreamSize(UInt64 subStream, UInt64 *value)
125{
126  *value = 0;
127  if ((int)subStream >= _extractStatuses->Size())
128    return S_FALSE;
129  *value = _db->Files[_startIndex + (int)subStream].Size;
130  return S_OK;
131}
132
133HRESULT CFolderOutStream::FlushCorrupted(Int32 resultEOperationResult)
134{
135  while (_currentIndex < _extractStatuses->Size())
136  {
137    if (_fileIsOpen)
138    {
139      RINOK(CloseFileAndSetResult(resultEOperationResult));
140    }
141    else
142    {
143      RINOK(OpenFile());
144    }
145  }
146  return S_OK;
147}
148
149}}
150