cabhandler.cpp

来自「由7-zip提供的压缩、解压缩程序」· C++ 代码 · 共 816 行 · 第 1/2 页

CPP
816
字号
// CabHandler.cpp#include "StdAfx.h"#include "Common/StringConvert.h"#include "Common/Defs.h"#include "Common/Alloc.h"#include "Common/UTFConvert.h"#include "Common/ComTry.h"#include "Common/IntToString.h"#include "Windows/PropVariant.h"#include "Windows/Time.h"#include "CabHandler.h"#include "CabBlockInStream.h"#include "../../Compress/Copy/CopyCoder.h"#include "../../Compress/Deflate/DeflateDecoder.h"#include "../../Compress/Lzx/LzxDecoder.h"#include "../../Compress/Quantum/QuantumDecoder.h"#include "../Common/ItemNameUtils.h"using namespace NWindows;namespace NArchive {namespace NCab {// #define _CAB_DETAILS#ifdef _CAB_DETAILSenum {  kpidBlockReal = kpidUserDefined,  kpidOffset,  kpidVolume,};#endifSTATPROPSTG kProperties[] = {  { NULL, kpidPath, VT_BSTR},  // { NULL, kpidIsFolder, VT_BOOL},  { NULL, kpidSize, VT_UI8},  { NULL, kpidLastWriteTime, VT_FILETIME},  { NULL, kpidAttributes, VT_UI4},  { NULL, kpidMethod, VT_BSTR},  { NULL, kpidBlock, VT_I4}  #ifdef _CAB_DETAILS  ,  { L"BlockReal", kpidBlockReal, VT_UI4},  { L"Offset", kpidOffset, VT_UI4},  { L"Volume", kpidVolume, VT_UI4}  #endif};static const int kNumProperties = sizeof(kProperties) / sizeof(kProperties[0]);static const wchar_t *kMethods[] = {  L"None",  L"MSZip",  L"Quantum",  L"LZX"};static const int kNumMethods = sizeof(kMethods) / sizeof(kMethods[0]);static const wchar_t *kUnknownMethod = L"Unknown";STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value){  value->vt = VT_EMPTY;  return S_OK;}STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties){  *numProperties = sizeof(kProperties) / sizeof(kProperties[0]);  return S_OK;}STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index,           BSTR *name, PROPID *propID, VARTYPE *varType){  if(index >= sizeof(kProperties) / sizeof(kProperties[0]))    return E_INVALIDARG;  const STATPROPSTG &srcItem = kProperties[index];  *propID = srcItem.propid;  *varType = srcItem.vt;  if (srcItem.lpwstrName == 0)    *name = 0;  else    *name = ::SysAllocString(srcItem.lpwstrName);  return S_OK;}STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties){  *numProperties = 0;  return S_OK;}STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32 index,           BSTR *name, PROPID *propID, VARTYPE *varType){  return E_INVALIDARG;}STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value){  COM_TRY_BEGIN  NWindows::NCOM::CPropVariant propVariant;    const CMvItem &mvItem = m_Database.Items[index];  const CDatabaseEx &db = m_Database.Volumes[mvItem.VolumeIndex];  int itemIndex = mvItem.ItemIndex;  const CItem &item = db.Items[itemIndex];  switch(propID)  {    case kpidPath:    {      UString unicodeName;      if (item.IsNameUTF())        ConvertUTF8ToUnicode(item.Name, unicodeName);      else        unicodeName = MultiByteToUnicodeString(item.Name, CP_ACP);      propVariant = (const wchar_t *)NItemName::WinNameToOSName(unicodeName);      break;    }    case kpidIsFolder:      propVariant = item.IsDirectory();      break;    case kpidSize:      propVariant = item.Size;      break;    case kpidLastWriteTime:    {      FILETIME localFileTime, utcFileTime;      if (NTime::DosTimeToFileTime(item.Time, localFileTime))      {        if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime))          utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;      }      else        utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;      propVariant = utcFileTime;      break;    }    case kpidAttributes:      propVariant = item.GetWinAttributes();      break;    case kpidMethod:    {      UInt16 realFolderIndex = item.GetFolderIndex(db.Folders.Size());      const CFolder &folder = db.Folders[realFolderIndex];      int methodIndex = folder.GetCompressionMethod();      UString method = (methodIndex < kNumMethods) ? kMethods[methodIndex] : kUnknownMethod;      if (methodIndex == NHeader::NCompressionMethodMajor::kLZX ||         methodIndex == NHeader::NCompressionMethodMajor::kQuantum)      {        method += L":";        wchar_t temp[32];        ConvertUInt64ToString(folder.CompressionTypeMinor, temp);        method += temp;      }      propVariant = method;      break;    }    case kpidBlock:      propVariant = (Int32)m_Database.GetFolderIndex(&mvItem);      break;        #ifdef _CAB_DETAILS        case kpidBlockReal:      propVariant = UInt32(item.FolderIndex);      break;    case kpidOffset:      propVariant = (UInt32)item.Offset;      break;    case kpidVolume:      propVariant = (UInt32)mvItem.VolumeIndex;      break;        #endif  }  propVariant.Detach(value);  return S_OK;  COM_TRY_END}/*class CPropgressImp: public CProgressVirt{  CMyComPtr<IArchiveOpenCallback> m_OpenArchiveCallback;public:  STDMETHOD(SetTotal)(const UInt64 *numFiles);  STDMETHOD(SetCompleted)(const UInt64 *numFiles);  void Init(IArchiveOpenCallback *openArchiveCallback)    { m_OpenArchiveCallback = openArchiveCallback; }};STDMETHODIMP CPropgressImp::SetTotal(const UInt64 *numFiles){  if (m_OpenArchiveCallback)    return m_OpenArchiveCallback->SetCompleted(numFiles, NULL);  return S_OK;}STDMETHODIMP CPropgressImp::SetCompleted(const UInt64 *numFiles){  if (m_OpenArchiveCallback)    return m_OpenArchiveCallback->SetCompleted(numFiles, NULL);  return S_OK;}*/STDMETHODIMP CHandler::Open(IInStream *inStream,     const UInt64 *maxCheckStartPosition,    IArchiveOpenCallback *openArchiveCallback){  COM_TRY_BEGIN  Close();  HRESULT res;  CInArchive archive;  CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;  {    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback;    openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback);  }  CMyComPtr<IInStream> nextStream = inStream;  bool prevChecked = false;  UInt64 numItems = 0;  try  {    while(nextStream != 0)    {      CDatabaseEx db;      db.Stream = nextStream;      res = archive.Open(maxCheckStartPosition, db);      if (res == S_OK)      {        if (!m_Database.Volumes.IsEmpty())        {          const CDatabaseEx &dbPrev = m_Database.Volumes[prevChecked ? m_Database.Volumes.Size() - 1 : 0];          if (dbPrev.ArchiveInfo.SetID != db.ArchiveInfo.SetID ||              dbPrev.ArchiveInfo.CabinetNumber + (prevChecked ? 1: - 1) !=               db.ArchiveInfo.CabinetNumber)            res = S_FALSE;        }      }      if (res == S_OK)        m_Database.Volumes.Insert(prevChecked ? m_Database.Volumes.Size() : 0, db);      else if (res != S_FALSE)        return res;      else       {        if (m_Database.Volumes.IsEmpty())          return S_FALSE;        if (prevChecked)          break;        prevChecked = true;      }      numItems += db.Items.Size();      RINOK(openArchiveCallback->SetCompleted(&numItems, NULL));              nextStream = 0;      while(true)      {        const COtherArchive *otherArchive = 0;        if (!prevChecked)        {          const CInArchiveInfo &ai = m_Database.Volumes.Front().ArchiveInfo;          if (ai.IsTherePrev())            otherArchive = &ai.PreviousArchive;          else            prevChecked = true;        }        if (otherArchive == 0)        {          const CInArchiveInfo &ai = m_Database.Volumes.Back().ArchiveInfo;          if (ai.IsThereNext())            otherArchive = &ai.NextArchive;        }        if (!otherArchive)          break;        const UString fullName = MultiByteToUnicodeString(otherArchive->FileName, CP_ACP);        HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream);        if (result == S_OK)          break;        if (result != S_FALSE)          return result;        if (prevChecked)          break;        prevChecked = true;      }    }    if (res == S_OK)    {      m_Database.FillSortAndShrink();      if (!m_Database.Check())        res = S_FALSE;    }  }  catch(...)  {    res = S_FALSE;  }  if (res != S_OK)  {    Close();    return res;  }  COM_TRY_END  return S_OK;}STDMETHODIMP CHandler::Close(){  m_Database.Clear();  return S_OK;}class CCabFolderOutStream:   public ISequentialOutStream,  public CMyUnknownImp{public:  MY_UNKNOWN_IMP  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);private:  const CMvDatabaseEx *m_Database;  const CRecordVector<bool> *m_ExtractStatuses;  int m_StartIndex;  int m_CurrentIndex;  CMyComPtr<IArchiveExtractCallback> m_ExtractCallback;  bool m_TestMode;  CMyComPtr<ISequentialOutStream> m_RealOutStream;  bool m_IsOk;  bool m_FileIsOpen;  UInt64 m_RemainFileSize;  UInt64 m_FolderSize;  UInt64 m_PosInFolder;  HRESULT OpenFile();  HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK);public:  HRESULT WriteEmptyFiles();  void Init(      const CMvDatabaseEx *database,      const CRecordVector<bool> *extractStatuses,       int startIndex,       UInt64 folderSize,      IArchiveExtractCallback *extractCallback,      bool testMode);  HRESULT FlushCorrupted();  HRESULT Unsupported();  UInt64 GetRemain() const { return m_FolderSize - m_PosInFolder; }  UInt64 GetPosInFolder() const { return m_PosInFolder; }};void CCabFolderOutStream::Init(    const CMvDatabaseEx *database,    const CRecordVector<bool> *extractStatuses,     int startIndex,     UInt64 folderSize,    IArchiveExtractCallback *extractCallback,    bool testMode){  m_Database = database;  m_ExtractStatuses = extractStatuses;  m_StartIndex = startIndex;  m_FolderSize = folderSize;  m_ExtractCallback = extractCallback;  m_TestMode = testMode;  m_CurrentIndex = 0;  m_PosInFolder = 0;  m_FileIsOpen = false;  m_IsOk = true;}HRESULT CCabFolderOutStream::OpenFile(){  Int32 askMode = (*m_ExtractStatuses)[m_CurrentIndex] ? (m_TestMode ?       NExtract::NAskMode::kTest :      NExtract::NAskMode::kExtract) :      NExtract::NAskMode::kSkip;  RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &m_RealOutStream, askMode));  if (!m_RealOutStream && !m_TestMode)    askMode = NArchive::NExtract::NAskMode::kSkip;  return m_ExtractCallback->PrepareOperation(askMode);}HRESULT CCabFolderOutStream::WriteEmptyFiles(){  if (m_FileIsOpen)    return S_OK;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?