// Client7z.cpp #include "StdAfx.h" #include <stdio.h> #include "../../../Common/MyWindows.h" #include "../../../Common/Defs.h" #include "../../../Common/MyInitGuid.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/DLL.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileName.h" #include "../../../Windows/NtCheck.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #include "../../Common/FileStreams.h" #include "../../Archive/IArchive.h" #include "../../IPassword.h" #include "../../../../C/7zVersion.h" #ifdef _WIN32 extern HINSTANCE g_hInstance; HINSTANCE g_hInstance = 0; #endif // You can find full list of all GUIDs supported by 7-Zip in Guid.txt file. // 7z format GUID: {23170F69-40C1-278A-1000-000110070000} #define DEFINE_GUID_ARC(name, id) DEFINE_GUID(name, \ 0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, id, 0x00, 0x00); enum { kId_Zip = 1, kId_BZip2 = 2, kId_7z = 7, kId_Xz = 0xC, kId_Tar = 0xEE, kId_GZip = 0xEF }; // use another id, if you want to support other formats (zip, Xz, ...). // DEFINE_GUID_ARC (CLSID_Format, kId_Zip) // DEFINE_GUID_ARC (CLSID_Format, kId_BZip2) // DEFINE_GUID_ARC (CLSID_Format, kId_Xz) // DEFINE_GUID_ARC (CLSID_Format, kId_Tar) // DEFINE_GUID_ARC (CLSID_Format, kId_GZip) DEFINE_GUID_ARC (CLSID_Format, kId_7z) using namespace NWindows; using namespace NFile; using namespace NDir; #ifdef _WIN32 #define kDllName "7z.dll" #else #define kDllName "7z.so" #endif static const char * const kCopyrightString = "\n" "7-Zip" " (" kDllName " client)" " " MY_VERSION " : " MY_COPYRIGHT_DATE "\n"; static const char * const kHelpString = "Usage: 7zcl.exe [a | l | x] archive.7z [fileName ...]\n" "Examples:\n" " 7zcl.exe a archive.7z f1.txt f2.txt : compress two files to archive.7z\n" " 7zcl.exe l archive.7z : List contents of archive.7z\n" " 7zcl.exe x archive.7z : eXtract files from archive.7z\n"; static void Convert_UString_to_AString(const UString &s, AString &temp) { int codePage = CP_OEMCP; /* int g_CodePage = -1; int codePage = g_CodePage; if (codePage == -1) codePage = CP_OEMCP; if (codePage == CP_UTF8) ConvertUnicodeToUTF8(s, temp); else */ UnicodeStringToMultiByte2(temp, s, (UINT)codePage); } static FString CmdStringToFString(const char *s) { return us2fs(GetUnicodeString(s)); } static void Print(const char *s) { fputs(s, stdout); } static void Print(const AString &s) { Print(s.Ptr()); } static void Print(const UString &s) { AString as; Convert_UString_to_AString(s, as); Print(as); } static void Print(const wchar_t *s) { Print(UString(s)); } static void PrintNewLine() { Print("\n"); } static void PrintStringLn(const char *s) { Print(s); PrintNewLine(); } static void PrintError(const char *message) { Print("Error: "); PrintNewLine(); Print(message); PrintNewLine(); } static void PrintError(const char *message, const FString &name) { PrintError(message); Print(name); } static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, propID, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt == VT_EMPTY) result = false; else return E_FAIL; return S_OK; } static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) { return IsArchiveItemProp(archive, index, kpidIsDir, result); } static const wchar_t * const kEmptyFileAlias = L"[Content]"; ////////////////////////////////////////////////////////////// // Archive Open callback class class CArchiveOpenCallback: public IArchiveOpenCallback, public ICryptoGetTextPassword, public CMyUnknownImp { public: MY_UNKNOWN_IMP1(ICryptoGetTextPassword) STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); STDMETHOD(CryptoGetTextPassword)(BSTR *password); bool PasswordIsDefined; UString Password; CArchiveOpenCallback() : PasswordIsDefined(false) {} }; STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */) { return S_OK; } STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */) { return S_OK; } STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password) { if (!PasswordIsDefined) { // You can ask real password here from user // Password = GetPassword(OutStream); // PasswordIsDefined = true; PrintError("Password is not defined"); return E_ABORT; } return StringToBstr(Password, password); } static const char * const kIncorrectCommand = "incorrect command"; ////////////////////////////////////////////////////////////// // Archive Extracting callback class static const char * const kTestingString = "Testing "; static const char * const kExtractingString = "Extracting "; static const char * const kSkippingString = "Skipping "; static const char * const kReadingString = "Reading "; static const char * const kUnsupportedMethod = "Unsupported Method"; static const char * const kCRCFailed = "CRC Failed"; static const char * const kDataError = "Data Error"; static const char * const kUnavailableData = "Unavailable data"; static const char * const kUnexpectedEnd = "Unexpected end of data"; static const char * const kDataAfterEnd = "There are some data after the end of the payload data"; static const char * const kIsNotArc = "Is not archive"; static const char * const kHeadersError = "Headers Error"; struct CArcTime { FILETIME FT; UInt16 Prec; Byte Ns100; bool Def; CArcTime() { Clear(); } void Clear() { FT.dwHighDateTime = FT.dwLowDateTime = 0; Prec = 0; Ns100 = 0; Def = false; } bool IsZero() const { return FT.dwLowDateTime == 0 && FT.dwHighDateTime == 0 && Ns100 == 0; } int GetNumDigits() const { if (Prec == k_PropVar_TimePrec_Unix || Prec == k_PropVar_TimePrec_DOS) return 0; if (Prec == k_PropVar_TimePrec_HighPrec) return 9; if (Prec == k_PropVar_TimePrec_0) return 7; int digits = (int)Prec - (int)k_PropVar_TimePrec_Base; if (digits < 0) digits = 0; return digits; } void Write_To_FiTime(CFiTime &dest) const { #ifdef _WIN32 dest = FT; #else if (FILETIME_To_timespec(FT, dest)) if ((Prec == k_PropVar_TimePrec_Base + 8 || Prec == k_PropVar_TimePrec_Base + 9) && Ns100 != 0) { dest.tv_nsec += Ns100; } #endif } void Set_From_Prop(const PROPVARIANT &prop) { FT = prop.filetime; unsigned prec = 0; unsigned ns100 = 0; const unsigned prec_Temp = prop.wReserved1; if (prec_Temp != 0 && prec_Temp <= k_PropVar_TimePrec_1ns && prop.wReserved3 == 0) { const unsigned ns100_Temp = prop.wReserved2; if (ns100_Temp < 100) { ns100 = ns100_Temp; prec = prec_Temp; } } Prec = (UInt16)prec; Ns100 = (Byte)ns100; Def = true; } }; class CArchiveExtractCallback: public IArchiveExtractCallback, public ICryptoGetTextPassword, public CMyUnknownImp { public: MY_UNKNOWN_IMP1(ICryptoGetTextPassword) // IProgress STDMETHOD(SetTotal)(UInt64 size); STDMETHOD(SetCompleted)(const UInt64 *completeValue); // IArchiveExtractCallback STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode); STDMETHOD(PrepareOperation)(Int32 askExtractMode); STDMETHOD(SetOperationResult)(Int32 resultEOperationResult); // ICryptoGetTextPassword STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword); private: CMyComPtr<IInArchive> _archiveHandler; FString _directoryPath; // Output directory UString _filePath; // name inside arcvhive FString _diskFilePath; // full path to file on disk bool _extractMode; struct CProcessedFileInfo { CArcTime MTime; UInt32 Attrib; bool isDir; bool Attrib_Defined; } _processedFileInfo; COutFileStream *_outFileStreamSpec; CMyComPtr<ISequentialOutStream> _outFileStream; public: void Init(IInArchive *archiveHandler, const FString &directoryPath); UInt64 NumErrors; bool PasswordIsDefined; UString Password; CArchiveExtractCallback() : PasswordIsDefined(false) {} }; void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const FString &directoryPath) { NumErrors = 0; _archiveHandler = archiveHandler; _directoryPath = directoryPath; NName::NormalizeDirPathPrefix(_directoryPath); } STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */) { return S_OK; } STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */) { return S_OK; } STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) { *outStream = 0; _outFileStream.Release(); { // Get Name NCOM::CPropVariant prop; RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop)); UString fullPath; if (prop.vt == VT_EMPTY) fullPath = kEmptyFileAlias; else { if (prop.vt != VT_BSTR) return E_FAIL; fullPath = prop.bstrVal; } _filePath = fullPath; } if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) return S_OK; { // Get Attrib NCOM::CPropVariant prop; RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop)); if (prop.vt == VT_EMPTY) { _processedFileInfo.Attrib = 0; _processedFileInfo.Attrib_Defined = false; } else { if (prop.vt != VT_UI4) return E_FAIL; _processedFileInfo.Attrib = prop.ulVal; _processedFileInfo.Attrib_Defined = true; } } RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir)); { _processedFileInfo.MTime.Clear(); // Get Modified Time NCOM::CPropVariant prop; RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop)); switch (prop.vt) { case VT_EMPTY: // _processedFileInfo.MTime = _utcMTimeDefault; break; case VT_FILETIME: _processedFileInfo.MTime.Set_From_Prop(prop); break; default: return E_FAIL; } } { // Get Size NCOM::CPropVariant prop; RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop)); UInt64 newFileSize; /* bool newFileSizeDefined = */ ConvertPropVariantToUInt64(prop, newFileSize); } { // Create folders for file int slashPos = _filePath.ReverseFind_PathSepar(); if (slashPos >= 0) CreateComplexDir(_directoryPath + us2fs(_filePath.Left(slashPos))); } FString fullProcessedPath = _directoryPath + us2fs(_filePath); _diskFilePath = fullProcessedPath; if (_processedFileInfo.isDir) { CreateComplexDir(fullProcessedPath); } else { NFind::CFileInfo fi; if (fi.Find(fullProcessedPath)) { if (!DeleteFileAlways(fullProcessedPath)) { PrintError("Cannot delete output file", fullProcessedPath); return E_ABORT; } } _outFileStreamSpec = new COutFileStream; CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS)) { PrintError("Cannot open output file", fullProcessedPath); return E_ABORT; } _outFileStream = outStreamLoc; *outStream = outStreamLoc.Detach(); } return S_OK; } STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) { _extractMode = false; switch (askExtractMode) { case NArchive::NExtract::NAskMode::kExtract: _extractMode = true; break; }; switch (askExtractMode) { case NArchive::NExtract::NAskMode::kExtract: Print(kExtractingString); break; case NArchive::NExtract::NAskMode::kTest: Print(kTestingString); break; case NArchive::NExtract::NAskMode::kSkip: Print(kSkippingString); break; case NArchive::NExtract::NAskMode::kReadExternal: Print(kReadingString); break; default: Print("??? "); break; }; Print(_filePath); return S_OK; } STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) { switch (operationResult) { case NArchive::NExtract::NOperationResult::kOK: break; default: { NumErrors++; Print(" : "); const char *s = NULL; switch (operationResult) { case NArchive::NExtract::NOperationResult::kUnsupportedMethod: s = kUnsupportedMethod; break; case NArchive::NExtract::NOperationResult::kCRCError: s = kCRCFailed; break; case NArchive::NExtract::NOperationResult::kDataError: s = kDataError; break; case NArchive::NExtract::NOperationResult::kUnavailable: s = kUnavailableData; break; case NArchive::NExtract::NOperationResult::kUnexpectedEnd: s = kUnexpectedEnd; break; case NArchive::NExtract::NOperationResult::kDataAfterEnd: s = kDataAfterEnd; break; case NArchive::NExtract::NOperationResult::kIsNotArc: s = kIsNotArc; break; case NArchive::NExtract::NOperationResult::kHeadersError: s = kHeadersError; break; } if (s) { Print("Error : "); Print(s); } else { char temp[16]; ConvertUInt32ToString(operationResult, temp); Print("Error #"); Print(temp); } } } if (_outFileStream) { if (_processedFileInfo.MTime.Def) { CFiTime ft; _processedFileInfo.MTime.Write_To_FiTime(ft); _outFileStreamSpec->SetMTime(&ft); } RINOK(_outFileStreamSpec->Close()); } _outFileStream.Release(); if (_extractMode && _processedFileInfo.Attrib_Defined) SetFileAttrib_PosixHighDetect(_diskFilePath, _processedFileInfo.Attrib); PrintNewLine(); return S_OK; } STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) { if (!PasswordIsDefined) { // You can ask real password here from user // Password = GetPassword(OutStream); // PasswordIsDefined = true; PrintError("Password is not defined"); return E_ABORT; } return StringToBstr(Password, password); } ////////////////////////////////////////////////////////////// // Archive Creating callback class struct CDirItem: public NWindows::NFile::NFind::CFileInfoBase { UString Path_For_Handler; FString FullPath; // for filesystem CDirItem(const NWindows::NFile::NFind::CFileInfo &fi): CFileInfoBase(fi) {} }; class CArchiveUpdateCallback: public IArchiveUpdateCallback2, public ICryptoGetTextPassword2, public CMyUnknownImp { public: MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2) // IProgress STDMETHOD(SetTotal)(UInt64 size); STDMETHOD(SetCompleted)(const UInt64 *completeValue); // IUpdateCallback2 STDMETHOD(GetUpdateItemInfo)(UInt32 index, Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive); STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream); STDMETHOD(SetOperationResult)(Int32 operationResult); STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size); STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream); STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); public: CRecordVector<UInt64> VolumesSizes; UString VolName; UString VolExt; FString DirPrefix; const CObjectVector<CDirItem> *DirItems; bool PasswordIsDefined; UString Password; bool AskPassword; bool m_NeedBeClosed; FStringVector FailedFiles; CRecordVector<HRESULT> FailedCodes; CArchiveUpdateCallback(): DirItems(NULL), PasswordIsDefined(false), AskPassword(false) {} ~CArchiveUpdateCallback() { Finilize(); } HRESULT Finilize(); void Init(const CObjectVector<CDirItem> *dirItems) { DirItems = dirItems; m_NeedBeClosed = false; FailedFiles.Clear(); FailedCodes.Clear(); } }; STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */) { return S_OK; } STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */) { return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */, Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive) { if (newData) *newData = BoolToInt(true); if (newProperties) *newProperties = BoolToInt(true); if (indexInArchive) *indexInArchive = (UInt32)(Int32)-1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; if (propID == kpidIsAnti) { prop = false; prop.Detach(value); return S_OK; } { const CDirItem &di = (*DirItems)[index]; switch (propID) { case kpidPath: prop = di.Path_For_Handler; break; case kpidIsDir: prop = di.IsDir(); break; case kpidSize: prop = di.Size; break; case kpidCTime: PropVariant_SetFrom_FiTime(prop, di.CTime); break; case kpidATime: PropVariant_SetFrom_FiTime(prop, di.ATime); break; case kpidMTime: PropVariant_SetFrom_FiTime(prop, di.MTime); break; case kpidAttrib: prop = (UInt32)di.GetWinAttrib(); break; case kpidPosixAttrib: prop = (UInt32)di.GetPosixAttrib(); break; } } prop.Detach(value); return S_OK; } HRESULT CArchiveUpdateCallback::Finilize() { if (m_NeedBeClosed) { PrintNewLine(); m_NeedBeClosed = false; } return S_OK; } static void GetStream2(const wchar_t *name) { Print("Compressing "); if (name[0] == 0) name = kEmptyFileAlias; Print(name); } STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) { RINOK(Finilize()); const CDirItem &dirItem = (*DirItems)[index]; GetStream2(dirItem.Path_For_Handler); if (dirItem.IsDir()) return S_OK; { CInFileStream *inStreamSpec = new CInFileStream; CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec); FString path = DirPrefix + dirItem.FullPath; if (!inStreamSpec->Open(path)) { DWORD sysError = ::GetLastError(); FailedCodes.Add(sysError); FailedFiles.Add(path); // if (systemError == ERROR_SHARING_VIOLATION) { PrintNewLine(); PrintError("WARNING: can't open file"); // Print(NError::MyFormatMessageW(systemError)); return S_FALSE; } // return sysError; } *inStream = inStreamLoc.Detach(); } return S_OK; } STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */) { m_NeedBeClosed = true; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) { if (VolumesSizes.Size() == 0) return S_FALSE; if (index >= (UInt32)VolumesSizes.Size()) index = VolumesSizes.Size() - 1; *size = VolumesSizes[index]; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) { wchar_t temp[16]; ConvertUInt32ToString(index + 1, temp); UString res = temp; while (res.Len() < 2) res.InsertAtFront(L'0'); UString fileName = VolName; fileName += '.'; fileName += res; fileName += VolExt; COutFileStream *streamSpec = new COutFileStream; CMyComPtr<ISequentialOutStream> streamLoc(streamSpec); if (!streamSpec->Create(us2fs(fileName), false)) return ::GetLastError(); *volumeStream = streamLoc.Detach(); return S_OK; } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { if (!PasswordIsDefined) { if (AskPassword) { // You can ask real password here from user // Password = GetPassword(OutStream); // PasswordIsDefined = true; PrintError("Password is not defined"); return E_ABORT; } } *passwordIsDefined = BoolToInt(PasswordIsDefined); return StringToBstr(Password, password); } // Main function #if defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE) #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1; #endif int MY_CDECL main(int numArgs, const char *args[]) { NT_CHECK #ifdef ENV_HAVE_LOCALE MY_SetLocale(); #endif PrintStringLn(kCopyrightString); if (numArgs < 2) { PrintStringLn(kHelpString); return 0; } FString dllPrefix; #ifdef _WIN32 dllPrefix = NDLL::GetModuleDirPrefix(); #else { AString s (args[0]); int sep = s.ReverseFind_PathSepar(); s.DeleteFrom(sep + 1); dllPrefix = s; } #endif NDLL::CLibrary lib; if (!lib.Load(dllPrefix + FTEXT(kDllName))) { PrintError("Cannot load 7-zip library"); return 1; } Func_CreateObject createObjectFunc = (Func_CreateObject)lib.GetProc("CreateObject"); if (!createObjectFunc) { PrintError("Cannot get CreateObject"); return 1; } char c = 0; UString password; bool passwordIsDefined = false; CObjectVector<FString> params; for (int curCmd = 1; curCmd < numArgs; curCmd++) { AString a(args[curCmd]); if (!a.IsEmpty()) { if (a[0] == '-') { if (!passwordIsDefined && a[1] == 'p') { password = GetUnicodeString(a.Ptr(2)); passwordIsDefined = true; continue; } } else { if (c) { params.Add(CmdStringToFString(a)); continue; } if (a.Len() == 1) { c = (char)MyCharLower_Ascii(a[0]); continue; } } } { PrintError(kIncorrectCommand); return 1; } } if (!c || params.Size() < 1) { PrintError(kIncorrectCommand); return 1; } const FString &archiveName = params[0]; if (c == 'a') { // create archive command if (params.Size() < 2) { PrintError(kIncorrectCommand); return 1; } CObjectVector<CDirItem> dirItems; { unsigned i; for (i = 1; i < params.Size(); i++) { const FString &name = params[i]; NFind::CFileInfo fi; if (!fi.Find(name)) { PrintError("Can't find file", name); return 1; } CDirItem di(fi); di.Path_For_Handler = fs2us(name); di.FullPath = name; dirItems.Add(di); } } COutFileStream *outFileStreamSpec = new COutFileStream; CMyComPtr<IOutStream> outFileStream = outFileStreamSpec; if (!outFileStreamSpec->Create(archiveName, false)) { PrintError("can't create archive file"); return 1; } CMyComPtr<IOutArchive> outArchive; if (createObjectFunc(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK) { PrintError("Cannot get class object"); return 1; } CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec); updateCallbackSpec->Init(&dirItems); updateCallbackSpec->PasswordIsDefined = passwordIsDefined; updateCallbackSpec->Password = password; /* { const wchar_t *names[] = { L"m", L"s", L"x" }; const unsigned kNumProps = ARRAY_SIZE(names); NCOM::CPropVariant values[kNumProps] = { L"lzma", false, // solid mode OFF (UInt32)9 // compression level = 9 - ultra }; CMyComPtr<ISetProperties> setProperties; outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties); if (!setProperties) { PrintError("ISetProperties unsupported"); return 1; } if (setProperties->SetProperties(names, values, kNumProps) != S_OK) { PrintError("SetProperties() error"); return 1; } } */ HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback); updateCallbackSpec->Finilize(); if (result != S_OK) { PrintError("Update Error"); return 1; } FOR_VECTOR (i, updateCallbackSpec->FailedFiles) { PrintNewLine(); PrintError("Error for file", updateCallbackSpec->FailedFiles[i]); } if (updateCallbackSpec->FailedFiles.Size() != 0) return 1; } else { if (params.Size() != 1) { PrintError(kIncorrectCommand); return 1; } bool listCommand; if (c == 'l') listCommand = true; else if (c == 'x') listCommand = false; else { PrintError(kIncorrectCommand); return 1; } CMyComPtr<IInArchive> archive; if (createObjectFunc(&CLSID_Format, &IID_IInArchive, (void **)&archive) != S_OK) { PrintError("Cannot get class object"); return 1; } CInFileStream *fileSpec = new CInFileStream; CMyComPtr<IInStream> file = fileSpec; if (!fileSpec->Open(archiveName)) { PrintError("Cannot open archive file", archiveName); return 1; } { CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback; CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec); openCallbackSpec->PasswordIsDefined = passwordIsDefined; openCallbackSpec->Password = password; const UInt64 scanSize = 1 << 23; if (archive->Open(file, &scanSize, openCallback) != S_OK) { PrintError("Cannot open file as archive", archiveName); return 1; } } if (listCommand) { // List command UInt32 numItems = 0; archive->GetNumberOfItems(&numItems); for (UInt32 i = 0; i < numItems; i++) { { // Get uncompressed size of file NCOM::CPropVariant prop; archive->GetProperty(i, kpidSize, &prop); char s[32]; ConvertPropVariantToShortString(prop, s); Print(s); Print(" "); } { // Get name of file NCOM::CPropVariant prop; archive->GetProperty(i, kpidPath, &prop); if (prop.vt == VT_BSTR) Print(prop.bstrVal); else if (prop.vt != VT_EMPTY) Print("ERROR!"); } PrintNewLine(); } } else { // Extract command CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback; CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec); extractCallbackSpec->Init(archive, FString()); // second parameter is output folder path extractCallbackSpec->PasswordIsDefined = passwordIsDefined; extractCallbackSpec->Password = password; /* const wchar_t *names[] = { L"mt", L"mtf" }; const unsigned kNumProps = sizeof(names) / sizeof(names[0]); NCOM::CPropVariant values[kNumProps] = { (UInt32)1, false }; CMyComPtr<ISetProperties> setProperties; archive->QueryInterface(IID_ISetProperties, (void **)&setProperties); if (setProperties) { if (setProperties->SetProperties(names, values, kNumProps) != S_OK) { PrintError("SetProperties() error"); return 1; } } */ HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback); if (result != S_OK) { PrintError("Extract Error"); return 1; } } } return 0; }