// ComHandler.cpp #include "StdAfx.h" #include "Common/ComTry.h" #include "Windows/PropVariant.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamUtils.h" #include "../../Compress/CopyCoder.h" #include "ComHandler.h" namespace NArchive { namespace NCom { STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidIsDir, VT_BOOL}, { NULL, kpidSize, VT_UI8}, { NULL, kpidPackSize, VT_UI8}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidMTime, VT_FILETIME} }; STATPROPSTG kArcProps[] = { { NULL, kpidClusterSize, VT_UI4}, { NULL, kpidSectorSize, 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; case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break; case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; 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: prop = _db.GetItemPath(index); break; case kpidIsDir: prop = item.IsDir(); break; case kpidCTime: prop = item.CTime; break; case kpidMTime: prop = item.MTime; break; case kpidPackSize: if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break; case kpidSize: if (!item.IsDir()) prop = 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 (_db.Open(inStream) != 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 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN 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 totalPackSize; totalSize = totalPackSize = 0; NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); CMyComPtr copyCoder = copyCoderSpec; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); for (i = 0; i < numItems; i++) { lps->InSize = totalPackSize; lps->OutSize = totalSize; RINOK(lps->SetCur()); Int32 index = allFilesMode ? i : indices[i]; const CItem &item = _db.Items[_db.Refs[index].Did]; CMyComPtr outStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(index, &outStream, askMode)); if (item.IsDir()) { RINOK(extractCallback->PrepareOperation(askMode)); RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); continue; } totalPackSize += _db.GetItemPackSize(item.Size); totalSize += item.Size; if (!testMode && !outStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); Int32 res = NExtract::NOperationResult::kDataError; CMyComPtr inStream; HRESULT hres = GetStream(index, &inStream); if (hres == S_FALSE) res = NExtract::NOperationResult::kDataError; else if (hres == E_NOTIMPL) res = NExtract::NOperationResult::kUnSupportedMethod; else { RINOK(hres); if (inStream) { RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); if (copyCoderSpec->TotalSize == item.Size) res = NExtract::NOperationResult::kOK; } } outStream.Release(); RINOK(extractCallback->SetOperationResult(res)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _db.Refs.Size(); return S_OK; } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN *stream = 0; const CItem &item = _db.Items[_db.Refs[index].Did]; CClusterInStream *streamSpec = new CClusterInStream; CMyComPtr streamTemp = streamSpec; streamSpec->Stream = _stream; streamSpec->StartOffset = 0; bool isLargeStream = _db.IsLargeStream(item.Size); int bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits; streamSpec->BlockSizeLog = bsLog; streamSpec->Size = item.Size; UInt32 clusterSize = (UInt32)1 << bsLog; UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog; if (numClusters64 >= ((UInt32)1 << 31)) return E_NOTIMPL; streamSpec->Vector.Reserve((int)numClusters64); UInt32 sid = item.Sid; UInt64 size = item.Size; if (size != 0) { for (;; size -= clusterSize) { if (isLargeStream) { if (sid >= _db.FatSize) return S_FALSE; streamSpec->Vector.Add(sid + 1); sid = _db.Fat[sid]; } else { UInt64 val; if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32) return S_FALSE; streamSpec->Vector.Add((UInt32)val); sid = _db.Mat[sid]; } if (size <= clusterSize) break; } } if (sid != NFatID::kEndOfChain) return S_FALSE; RINOK(streamSpec->InitAndSeek()); *stream = streamTemp.Detach(); return S_OK; COM_TRY_END } }}