00001
00002
00003
00004
00005 #include "StdAfx.h"
00006
00007 #include "OpenArchive.h"
00008
00009 #include "Common/Wildcard.h"
00010
00011 #include "Windows/FileName.h"
00012 #include "Windows/FileDir.h"
00013 #include "Windows/Defs.h"
00014 #include "Windows/PropVariant.h"
00015
00016 #include "../../Common/FileStreams.h"
00017 #include "../../Common/StreamUtils.h"
00018
00019 #include "Common/StringConvert.h"
00020
00021 #ifdef FORMAT_7Z
00022 #include "../../Archive/7z/7zHandler.h"
00023 #endif
00024
00025 #ifdef FORMAT_BZIP2
00026 #include "../../Archive/BZip2/BZip2Handler.h"
00027 #endif
00028
00029 #ifdef FORMAT_GZIP
00030 #include "../../Archive/GZip/GZipHandler.h"
00031 #endif
00032
00033 #ifdef FORMAT_SPLIT
00034 #include "../../Archive/Split/SplitHandler.h"
00035 #endif
00036
00037 #ifdef FORMAT_TAR
00038 #include "../../Archive/Tar/TarHandler.h"
00039 #endif
00040
00041 #ifdef FORMAT_ZIP
00042 #include "../../Archive/Zip/ZipHandler.h"
00043 #endif
00044
00045 #ifdef FORMAT_Z
00046 #include "../../Archive/Z/ZHandler.h"
00047 #endif
00048
00049 #ifndef EXCLUDE_COM
00050 #include "HandlerLoader.h"
00051 #endif
00052
00053 #include "DefaultName.h"
00054
00055 using namespace NWindows;
00056
00057 HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result)
00058 {
00059 NCOM::CPropVariant prop;
00060 RINOK(archive->GetProperty(index, kpidPath, &prop));
00061 if(prop.vt == VT_BSTR)
00062 result = prop.bstrVal;
00063 else if (prop.vt == VT_EMPTY)
00064 result.Empty();
00065 else
00066 return E_FAIL;
00067 return S_OK;
00068 }
00069
00070 HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result)
00071 {
00072 RINOK(GetArchiveItemPath(archive, index, result));
00073 if (result.IsEmpty())
00074 result = defaultName;
00075 return S_OK;
00076 }
00077
00078 HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index,
00079 const FILETIME &defaultFileTime, FILETIME &fileTime)
00080 {
00081 NCOM::CPropVariant prop;
00082 RINOK(archive->GetProperty(index, kpidLastWriteTime, &prop));
00083 if (prop.vt == VT_FILETIME)
00084 fileTime = prop.filetime;
00085 else if (prop.vt == VT_EMPTY)
00086 fileTime = defaultFileTime;
00087 else
00088 return E_FAIL;
00089 return S_OK;
00090 }
00091
00092 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
00093 {
00094 NCOM::CPropVariant prop;
00095 RINOK(archive->GetProperty(index, propID, &prop));
00096 if(prop.vt == VT_BOOL)
00097 result = VARIANT_BOOLToBool(prop.boolVal);
00098 else if (prop.vt == VT_EMPTY)
00099 result = false;
00100 else
00101 return E_FAIL;
00102 return S_OK;
00103 }
00104
00105 HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
00106 {
00107 return IsArchiveItemProp(archive, index, kpidIsFolder, result);
00108 }
00109
00110 HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result)
00111 {
00112 return IsArchiveItemProp(archive, index, kpidIsAnti, result);
00113 }
00114
00115 const UInt64 kMaxCheckStartPosition =
00116 #ifdef _WIN32
00117 1 << 20;
00118 #else
00119 1 << 22;
00120 #endif
00121
00122 HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName)
00123 {
00124 CInFileStream *inStreamSpec = new CInFileStream;
00125 CMyComPtr<IInStream> inStream(inStreamSpec);
00126 inStreamSpec->Open(fileName);
00127 return archive->Open(inStream, &kMaxCheckStartPosition, NULL);
00128 }
00129
00130 #ifndef _SFX
00131 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size)
00132 {
00133 for (size_t i = 0; i < size; i++)
00134 if (p1[i] != p2[i])
00135 return false;
00136 return true;
00137 }
00138 #endif
00139
00140 HRESULT OpenArchive(
00141 IInStream *inStream,
00142 const UString &fileName,
00143 #ifndef EXCLUDE_COM
00144 HMODULE *module,
00145 #endif
00146 IInArchive **archiveResult,
00147 CArchiverInfo &archiverInfoResult,
00148 UString &defaultItemName,
00149 IArchiveOpenCallback *openArchiveCallback)
00150 {
00151 *archiveResult = NULL;
00152 CObjectVector<CArchiverInfo> archiverInfoList;
00153 ReadArchiverInfoList(archiverInfoList);
00154 UString extension;
00155 {
00156 int dotPos = fileName.ReverseFind(L'.');
00157 if (dotPos >= 0)
00158 extension = fileName.Mid(dotPos + 1);
00159 }
00160 CIntVector orderIndices;
00161 int i;
00162 bool finded = false;
00163 for(i = 0; i < archiverInfoList.Size(); i++)
00164 {
00165 if (archiverInfoList[i].FindExtension(extension) >= 0)
00166 {
00167 orderIndices.Insert(0, i);
00168 finded = true;
00169 }
00170 else
00171 orderIndices.Add(i);
00172 }
00173
00174 #ifndef _SFX
00175 if (!finded)
00176 {
00177 CByteBuffer byteBuffer;
00178 const UInt32 kBufferSize = (200 << 10);
00179 byteBuffer.SetCapacity(kBufferSize);
00180 Byte *buffer = byteBuffer;
00181 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
00182 UInt32 processedSize;
00183 RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize));
00184 int numFinded = 0;
00185 for (int pos = (int)processedSize; pos >= 0 ; pos--)
00186 {
00187 for(int i = numFinded; i < orderIndices.Size(); i++)
00188 {
00189 int index = orderIndices[i];
00190 const CArchiverInfo &ai = archiverInfoList[index];
00191 const CByteBuffer &sig = ai.StartSignature;
00192 if (sig.GetCapacity() == 0)
00193 continue;
00194 if (pos + sig.GetCapacity() > processedSize)
00195 continue;
00196 if (TestSignature(buffer + pos, sig, sig.GetCapacity()))
00197 {
00198 orderIndices.Delete(i);
00199 orderIndices.Insert(0, index);
00200 numFinded++;
00201 }
00202 }
00203 }
00204 }
00205 #endif
00206
00207 HRESULT badResult = S_OK;
00208 for(i = 0; i < orderIndices.Size(); i++)
00209 {
00210 inStream->Seek(0, STREAM_SEEK_SET, NULL);
00211 const CArchiverInfo &archiverInfo = archiverInfoList[orderIndices[i]];
00212 #ifndef EXCLUDE_COM
00213 CHandlerLoader loader;
00214 #endif
00215 CMyComPtr<IInArchive> archive;
00216
00217 #ifdef FORMAT_7Z
00218 if (archiverInfo.Name.CompareNoCase(L"7z") == 0)
00219 archive = new NArchive::N7z::CHandler;
00220 #endif
00221
00222 #ifdef FORMAT_BZIP2
00223 if (archiverInfo.Name.CompareNoCase(L"BZip2") == 0)
00224 archive = new NArchive::NBZip2::CHandler;
00225 #endif
00226
00227 #ifdef FORMAT_GZIP
00228 if (archiverInfo.Name.CompareNoCase(L"GZip") == 0)
00229 archive = new NArchive::NGZip::CHandler;
00230 #endif
00231
00232 #ifdef FORMAT_SPLIT
00233 if (archiverInfo.Name.CompareNoCase(L"Split") == 0)
00234 archive = new NArchive::NSplit::CHandler;
00235 #endif
00236
00237 #ifdef FORMAT_TAR
00238 if (archiverInfo.Name.CompareNoCase(L"Tar") == 0)
00239 archive = new NArchive::NTar::CHandler;
00240 #endif
00241
00242 #ifdef FORMAT_ZIP
00243 if (archiverInfo.Name.CompareNoCase(L"Zip") == 0)
00244 archive = new NArchive::NZip::CHandler;
00245 #endif
00246
00247 #ifdef FORMAT_Z
00248 if (archiverInfo.Name.CompareNoCase(L"Z") == 0)
00249 archive = new NArchive::NZ::CHandler;
00250 #endif
00251
00252 #ifndef EXCLUDE_COM
00253 if (!archive)
00254 {
00255 HRESULT result = loader.CreateHandler(archiverInfo.FilePath,
00256 archiverInfo.ClassID, (void **)&archive, false);
00257 if (result != S_OK)
00258 continue;
00259 }
00260 #endif EXCLUDE_COM
00261
00262 if (!archive)
00263 return E_FAIL;
00264
00265 HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback);
00266 if(result == S_FALSE)
00267 continue;
00268 if(result != S_OK)
00269 {
00270 badResult = result;
00271 if(result == E_ABORT)
00272 break;
00273 continue;
00274 }
00275 *archiveResult = archive.Detach();
00276 #ifndef EXCLUDE_COM
00277 *module = loader.Detach();
00278 #endif
00279 archiverInfoResult = archiverInfo;
00280 int subExtIndex = archiverInfo.FindExtension(extension);
00281 if (subExtIndex < 0)
00282 subExtIndex = 0;
00283 defaultItemName = GetDefaultName2(fileName,
00284 archiverInfo.Extensions[subExtIndex].Ext,
00285 archiverInfo.Extensions[subExtIndex].AddExt);
00286
00287 return S_OK;
00288 }
00289 if (badResult != S_OK)
00290 return badResult;
00291 return S_FALSE;
00292 }
00293
00294 HRESULT OpenArchive(const UString &filePath,
00295 #ifndef EXCLUDE_COM
00296 HMODULE *module,
00297 #endif
00298 IInArchive **archiveResult,
00299 CArchiverInfo &archiverInfo,
00300 UString &defaultItemName,
00301 IArchiveOpenCallback *openArchiveCallback)
00302 {
00303 CInFileStream *inStreamSpec = new CInFileStream;
00304 CMyComPtr<IInStream> inStream(inStreamSpec);
00305 if (!inStreamSpec->Open(filePath))
00306 return GetLastError();
00307 return OpenArchive(inStream, ExtractFileNameFromPath(filePath),
00308 #ifndef EXCLUDE_COM
00309 module,
00310 #endif
00311 archiveResult, archiverInfo,
00312 defaultItemName, openArchiveCallback);
00313 }
00314
00315 static void MakeDefaultName(UString &name)
00316 {
00317 int dotPos = name.ReverseFind(L'.');
00318 if (dotPos < 0)
00319 return;
00320 UString ext = name.Mid(dotPos + 1);
00321 if (ext.IsEmpty())
00322 return;
00323 for (int pos = 0; pos < ext.Length(); pos++)
00324 if (ext[pos] < L'0' || ext[pos] > L'9')
00325 return;
00326 name = name.Left(dotPos);
00327 }
00328
00329 HRESULT OpenArchive(const UString &fileName,
00330 #ifndef EXCLUDE_COM
00331 HMODULE *module0,
00332 HMODULE *module1,
00333 #endif
00334 IInArchive **archive0,
00335 IInArchive **archive1,
00336 CArchiverInfo &archiverInfo0,
00337 CArchiverInfo &archiverInfo1,
00338 UString &defaultItemName0,
00339 UString &defaultItemName1,
00340 IArchiveOpenCallback *openArchiveCallback)
00341 {
00342 HRESULT result = OpenArchive(fileName,
00343 #ifndef EXCLUDE_COM
00344 module0,
00345 #endif
00346 archive0, archiverInfo0, defaultItemName0, openArchiveCallback);
00347 RINOK(result);
00348 CMyComPtr<IInArchiveGetStream> getStream;
00349 result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream);
00350 if (result != S_OK || getStream == 0)
00351 return S_OK;
00352
00353 CMyComPtr<ISequentialInStream> subSeqStream;
00354 result = getStream->GetStream(0, &subSeqStream);
00355 if (result != S_OK)
00356 return S_OK;
00357
00358 CMyComPtr<IInStream> subStream;
00359 if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK)
00360 return S_OK;
00361 if (!subStream)
00362 return S_OK;
00363
00364 UInt32 numItems;
00365 RINOK((*archive0)->GetNumberOfItems(&numItems));
00366 if (numItems < 1)
00367 return S_OK;
00368
00369 UString subPath;
00370 RINOK(GetArchiveItemPath(*archive0, 0, subPath))
00371 if (subPath.IsEmpty())
00372 {
00373 MakeDefaultName(defaultItemName0);
00374 subPath = defaultItemName0;
00375 if (archiverInfo0.Name.CompareNoCase(L"7z") == 0)
00376 {
00377 if (subPath.Right(3).CompareNoCase(L".7z") != 0)
00378 subPath += L".7z";
00379 }
00380 }
00381 else
00382 subPath = ExtractFileNameFromPath(subPath);
00383
00384 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
00385 openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
00386 if (setSubArchiveName)
00387 setSubArchiveName->SetSubArchiveName(subPath);
00388
00389 result = OpenArchive(subStream, subPath,
00390 #ifndef EXCLUDE_COM
00391 module1,
00392 #endif
00393 archive1, archiverInfo1, defaultItemName1, openArchiveCallback);
00394 return S_OK;
00395 }
00396
00397 HRESULT MyOpenArchive(const UString &archiveName,
00398 #ifndef EXCLUDE_COM
00399 HMODULE *module,
00400 #endif
00401 IInArchive **archive,
00402 UString &defaultItemName,
00403 IOpenCallbackUI *openCallbackUI)
00404 {
00405 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
00406 CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec;
00407 openCallbackSpec->Callback = openCallbackUI;
00408
00409 UString fullName;
00410 int fileNamePartStartIndex;
00411 NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
00412 openCallbackSpec->Init(
00413 fullName.Left(fileNamePartStartIndex),
00414 fullName.Mid(fileNamePartStartIndex));
00415
00416 CArchiverInfo archiverInfo;
00417 return OpenArchive(archiveName,
00418 #ifndef EXCLUDE_COM
00419 module,
00420 #endif
00421 archive,
00422 archiverInfo,
00423 defaultItemName,
00424 openCallback);
00425 }
00426
00427 HRESULT MyOpenArchive(const UString &archiveName,
00428 #ifndef EXCLUDE_COM
00429 HMODULE *module0,
00430 HMODULE *module1,
00431 #endif
00432 IInArchive **archive0,
00433 IInArchive **archive1,
00434 UString &defaultItemName0,
00435 UString &defaultItemName1,
00436 UStringVector &volumePaths,
00437 IOpenCallbackUI *openCallbackUI)
00438 {
00439 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
00440 CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec;
00441 openCallbackSpec->Callback = openCallbackUI;
00442
00443 UString fullName;
00444 int fileNamePartStartIndex;
00445 NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
00446 UString prefix = fullName.Left(fileNamePartStartIndex);
00447 UString name = fullName.Mid(fileNamePartStartIndex);
00448 openCallbackSpec->Init(prefix, name);
00449
00450 CArchiverInfo archiverInfo0, archiverInfo1;
00451 HRESULT result = OpenArchive(archiveName,
00452 #ifndef EXCLUDE_COM
00453 module0,
00454 module1,
00455 #endif
00456 archive0,
00457 archive1,
00458 archiverInfo0,
00459 archiverInfo1,
00460 defaultItemName0,
00461 defaultItemName1,
00462 openCallback);
00463 RINOK(result);
00464 volumePaths.Add(prefix + name);
00465 for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++)
00466 volumePaths.Add(prefix + openCallbackSpec->FileNames[i]);
00467 return S_OK;
00468 }
00469
00470 HRESULT CArchiveLink::Close()
00471 {
00472 if (Archive1 != 0)
00473 RINOK(Archive1->Close());
00474 if (Archive0 != 0)
00475 RINOK(Archive0->Close());
00476 return S_OK;
00477 }
00478
00479 void CArchiveLink::Release()
00480 {
00481 if (Archive1 != 0)
00482 Archive1.Release();
00483 if (Archive0 != 0)
00484 Archive0.Release();
00485 #ifndef EXCLUDE_COM
00486 Library1.Free();
00487 Library0.Free();
00488 #endif
00489 }
00490
00491 HRESULT OpenArchive(const UString &archiveName,
00492 CArchiveLink &archiveLink,
00493 IArchiveOpenCallback *openCallback)
00494 {
00495 return OpenArchive(archiveName,
00496 #ifndef EXCLUDE_COM
00497 &archiveLink.Library0, &archiveLink.Library1,
00498 #endif
00499 &archiveLink.Archive0, &archiveLink.Archive1,
00500 archiveLink.ArchiverInfo0, archiveLink.ArchiverInfo1,
00501 archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
00502 openCallback);
00503 }
00504
00505 HRESULT MyOpenArchive(const UString &archiveName,
00506 CArchiveLink &archiveLink,
00507 IOpenCallbackUI *openCallbackUI)
00508 {
00509 return MyOpenArchive(archiveName,
00510 #ifndef EXCLUDE_COM
00511 &archiveLink.Library0, &archiveLink.Library1,
00512 #endif
00513 &archiveLink.Archive0, &archiveLink.Archive1,
00514 archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
00515 archiveLink.VolumePaths,
00516 openCallbackUI);
00517 }
00518
00519 HRESULT ReOpenArchive(CArchiveLink &archiveLink,
00520 const UString &fileName)
00521 {
00522 if (archiveLink.GetNumLevels() > 1)
00523 return E_NOTIMPL;
00524 if (archiveLink.GetNumLevels() == 0)
00525 return MyOpenArchive(fileName, archiveLink, 0);
00526 return ReOpenArchive(archiveLink.GetArchive(), fileName);
00527 }