michael@0: // OpenArchive.cpp michael@0: michael@0: #include "StdAfx.h" michael@0: michael@0: #include "OpenArchive.h" michael@0: michael@0: #include "Common/Wildcard.h" michael@0: michael@0: #include "Windows/FileName.h" michael@0: #include "Windows/FileDir.h" michael@0: #include "Windows/Defs.h" michael@0: #include "Windows/PropVariant.h" michael@0: michael@0: #include "../../Common/FileStreams.h" michael@0: #include "../../Common/StreamUtils.h" michael@0: michael@0: #include "Common/StringConvert.h" michael@0: michael@0: #ifdef FORMAT_7Z michael@0: #include "../../Archive/7z/7zHandler.h" michael@0: #endif michael@0: michael@0: #ifdef FORMAT_BZIP2 michael@0: #include "../../Archive/BZip2/BZip2Handler.h" michael@0: #endif michael@0: michael@0: #ifdef FORMAT_GZIP michael@0: #include "../../Archive/GZip/GZipHandler.h" michael@0: #endif michael@0: michael@0: #ifdef FORMAT_SPLIT michael@0: #include "../../Archive/Split/SplitHandler.h" michael@0: #endif michael@0: michael@0: #ifdef FORMAT_TAR michael@0: #include "../../Archive/Tar/TarHandler.h" michael@0: #endif michael@0: michael@0: #ifdef FORMAT_ZIP michael@0: #include "../../Archive/Zip/ZipHandler.h" michael@0: #endif michael@0: michael@0: #ifdef FORMAT_Z michael@0: #include "../../Archive/Z/ZHandler.h" michael@0: #endif michael@0: michael@0: #ifndef EXCLUDE_COM michael@0: #include "HandlerLoader.h" michael@0: #endif michael@0: michael@0: #include "DefaultName.h" michael@0: michael@0: using namespace NWindows; michael@0: michael@0: HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result) michael@0: { michael@0: NCOM::CPropVariant prop; michael@0: RINOK(archive->GetProperty(index, kpidPath, &prop)); michael@0: if(prop.vt == VT_BSTR) michael@0: result = prop.bstrVal; michael@0: else if (prop.vt == VT_EMPTY) michael@0: result.Empty(); michael@0: else michael@0: return E_FAIL; michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result) michael@0: { michael@0: RINOK(GetArchiveItemPath(archive, index, result)); michael@0: if (result.IsEmpty()) michael@0: result = defaultName; michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index, michael@0: const FILETIME &defaultFileTime, FILETIME &fileTime) michael@0: { michael@0: NCOM::CPropVariant prop; michael@0: RINOK(archive->GetProperty(index, kpidLastWriteTime, &prop)); michael@0: if (prop.vt == VT_FILETIME) michael@0: fileTime = prop.filetime; michael@0: else if (prop.vt == VT_EMPTY) michael@0: fileTime = defaultFileTime; michael@0: else michael@0: return E_FAIL; michael@0: return S_OK; michael@0: } michael@0: michael@0: static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) michael@0: { michael@0: NCOM::CPropVariant prop; michael@0: RINOK(archive->GetProperty(index, propID, &prop)); michael@0: if(prop.vt == VT_BOOL) michael@0: result = VARIANT_BOOLToBool(prop.boolVal); michael@0: else if (prop.vt == VT_EMPTY) michael@0: result = false; michael@0: else michael@0: return E_FAIL; michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) michael@0: { michael@0: return IsArchiveItemProp(archive, index, kpidIsFolder, result); michael@0: } michael@0: michael@0: HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result) michael@0: { michael@0: return IsArchiveItemProp(archive, index, kpidIsAnti, result); michael@0: } michael@0: michael@0: // Static-SFX (for Linux) can be big michael@0: const UInt64 kMaxCheckStartPosition = michael@0: #ifdef _WIN32 michael@0: 1 << 20; michael@0: #else michael@0: 1 << 22; michael@0: #endif michael@0: michael@0: michael@0: HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName) michael@0: { michael@0: CInFileStream *inStreamSpec = new CInFileStream; michael@0: CMyComPtr inStream(inStreamSpec); michael@0: inStreamSpec->Open(fileName); michael@0: return archive->Open(inStream, &kMaxCheckStartPosition, NULL); michael@0: } michael@0: michael@0: #ifndef _SFX michael@0: static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) michael@0: { michael@0: for (size_t i = 0; i < size; i++) michael@0: if (p1[i] != p2[i]) michael@0: return false; michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: HRESULT OpenArchive( michael@0: IInStream *inStream, michael@0: const UString &fileName, michael@0: #ifndef EXCLUDE_COM michael@0: HMODULE *module, michael@0: #endif michael@0: IInArchive **archiveResult, michael@0: CArchiverInfo &archiverInfoResult, michael@0: UString &defaultItemName, michael@0: IArchiveOpenCallback *openArchiveCallback) michael@0: { michael@0: *archiveResult = NULL; michael@0: CObjectVector archiverInfoList; michael@0: ReadArchiverInfoList(archiverInfoList); michael@0: UString extension; michael@0: { michael@0: int dotPos = fileName.ReverseFind(L'.'); michael@0: if (dotPos >= 0) michael@0: extension = fileName.Mid(dotPos + 1); michael@0: } michael@0: CIntVector orderIndices; michael@0: int i; michael@0: bool finded = false; michael@0: for(i = 0; i < archiverInfoList.Size(); i++) michael@0: { michael@0: if (archiverInfoList[i].FindExtension(extension) >= 0) michael@0: { michael@0: orderIndices.Insert(0, i); michael@0: finded = true; michael@0: } michael@0: else michael@0: orderIndices.Add(i); michael@0: } michael@0: michael@0: #ifndef _SFX michael@0: if (!finded) michael@0: { michael@0: CByteBuffer byteBuffer; michael@0: const UInt32 kBufferSize = (200 << 10); michael@0: byteBuffer.SetCapacity(kBufferSize); michael@0: Byte *buffer = byteBuffer; michael@0: RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); michael@0: UInt32 processedSize; michael@0: RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize)); michael@0: int numFinded = 0; michael@0: for (int pos = (int)processedSize; pos >= 0 ; pos--) michael@0: { michael@0: for(int i = numFinded; i < orderIndices.Size(); i++) michael@0: { michael@0: int index = orderIndices[i]; michael@0: const CArchiverInfo &ai = archiverInfoList[index]; michael@0: const CByteBuffer &sig = ai.StartSignature; michael@0: if (sig.GetCapacity() == 0) michael@0: continue; michael@0: if (pos + sig.GetCapacity() > processedSize) michael@0: continue; michael@0: if (TestSignature(buffer + pos, sig, sig.GetCapacity())) michael@0: { michael@0: orderIndices.Delete(i); michael@0: orderIndices.Insert(0, index); michael@0: numFinded++; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: HRESULT badResult = S_OK; michael@0: for(i = 0; i < orderIndices.Size(); i++) michael@0: { michael@0: inStream->Seek(0, STREAM_SEEK_SET, NULL); michael@0: const CArchiverInfo &archiverInfo = archiverInfoList[orderIndices[i]]; michael@0: #ifndef EXCLUDE_COM michael@0: CHandlerLoader loader; michael@0: #endif michael@0: CMyComPtr archive; michael@0: michael@0: #ifdef FORMAT_7Z michael@0: if (archiverInfo.Name.CompareNoCase(L"7z") == 0) michael@0: archive = new NArchive::N7z::CHandler; michael@0: #endif michael@0: michael@0: #ifdef FORMAT_BZIP2 michael@0: if (archiverInfo.Name.CompareNoCase(L"BZip2") == 0) michael@0: archive = new NArchive::NBZip2::CHandler; michael@0: #endif michael@0: michael@0: #ifdef FORMAT_GZIP michael@0: if (archiverInfo.Name.CompareNoCase(L"GZip") == 0) michael@0: archive = new NArchive::NGZip::CHandler; michael@0: #endif michael@0: michael@0: #ifdef FORMAT_SPLIT michael@0: if (archiverInfo.Name.CompareNoCase(L"Split") == 0) michael@0: archive = new NArchive::NSplit::CHandler; michael@0: #endif michael@0: michael@0: #ifdef FORMAT_TAR michael@0: if (archiverInfo.Name.CompareNoCase(L"Tar") == 0) michael@0: archive = new NArchive::NTar::CHandler; michael@0: #endif michael@0: michael@0: #ifdef FORMAT_ZIP michael@0: if (archiverInfo.Name.CompareNoCase(L"Zip") == 0) michael@0: archive = new NArchive::NZip::CHandler; michael@0: #endif michael@0: michael@0: #ifdef FORMAT_Z michael@0: if (archiverInfo.Name.CompareNoCase(L"Z") == 0) michael@0: archive = new NArchive::NZ::CHandler; michael@0: #endif michael@0: michael@0: michael@0: #ifndef EXCLUDE_COM michael@0: if (!archive) michael@0: { michael@0: HRESULT result = loader.CreateHandler(archiverInfo.FilePath, michael@0: archiverInfo.ClassID, (void **)&archive, false); michael@0: if (result != S_OK) michael@0: continue; michael@0: } michael@0: #endif michael@0: michael@0: if (!archive) michael@0: return E_FAIL; michael@0: michael@0: HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback); michael@0: if(result == S_FALSE) michael@0: continue; michael@0: if(result != S_OK) michael@0: { michael@0: badResult = result; michael@0: if(result == E_ABORT) michael@0: break; michael@0: continue; michael@0: } michael@0: *archiveResult = archive.Detach(); michael@0: #ifndef EXCLUDE_COM michael@0: *module = loader.Detach(); michael@0: #endif michael@0: archiverInfoResult = archiverInfo; michael@0: int subExtIndex = archiverInfo.FindExtension(extension); michael@0: if (subExtIndex < 0) michael@0: subExtIndex = 0; michael@0: defaultItemName = GetDefaultName2(fileName, michael@0: archiverInfo.Extensions[subExtIndex].Ext, michael@0: archiverInfo.Extensions[subExtIndex].AddExt); michael@0: michael@0: return S_OK; michael@0: } michael@0: if (badResult != S_OK) michael@0: return badResult; michael@0: return S_FALSE; michael@0: } michael@0: michael@0: HRESULT OpenArchive(const UString &filePath, michael@0: #ifndef EXCLUDE_COM michael@0: HMODULE *module, michael@0: #endif michael@0: IInArchive **archiveResult, michael@0: CArchiverInfo &archiverInfo, michael@0: UString &defaultItemName, michael@0: IArchiveOpenCallback *openArchiveCallback) michael@0: { michael@0: CInFileStream *inStreamSpec = new CInFileStream; michael@0: CMyComPtr inStream(inStreamSpec); michael@0: if (!inStreamSpec->Open(filePath)) michael@0: return GetLastError(); michael@0: return OpenArchive(inStream, ExtractFileNameFromPath(filePath), michael@0: #ifndef EXCLUDE_COM michael@0: module, michael@0: #endif michael@0: archiveResult, archiverInfo, michael@0: defaultItemName, openArchiveCallback); michael@0: } michael@0: michael@0: static void MakeDefaultName(UString &name) michael@0: { michael@0: int dotPos = name.ReverseFind(L'.'); michael@0: if (dotPos < 0) michael@0: return; michael@0: UString ext = name.Mid(dotPos + 1); michael@0: if (ext.IsEmpty()) michael@0: return; michael@0: for (int pos = 0; pos < ext.Length(); pos++) michael@0: if (ext[pos] < L'0' || ext[pos] > L'9') michael@0: return; michael@0: name = name.Left(dotPos); michael@0: } michael@0: michael@0: HRESULT OpenArchive(const UString &fileName, michael@0: #ifndef EXCLUDE_COM michael@0: HMODULE *module0, michael@0: HMODULE *module1, michael@0: #endif michael@0: IInArchive **archive0, michael@0: IInArchive **archive1, michael@0: CArchiverInfo &archiverInfo0, michael@0: CArchiverInfo &archiverInfo1, michael@0: UString &defaultItemName0, michael@0: UString &defaultItemName1, michael@0: IArchiveOpenCallback *openArchiveCallback) michael@0: { michael@0: HRESULT result = OpenArchive(fileName, michael@0: #ifndef EXCLUDE_COM michael@0: module0, michael@0: #endif michael@0: archive0, archiverInfo0, defaultItemName0, openArchiveCallback); michael@0: RINOK(result); michael@0: CMyComPtr getStream; michael@0: result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream); michael@0: if (result != S_OK || getStream == 0) michael@0: return S_OK; michael@0: michael@0: CMyComPtr subSeqStream; michael@0: result = getStream->GetStream(0, &subSeqStream); michael@0: if (result != S_OK) michael@0: return S_OK; michael@0: michael@0: CMyComPtr subStream; michael@0: if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK) michael@0: return S_OK; michael@0: if (!subStream) michael@0: return S_OK; michael@0: michael@0: UInt32 numItems; michael@0: RINOK((*archive0)->GetNumberOfItems(&numItems)); michael@0: if (numItems < 1) michael@0: return S_OK; michael@0: michael@0: UString subPath; michael@0: RINOK(GetArchiveItemPath(*archive0, 0, subPath)) michael@0: if (subPath.IsEmpty()) michael@0: { michael@0: MakeDefaultName(defaultItemName0); michael@0: subPath = defaultItemName0; michael@0: if (archiverInfo0.Name.CompareNoCase(L"7z") == 0) michael@0: { michael@0: if (subPath.Right(3).CompareNoCase(L".7z") != 0) michael@0: subPath += L".7z"; michael@0: } michael@0: } michael@0: else michael@0: subPath = ExtractFileNameFromPath(subPath); michael@0: michael@0: CMyComPtr setSubArchiveName; michael@0: openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); michael@0: if (setSubArchiveName) michael@0: setSubArchiveName->SetSubArchiveName(subPath); michael@0: michael@0: result = OpenArchive(subStream, subPath, michael@0: #ifndef EXCLUDE_COM michael@0: module1, michael@0: #endif michael@0: archive1, archiverInfo1, defaultItemName1, openArchiveCallback); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT MyOpenArchive(const UString &archiveName, michael@0: #ifndef EXCLUDE_COM michael@0: HMODULE *module, michael@0: #endif michael@0: IInArchive **archive, michael@0: UString &defaultItemName, michael@0: IOpenCallbackUI *openCallbackUI) michael@0: { michael@0: COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; michael@0: CMyComPtr openCallback = openCallbackSpec; michael@0: openCallbackSpec->Callback = openCallbackUI; michael@0: michael@0: UString fullName; michael@0: int fileNamePartStartIndex; michael@0: NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex); michael@0: openCallbackSpec->Init( michael@0: fullName.Left(fileNamePartStartIndex), michael@0: fullName.Mid(fileNamePartStartIndex)); michael@0: michael@0: CArchiverInfo archiverInfo; michael@0: return OpenArchive(archiveName, michael@0: #ifndef EXCLUDE_COM michael@0: module, michael@0: #endif michael@0: archive, michael@0: archiverInfo, michael@0: defaultItemName, michael@0: openCallback); michael@0: } michael@0: michael@0: HRESULT MyOpenArchive(const UString &archiveName, michael@0: #ifndef EXCLUDE_COM michael@0: HMODULE *module0, michael@0: HMODULE *module1, michael@0: #endif michael@0: IInArchive **archive0, michael@0: IInArchive **archive1, michael@0: UString &defaultItemName0, michael@0: UString &defaultItemName1, michael@0: UStringVector &volumePaths, michael@0: IOpenCallbackUI *openCallbackUI) michael@0: { michael@0: COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; michael@0: CMyComPtr openCallback = openCallbackSpec; michael@0: openCallbackSpec->Callback = openCallbackUI; michael@0: michael@0: UString fullName; michael@0: int fileNamePartStartIndex; michael@0: NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex); michael@0: UString prefix = fullName.Left(fileNamePartStartIndex); michael@0: UString name = fullName.Mid(fileNamePartStartIndex); michael@0: openCallbackSpec->Init(prefix, name); michael@0: michael@0: CArchiverInfo archiverInfo0, archiverInfo1; michael@0: HRESULT result = OpenArchive(archiveName, michael@0: #ifndef EXCLUDE_COM michael@0: module0, michael@0: module1, michael@0: #endif michael@0: archive0, michael@0: archive1, michael@0: archiverInfo0, michael@0: archiverInfo1, michael@0: defaultItemName0, michael@0: defaultItemName1, michael@0: openCallback); michael@0: RINOK(result); michael@0: volumePaths.Add(prefix + name); michael@0: for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++) michael@0: volumePaths.Add(prefix + openCallbackSpec->FileNames[i]); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT CArchiveLink::Close() michael@0: { michael@0: if (Archive1 != 0) michael@0: RINOK(Archive1->Close()); michael@0: if (Archive0 != 0) michael@0: RINOK(Archive0->Close()); michael@0: return S_OK; michael@0: } michael@0: michael@0: void CArchiveLink::Release() michael@0: { michael@0: if (Archive1 != 0) michael@0: Archive1.Release(); michael@0: if (Archive0 != 0) michael@0: Archive0.Release(); michael@0: #ifndef EXCLUDE_COM michael@0: Library1.Free(); michael@0: Library0.Free(); michael@0: #endif michael@0: } michael@0: michael@0: HRESULT OpenArchive(const UString &archiveName, michael@0: CArchiveLink &archiveLink, michael@0: IArchiveOpenCallback *openCallback) michael@0: { michael@0: return OpenArchive(archiveName, michael@0: #ifndef EXCLUDE_COM michael@0: &archiveLink.Library0, &archiveLink.Library1, michael@0: #endif michael@0: &archiveLink.Archive0, &archiveLink.Archive1, michael@0: archiveLink.ArchiverInfo0, archiveLink.ArchiverInfo1, michael@0: archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, michael@0: openCallback); michael@0: } michael@0: michael@0: HRESULT MyOpenArchive(const UString &archiveName, michael@0: CArchiveLink &archiveLink, michael@0: IOpenCallbackUI *openCallbackUI) michael@0: { michael@0: return MyOpenArchive(archiveName, michael@0: #ifndef EXCLUDE_COM michael@0: &archiveLink.Library0, &archiveLink.Library1, michael@0: #endif michael@0: &archiveLink.Archive0, &archiveLink.Archive1, michael@0: archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, michael@0: archiveLink.VolumePaths, michael@0: openCallbackUI); michael@0: } michael@0: michael@0: HRESULT ReOpenArchive(CArchiveLink &archiveLink, michael@0: const UString &fileName) michael@0: { michael@0: if (archiveLink.GetNumLevels() > 1) michael@0: return E_NOTIMPL; michael@0: if (archiveLink.GetNumLevels() == 0) michael@0: return MyOpenArchive(fileName, archiveLink, 0); michael@0: return ReOpenArchive(archiveLink.GetArchive(), fileName); michael@0: }