// ComHandler.cpp #include "StdAfx.h" #include "Common/ComTry.h" #include "Windows/PropVariant.h" #include "../../Common/StreamUtils.h" #include "ComHandler.h" namespace NArchive { namespace NCom { STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidIsDir, VT_BOOL}, { NULL, kpidSize, VT_UI8}, // { NULL, kpidAttributes, VT_UI4}, { NULL, kpidPackSize, VT_UI8}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidMTime, VT_FILETIME} }; STATPROPSTG kArcProps[] = { { NULL, kpidClusterSize, VT_UI4} }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; switch(propID) { case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break; } prop.Detach(value); return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; const CRef &ref = _db.Refs[index]; const CItem &item = _db.Items[ref.Did]; switch(propID) { case kpidPath: { UString name = _db.GetItemPath(index); prop = name; break; } case kpidIsDir: prop = item.IsDir(); break; case kpidCTime: prop = item.CTime; break; case kpidMTime: prop = item.MTime; break; /* case kpidAttributes: prop = item.Falgs; break; */ case kpidPackSize: if (!item.IsDir()) { int numBits = _db.IsLargeStream(item.Size) ? _db.SectorSizeBits : _db.MiniSectorSizeBits; prop = (item.Size + ((UInt64)1 << numBits) - 1) >> numBits << numBits; break; } case kpidSize: if (!item.IsDir()) prop = (UInt64)item.Size; break; } prop.Detach(value); return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback * /* openArchiveCallback */) { COM_TRY_BEGIN Close(); try { if (OpenArchive(inStream, _db) != S_OK) return S_FALSE; _stream = inStream; } catch(...) { return S_FALSE; } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { _db.Clear(); _stream.Release(); return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems, Int32 _aTestMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN bool testMode = (_aTestMode != 0); bool allFilesMode = (numItems == UInt32(-1)); if (allFilesMode) numItems = _db.Refs.Size(); if (numItems == 0) return S_OK; UInt32 i; UInt64 totalSize = 0; for(i = 0; i < numItems; i++) { const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did]; if (!item.IsDir()) totalSize += item.Size; } RINOK(extractCallback->SetTotal(totalSize)); UInt64 currentTotalSize = 0, currentItemSize = 0; CByteBuffer sect; sect.SetCapacity((UInt32)1 << _db.SectorSizeBits); for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) { RINOK(extractCallback->SetCompleted(¤tTotalSize)); Int32 index = allFilesMode ? i : indices[i]; const CItem &item = _db.Items[_db.Refs[index].Did]; currentItemSize = 0; if (!item.IsDir()) currentItemSize = item.Size; CMyComPtr realOutStream; Int32 askMode = testMode ? NArchive::NExtract::NAskMode::kTest : NArchive::NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); if (item.IsDir()) { RINOK(extractCallback->PrepareOperation(askMode)); RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK)); continue; } if (!testMode && (!realOutStream)) continue; RINOK(extractCallback->PrepareOperation(askMode)); Int32 res = NArchive::NExtract::NOperationResult::kDataError; { UInt32 sid = item.Sid; UInt64 prev = 0; for (UInt64 pos = 0;;) { if (sid == NFatID::kEndOfChain) { if (pos != item.Size) break; res = NArchive::NExtract::NOperationResult::kOK; break; } if (pos >= item.Size) break; UInt64 offset; UInt32 size; if (_db.IsLargeStream(item.Size)) { if (pos - prev > (1 << 20)) { UInt64 processed = currentTotalSize + pos; RINOK(extractCallback->SetCompleted(&processed)); prev = pos; } size = 1 << _db.SectorSizeBits; offset = ((UInt64)sid + 1) << _db.SectorSizeBits; if (sid >= _db.FatSize) break; sid = _db.Fat[sid]; } else { int subBits = (_db.SectorSizeBits - _db.MiniSectorSizeBits); UInt32 fid = sid >> subBits; if (fid >= _db.NumSectorsInMiniStream) break; size = 1 << _db.MiniSectorSizeBits; offset = (((UInt64)_db.MiniSids[fid] + 1) << _db.SectorSizeBits) + ((sid & ((1 << subBits) - 1)) << _db.MiniSectorSizeBits); if (sid >= _db.MatSize) break; sid = _db.Mat[sid]; } // last sector can be smaller than sector size (it can contain requied data only). UInt64 rem = item.Size - pos; if (size > rem) size = (UInt32)rem; RINOK(_stream->Seek(offset, STREAM_SEEK_SET, NULL)); size_t realProcessedSize = size; RINOK(ReadStream(_stream, sect, &realProcessedSize)); if (realProcessedSize != size) break; if (realOutStream) { RINOK(WriteStream(realOutStream, sect, size)); } pos += size; } } realOutStream.Release(); RINOK(extractCallback->SetOperationResult(res)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _db.Refs.Size(); return S_OK; } }}