// 7zHandler.cpp #include "StdAfx.h" #include "7zHandler.h" #include "7zProperties.h" #include "../../../Common/IntToString.h" #include "../../../Common/ComTry.h" #include "../../../Windows/Defs.h" #include "../Common/ItemNameUtils.h" #ifdef _7Z_VOL #include "../Common/MultiStream.h" #endif #ifdef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY #include "../Common/ParseProperties.h" #endif #endif using namespace NWindows; namespace NArchive { namespace N7z { CHandler::CHandler() { #ifdef COMPRESS_MT _numThreads = NWindows::NSystem::GetNumberOfProcessors(); #endif #ifndef EXTRACT_ONLY Init(); #endif #ifndef EXCLUDE_COM LoadMethodMap(); #endif } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { COM_TRY_BEGIN *numItems = #ifdef _7Z_VOL _refs.Size(); #else *numItems = _database.Files.Size(); #endif return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { value->vt = VT_EMPTY; return S_OK; } #ifdef _SFX STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) { return E_NOTIMPL; } STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) { return E_NOTIMPL; } #endif STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) { *numProperties = 0; return S_OK; } STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) { return E_NOTIMPL; } static void MySetFileTime(bool timeDefined, FILETIME unixTime, NWindows::NCOM::CPropVariant &propVariant) { if (timeDefined) propVariant = unixTime; } /* inline static wchar_t GetHex(Byte value) { return (value < 10) ? ('0' + value) : ('A' + (value - 10)); } static UString ConvertBytesToHexString(const Byte *data, UInt32 size) { UString result; for (UInt32 i = 0; i < size; i++) { Byte b = data[i]; result += GetHex(b >> 4); result += GetHex(b & 0xF); } return result; } */ #ifndef _SFX static UString ConvertUInt32ToString(UInt32 value) { wchar_t buffer[32]; ConvertUInt64ToString(value, buffer); return buffer; } static UString GetStringForSizeValue(UInt32 value) { for (int i = 31; i >= 0; i--) if ((UInt32(1) << i) == value) return ConvertUInt32ToString(i); UString result; if (value % (1 << 20) == 0) { result += ConvertUInt32ToString(value >> 20); result += L"m"; } else if (value % (1 << 10) == 0) { result += ConvertUInt32ToString(value >> 10); result += L"k"; } else { result += ConvertUInt32ToString(value); result += L"b"; } return result; } static CMethodID k_Copy = { { 0x0 }, 1 }; static CMethodID k_LZMA = { { 0x3, 0x1, 0x1 }, 3 }; static CMethodID k_BCJ = { { 0x3, 0x3, 0x1, 0x3 }, 4 }; static CMethodID k_BCJ2 = { { 0x3, 0x3, 0x1, 0x1B }, 4 }; static CMethodID k_PPMD = { { 0x3, 0x4, 0x1 }, 3 }; static CMethodID k_Deflate = { { 0x4, 0x1, 0x8 }, 3 }; static CMethodID k_BZip2 = { { 0x4, 0x2, 0x2 }, 3 }; static inline char GetHex(Byte value) { return (value < 10) ? ('0' + value) : ('A' + (value - 10)); } static inline UString GetHex2(Byte value) { UString result; result += GetHex(value >> 4); result += GetHex(value & 0xF); return result; } #endif static inline UInt32 GetUInt32FromMemLE(const Byte *p) { return p[0] | (((UInt32)p[1]) << 8) | (((UInt32)p[2]) << 16) | (((UInt32)p[3]) << 24); } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant propVariant; /* const CRef2 &ref2 = _refs[index]; if (ref2.Refs.IsEmpty()) return E_FAIL; const CRef &ref = ref2.Refs.Front(); */ #ifdef _7Z_VOL const CRef &ref = _refs[index]; const CVolume &volume = _volumes[ref.VolumeIndex]; const CArchiveDatabaseEx &_database = volume.Database; UInt32 index2 = ref.ItemIndex; const CFileItem &item = _database.Files[index2]; #else const CFileItem &item = _database.Files[index]; UInt32 index2 = index; #endif switch(propID) { case kpidPath: { if (!item.Name.IsEmpty()) propVariant = NItemName::GetOSName(item.Name); break; } case kpidIsFolder: propVariant = item.IsDirectory; break; case kpidSize: { propVariant = item.UnPackSize; // propVariant = ref2.UnPackSize; break; } case kpidPosition: { /* if (ref2.Refs.Size() > 1) propVariant = ref2.StartPos; else */ if (item.IsStartPosDefined) propVariant = item.StartPos; break; } case kpidPackedSize: { // propVariant = ref2.PackSize; { CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2) propVariant = _database.GetFolderFullPackSize(folderIndex); /* else propVariant = UInt64(0); */ } else propVariant = UInt64(0); } break; } case kpidLastAccessTime: MySetFileTime(item.IsLastAccessTimeDefined, item.LastAccessTime, propVariant); break; case kpidCreationTime: MySetFileTime(item.IsCreationTimeDefined, item.CreationTime, propVariant); break; case kpidLastWriteTime: MySetFileTime(item.IsLastWriteTimeDefined, item.LastWriteTime, propVariant); break; case kpidAttributes: if (item.AreAttributesDefined) propVariant = item.Attributes; break; case kpidCRC: if (item.IsFileCRCDefined) propVariant = item.FileCRC; break; #ifndef _SFX case kpidMethod: { CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { const CFolder &folderInfo = _database.Folders[folderIndex]; UString methodsString; for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; if (!methodsString.IsEmpty()) methodsString += L' '; CMethodInfo methodInfo; bool methodIsKnown; for (int j = 0; j < coderInfo.AltCoders.Size(); j++) { if (j > 0) methodsString += L"|"; const CAltCoderInfo &altCoderInfo = coderInfo.AltCoders[j]; UString methodName; #ifdef NO_REGISTRY methodIsKnown = true; if (altCoderInfo.MethodID == k_Copy) methodName = L"Copy"; else if (altCoderInfo.MethodID == k_LZMA) methodName = L"LZMA"; else if (altCoderInfo.MethodID == k_BCJ) methodName = L"BCJ"; else if (altCoderInfo.MethodID == k_BCJ2) methodName = L"BCJ2"; else if (altCoderInfo.MethodID == k_PPMD) methodName = L"PPMD"; else if (altCoderInfo.MethodID == k_Deflate) methodName = L"Deflate"; else if (altCoderInfo.MethodID == k_BZip2) methodName = L"BZip2"; else methodIsKnown = false; #else methodIsKnown = GetMethodInfo( altCoderInfo.MethodID, methodInfo); methodName = methodInfo.Name; #endif if (methodIsKnown) { methodsString += methodName; if (altCoderInfo.MethodID == k_LZMA) { if (altCoderInfo.Properties.GetCapacity() >= 5) { methodsString += L":"; UInt32 dicSize = GetUInt32FromMemLE( ((const Byte *)altCoderInfo.Properties + 1)); methodsString += GetStringForSizeValue(dicSize); } } else if (altCoderInfo.MethodID == k_PPMD) { if (altCoderInfo.Properties.GetCapacity() >= 5) { Byte order = *(const Byte *)altCoderInfo.Properties; methodsString += L":o"; methodsString += ConvertUInt32ToString(order); methodsString += L":mem"; UInt32 dicSize = GetUInt32FromMemLE( ((const Byte *)altCoderInfo.Properties + 1)); methodsString += GetStringForSizeValue(dicSize); } } else { if (altCoderInfo.Properties.GetCapacity() > 0) { methodsString += L":["; for (size_t bi = 0; bi < altCoderInfo.Properties.GetCapacity(); bi++) { if (bi > 2 && bi + 1 < altCoderInfo.Properties.GetCapacity()) { methodsString += L".."; break; } else methodsString += GetHex2(altCoderInfo.Properties[bi]); } methodsString += L"]"; } } } else { methodsString += altCoderInfo.MethodID.ConvertToString(); } } } propVariant = methodsString; } } break; case kpidBlock: { CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) propVariant = (UInt32)folderIndex; } break; case kpidPackedSize0: case kpidPackedSize1: case kpidPackedSize2: case kpidPackedSize3: case kpidPackedSize4: { CNum folderIndex = _database.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { const CFolder &folderInfo = _database.Folders[folderIndex]; if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2 && folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0)) { propVariant = _database.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0); } else propVariant = UInt64(0); } else propVariant = UInt64(0); } break; #endif case kpidIsAnti: propVariant = item.IsAnti; break; } propVariant.Detach(value); return S_OK; COM_TRY_END } static const wchar_t *kExt = L"7z"; static const wchar_t *kAfterPart = L".7z"; #ifdef _7Z_VOL class CVolumeName { bool _first; UString _unchangedPart; UString _changedPart; UString _afterPart; public: bool InitName(const UString &name) { _first = true; int dotPos = name.ReverseFind('.'); UString basePart = name; if (dotPos >= 0) { UString ext = name.Mid(dotPos + 1); if (ext.CompareNoCase(kExt)==0 || ext.CompareNoCase(L"EXE") == 0) { _afterPart = kAfterPart; basePart = name.Left(dotPos); } } int numLetters = 1; bool splitStyle = false; if (basePart.Right(numLetters) == L"1") { while (numLetters < basePart.Length()) { if (basePart[basePart.Length() - numLetters - 1] != '0') break; numLetters++; } } else return false; _unchangedPart = basePart.Left(basePart.Length() - numLetters); _changedPart = basePart.Right(numLetters); return true; } UString GetNextName() { UString newName; // if (_newStyle || !_first) { int i; int numLetters = _changedPart.Length(); for (i = numLetters - 1; i >= 0; i--) { wchar_t c = _changedPart[i]; if (c == L'9') { c = L'0'; newName = c + newName; if (i == 0) newName = UString(L'1') + newName; continue; } c++; newName = UString(c) + newName; i--; for (; i >= 0; i--) newName = _changedPart[i] + newName; break; } _changedPart = newName; } _first = false; return _unchangedPart + _changedPart + _afterPart; } }; #endif STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback) { COM_TRY_BEGIN Close(); #ifndef _SFX _fileInfoPopIDs.Clear(); #endif try { CMyComPtr openArchiveCallbackTemp = openArchiveCallback; #ifdef _7Z_VOL CVolumeName seqName; CMyComPtr openVolumeCallback; #endif #ifndef _NO_CRYPTO CMyComPtr getTextPassword; if (openArchiveCallback) { openArchiveCallbackTemp.QueryInterface( IID_ICryptoGetTextPassword, &getTextPassword); } #endif #ifdef _7Z_VOL if (openArchiveCallback) { openArchiveCallbackTemp.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback); } while(true) { CMyComPtr inStream; if (!_volumes.IsEmpty()) { if (!openVolumeCallback) break; if(_volumes.Size() == 1) { UString baseName; { NCOM::CPropVariant propVariant; RINOK(openVolumeCallback->GetProperty(kpidName, &propVariant)); if (propVariant.vt != VT_BSTR) break; baseName = propVariant.bstrVal; } seqName.InitName(baseName); } UString fullName = seqName.GetNextName(); HRESULT result = openVolumeCallback->GetStream(fullName, &inStream); if (result == S_FALSE) break; if (result != S_OK) return result; if (!stream) break; } else inStream = stream; CInArchive archive; RINOK(archive.Open(inStream, maxCheckStartPosition)); _volumes.Add(CVolume()); CVolume &volume = _volumes.Back(); CArchiveDatabaseEx &database = volume.Database; volume.Stream = inStream; volume.StartRef2Index = _refs.Size(); HRESULT result = archive.ReadDatabase(database #ifndef _NO_CRYPTO , getTextPassword #endif ); if (result != S_OK) { _volumes.Clear(); return result; } database.Fill(); for(int i = 0; i < database.Files.Size(); i++) { CRef refNew; refNew.VolumeIndex = _volumes.Size() - 1; refNew.ItemIndex = i; _refs.Add(refNew); /* const CFileItem &file = database.Files[i]; int j; */ /* for (j = _refs.Size() - 1; j >= 0; j--) { CRef2 &ref2 = _refs[j]; const CRef &ref = ref2.Refs.Back(); const CVolume &volume2 = _volumes[ref.VolumeIndex]; const CArchiveDatabaseEx &database2 = volume2.Database; const CFileItem &file2 = database2.Files[ref.ItemIndex]; if (file2.Name.CompareNoCase(file.Name) == 0) { if (!file.IsStartPosDefined) continue; if (file.StartPos != ref2.StartPos + ref2.UnPackSize) continue; ref2.Refs.Add(refNew); break; } } */ /* j = -1; if (j < 0) { CRef2 ref2New; ref2New.Refs.Add(refNew); j = _refs.Add(ref2New); } CRef2 &ref2 = _refs[j]; ref2.UnPackSize += file.UnPackSize; ref2.PackSize += database.GetFilePackSize(i); if (ref2.Refs.Size() == 1 && file.IsStartPosDefined) ref2.StartPos = file.StartPos; */ } if (database.Files.Size() != 1) break; const CFileItem &file = database.Files.Front(); if (!file.IsStartPosDefined) break; } #else CInArchive archive; RINOK(archive.Open(stream, maxCheckStartPosition)); HRESULT result = archive.ReadDatabase(_database #ifndef _NO_CRYPTO , getTextPassword #endif ); RINOK(result); _database.Fill(); _inStream = stream; #endif } catch(...) { Close(); return S_FALSE; } // _inStream = stream; #ifndef _SFX FillPopIDs(); #endif return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { COM_TRY_BEGIN #ifdef _7Z_VOL _volumes.Clear(); _refs.Clear(); #else _inStream.Release(); _database.Clear(); #endif return S_OK; COM_TRY_END } #ifdef _7Z_VOL STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { if (index != 0) return E_INVALIDARG; *stream = 0; CMultiStream *streamSpec = new CMultiStream; CMyComPtr streamTemp = streamSpec; UInt64 pos = 0; const UString *fileName; for (int i = 0; i < _refs.Size(); i++) { const CRef &ref = _refs[i]; const CVolume &volume = _volumes[ref.VolumeIndex]; const CArchiveDatabaseEx &database = volume.Database; const CFileItem &file = database.Files[ref.ItemIndex]; if (i == 0) fileName = &file.Name; else if (fileName->Compare(file.Name) != 0) return S_FALSE; if (!file.IsStartPosDefined) return S_FALSE; if (file.StartPos != pos) return S_FALSE; CNum folderIndex = database.FileIndexToFolderIndexMap[ref.ItemIndex]; if (folderIndex == kNumNoIndex) { if (file.UnPackSize != 0) return E_FAIL; continue; } if (database.NumUnPackStreamsVector[folderIndex] != 1) return S_FALSE; const CFolder &folder = database.Folders[folderIndex]; if (folder.Coders.Size() != 1) return S_FALSE; const CCoderInfo &coder = folder.Coders.Front(); if (coder.NumInStreams != 1 || coder.NumOutStreams != 1) return S_FALSE; const CAltCoderInfo &altCoder = coder.AltCoders.Front(); if (altCoder.MethodID.IDSize != 1 || altCoder.MethodID.ID[0] != 0) return S_FALSE; pos += file.UnPackSize; CMultiStream::CSubStreamInfo subStreamInfo; subStreamInfo.Stream = volume.Stream; subStreamInfo.Pos = database.GetFolderStreamPos(folderIndex, 0); subStreamInfo.Size = file.UnPackSize; streamSpec->Streams.Add(subStreamInfo); } streamSpec->Init(); *stream = streamTemp.Detach(); return S_OK; } #endif #ifdef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) { COM_TRY_BEGIN const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); _numThreads = numProcessors; for (int i = 0; i < numProperties; i++) { UString name = names[i]; name.MakeUpper(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; UInt32 number; int index = ParseStringToUInt32(name, number); if (index == 0) { if(name.Left(2).CompareNoCase(L"MT") == 0) { RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads)); continue; } else return E_INVALIDARG; } } return S_OK; COM_TRY_END } #endif #endif }}