summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp')
-rw-r--r--src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp660
1 files changed, 660 insertions, 0 deletions
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp
new file mode 100644
index 000000000..eaad1e7ca
--- /dev/null
+++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp
@@ -0,0 +1,660 @@
+// WimHandler.cpp
+
+#include "StdAfx.h"
+
+#include "../../../../C/CpuArch.h"
+
+#include "Common/ComTry.h"
+#include "Common/IntToString.h"
+#include "Common/StringToInt.h"
+#include "Common/UTFConvert.h"
+
+#include "Windows/PropVariant.h"
+
+#include "../../Common/ProgressUtils.h"
+#include "../../Common/StreamUtils.h"
+
+#include "WimHandler.h"
+
+#define Get16(p) GetUi16(p)
+#define Get32(p) GetUi32(p)
+#define Get64(p) GetUi64(p)
+
+using namespace NWindows;
+
+namespace NArchive {
+namespace NWim {
+
+#define WIM_DETAILS
+
+static STATPROPSTG kProps[] =
+{
+ { NULL, kpidPath, VT_BSTR},
+ { NULL, kpidIsDir, VT_BOOL},
+ { NULL, kpidSize, VT_UI8},
+ { NULL, kpidPackSize, VT_UI8},
+ { NULL, kpidMTime, VT_FILETIME},
+ { NULL, kpidCTime, VT_FILETIME},
+ { NULL, kpidATime, VT_FILETIME},
+ { NULL, kpidAttrib, VT_UI4},
+ { NULL, kpidMethod, VT_BSTR},
+ { NULL, kpidShortName, VT_BSTR}
+
+ #ifdef WIM_DETAILS
+ , { NULL, kpidVolume, VT_UI4}
+ , { NULL, kpidOffset, VT_UI8}
+ , { NULL, kpidLinks, VT_UI4}
+ #endif
+};
+
+static STATPROPSTG kArcProps[] =
+{
+ { NULL, kpidSize, VT_UI8},
+ { NULL, kpidPackSize, VT_UI8},
+ { NULL, kpidMethod, VT_BSTR},
+ { NULL, kpidCTime, VT_FILETIME},
+ { NULL, kpidMTime, VT_FILETIME},
+ { NULL, kpidComment, VT_BSTR},
+ { NULL, kpidUnpackVer, VT_BSTR},
+ { NULL, kpidIsVolume, VT_BOOL},
+ { NULL, kpidVolume, VT_UI4},
+ { NULL, kpidNumVolumes, VT_UI4}
+};
+
+static bool ParseNumber64(const AString &s, UInt64 &res)
+{
+ const char *end;
+ if (s.Left(2) == "0x")
+ {
+ if (s.Length() == 2)
+ return false;
+ res = ConvertHexStringToUInt64((const char *)s + 2, &end);
+ }
+ else
+ {
+ if (s.IsEmpty())
+ return false;
+ res = ConvertStringToUInt64(s, &end);
+ }
+ return *end == 0;
+}
+
+static bool ParseNumber32(const AString &s, UInt32 &res)
+{
+ UInt64 res64;
+ if (!ParseNumber64(s, res64) || res64 >= ((UInt64)1 << 32))
+ return false;
+ res = (UInt32)res64;
+ return true;
+}
+
+bool ParseTime(const CXmlItem &item, FILETIME &ft, const char *tag)
+{
+ int index = item.FindSubTag(tag);
+ if (index >= 0)
+ {
+ const CXmlItem &timeItem = item.SubItems[index];
+ UInt32 low = 0, high = 0;
+ if (ParseNumber32(timeItem.GetSubStringForTag("LOWPART"), low) &&
+ ParseNumber32(timeItem.GetSubStringForTag("HIGHPART"), high))
+ {
+ ft.dwLowDateTime = low;
+ ft.dwHighDateTime = high;
+ return true;
+ }
+ }
+ return false;
+}
+
+void CImageInfo::Parse(const CXmlItem &item)
+{
+ CTimeDefined = ParseTime(item, CTime, "CREATIONTIME");
+ MTimeDefined = ParseTime(item, MTime, "LASTMODIFICATIONTIME");
+ NameDefined = ConvertUTF8ToUnicode(item.GetSubStringForTag("NAME"), Name);
+ // IndexDefined = ParseNumber32(item.GetPropertyValue("INDEX"), Index);
+}
+
+void CXml::ToUnicode(UString &s)
+{
+ size_t size = Data.GetCapacity();
+ if (size < 2 || (size & 1) != 0 || size > (1 << 24))
+ return;
+ const Byte *p = Data;
+ if (Get16(p) != 0xFEFF)
+ return;
+ wchar_t *chars = s.GetBuffer((int)size / 2);
+ for (size_t i = 2; i < size; i += 2)
+ *chars++ = (wchar_t)Get16(p + i);
+ *chars = 0;
+ s.ReleaseBuffer();
+}
+
+void CXml::Parse()
+{
+ UString s;
+ ToUnicode(s);
+ AString utf;
+ if (!ConvertUnicodeToUTF8(s, utf))
+ return;
+ ::CXml xml;
+ if (!xml.Parse(utf))
+ return;
+ if (xml.Root.Name != "WIM")
+ return;
+
+ for (int i = 0; i < xml.Root.SubItems.Size(); i++)
+ {
+ const CXmlItem &item = xml.Root.SubItems[i];
+ if (item.IsTagged("IMAGE"))
+ {
+ CImageInfo imageInfo;
+ imageInfo.Parse(item);
+ Images.Add(imageInfo);
+ }
+ }
+}
+
+static const char *kMethodLZX = "LZX";
+static const char *kMethodXpress = "XPress";
+static const char *kMethodCopy = "Copy";
+
+IMP_IInArchive_Props
+IMP_IInArchive_ArcProps
+
+STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
+{
+ COM_TRY_BEGIN
+ NWindows::NCOM::CPropVariant prop;
+
+ const CImageInfo *image = NULL;
+ if (_xmls.Size() == 1)
+ {
+ const CXml &xml = _xmls[0];
+ if (xml.Images.Size() == 1)
+ image = &xml.Images[0];
+ }
+
+ switch(propID)
+ {
+ case kpidSize: prop = _db.GetUnpackSize(); break;
+ case kpidPackSize: prop = _db.GetPackSize(); break;
+
+ case kpidCTime:
+ if (_xmls.Size() == 1)
+ {
+ const CXml &xml = _xmls[0];
+ int index = -1;
+ for (int i = 0; i < xml.Images.Size(); i++)
+ {
+ const CImageInfo &image = xml.Images[i];
+ if (image.CTimeDefined)
+ if (index < 0 || ::CompareFileTime(&image.CTime, &xml.Images[index].CTime) < 0)
+ index = i;
+ }
+ if (index >= 0)
+ prop = xml.Images[index].CTime;
+ }
+ break;
+
+ case kpidMTime:
+ if (_xmls.Size() == 1)
+ {
+ const CXml &xml = _xmls[0];
+ int index = -1;
+ for (int i = 0; i < xml.Images.Size(); i++)
+ {
+ const CImageInfo &image = xml.Images[i];
+ if (image.MTimeDefined)
+ if (index < 0 || ::CompareFileTime(&image.MTime, &xml.Images[index].MTime) > 0)
+ index = i;
+ }
+ if (index >= 0)
+ prop = xml.Images[index].MTime;
+ }
+ break;
+
+ case kpidComment:
+ if (image != NULL)
+ {
+ if (_xmlInComments)
+ {
+ UString s;
+ _xmls[0].ToUnicode(s);
+ prop = s;
+ }
+ else if (image->NameDefined)
+ prop = image->Name;
+ }
+ break;
+
+ case kpidUnpackVer:
+ {
+ UInt32 ver1 = _version >> 16;
+ UInt32 ver2 = (_version >> 8) & 0xFF;
+ UInt32 ver3 = (_version) & 0xFF;
+
+ char s[16];
+ ConvertUInt32ToString(ver1, s);
+ AString res = s;
+ res += '.';
+ ConvertUInt32ToString(ver2, s);
+ res += s;
+ if (ver3 != 0)
+ {
+ res += '.';
+ ConvertUInt32ToString(ver3, s);
+ res += s;
+ }
+ prop = res;
+ break;
+ }
+
+ case kpidIsVolume:
+ if (_xmls.Size() > 0)
+ {
+ UInt16 volIndex = _xmls[0].VolIndex;
+ if (volIndex < _volumes.Size())
+ prop = (_volumes[volIndex].Header.NumParts > 1);
+ }
+ break;
+ case kpidVolume:
+ if (_xmls.Size() > 0)
+ {
+ UInt16 volIndex = _xmls[0].VolIndex;
+ if (volIndex < _volumes.Size())
+ prop = (UInt32)_volumes[volIndex].Header.PartNumber;
+ }
+ break;
+ case kpidNumVolumes: if (_volumes.Size() > 0) prop = (UInt32)(_volumes.Size() - 1); break;
+ case kpidMethod:
+ {
+ bool lzx = false, xpress = false, copy = false;
+ for (int i = 0; i < _xmls.Size(); i++)
+ {
+ const CHeader &header = _volumes[_xmls[i].VolIndex].Header;
+ if (header.IsCompressed())
+ if (header.IsLzxMode())
+ lzx = true;
+ else
+ xpress = true;
+ else
+ copy = true;
+ }
+ AString res;
+ if (lzx)
+ res = kMethodLZX;
+ if (xpress)
+ {
+ if (!res.IsEmpty())
+ res += ' ';
+ res += kMethodXpress;
+ }
+ if (copy)
+ {
+ if (!res.IsEmpty())
+ res += ' ';
+ res += kMethodCopy;
+ }
+ prop = res;
+ }
+ }
+ prop.Detach(value);
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+ COM_TRY_BEGIN
+ NWindows::NCOM::CPropVariant prop;
+ if (index < (UInt32)_db.SortedItems.Size())
+ {
+ int realIndex = _db.SortedItems[index];
+ const CItem &item = _db.Items[realIndex];
+ const CStreamInfo *si = NULL;
+ const CVolume *vol = NULL;
+ if (item.StreamIndex >= 0)
+ {
+ si = &_db.Streams[item.StreamIndex];
+ vol = &_volumes[si->PartNumber];
+ }
+
+ switch(propID)
+ {
+ case kpidPath:
+ if (item.HasMetadata)
+ prop = _db.GetItemPath(realIndex);
+ else
+ {
+ char sz[16];
+ ConvertUInt32ToString(item.StreamIndex, sz);
+ AString s = sz;
+ while (s.Length() < _nameLenForStreams)
+ s = '0' + s;
+ /*
+ if (si->Resource.IsFree())
+ prefix = "[Free]";
+ */
+ s = "[Files]" STRING_PATH_SEPARATOR + s;
+ prop = s;
+ }
+ break;
+ case kpidShortName: if (item.HasMetadata) prop = item.ShortName; break;
+
+ case kpidIsDir: prop = item.IsDir(); break;
+ case kpidAttrib: if (item.HasMetadata) prop = item.Attrib; break;
+ case kpidCTime: if (item.HasMetadata) prop = item.CTime; break;
+ case kpidATime: if (item.HasMetadata) prop = item.ATime; break;
+ case kpidMTime: if (item.HasMetadata) prop = item.MTime; break;
+ case kpidPackSize: prop = si ? si->Resource.PackSize : (UInt64)0; break;
+ case kpidSize: prop = si ? si->Resource.UnpackSize : (UInt64)0; break;
+ case kpidMethod: if (si) prop = si->Resource.IsCompressed() ?
+ (vol->Header.IsLzxMode() ? kMethodLZX : kMethodXpress) : kMethodCopy; break;
+ #ifdef WIM_DETAILS
+ case kpidVolume: if (si) prop = (UInt32)si->PartNumber; break;
+ case kpidOffset: if (si) prop = (UInt64)si->Resource.Offset; break;
+ case kpidLinks: prop = si ? (UInt32)si->RefCount : (UInt32)0; break;
+ #endif
+ }
+ }
+ else
+ {
+ index -= _db.SortedItems.Size();
+ {
+ switch(propID)
+ {
+ case kpidPath:
+ {
+ char sz[16];
+ ConvertUInt32ToString(_xmls[index].VolIndex, sz);
+ prop = (AString)"[" + (AString)sz + "].xml";
+ break;
+ }
+ case kpidIsDir: prop = false; break;
+ case kpidPackSize:
+ case kpidSize: prop = (UInt64)_xmls[index].Data.GetCapacity(); break;
+ case kpidMethod: prop = kMethodCopy; break;
+ }
+ }
+ }
+ prop.Detach(value);
+ return S_OK;
+ COM_TRY_END
+}
+
+class CVolumeName
+{
+ // UInt32 _volIndex;
+ UString _before;
+ UString _after;
+public:
+ CVolumeName() {};
+
+ void InitName(const UString &name)
+ {
+ // _volIndex = 1;
+ int dotPos = name.ReverseFind('.');
+ if (dotPos < 0)
+ dotPos = name.Length();
+ _before = name.Left(dotPos);
+ _after = name.Mid(dotPos);
+ }
+
+ UString GetNextName(UInt32 index)
+ {
+ wchar_t s[16];
+ ConvertUInt32ToString(index, s);
+ return _before + (UString)s + _after;
+ }
+};
+
+STDMETHODIMP CHandler::Open(IInStream *inStream,
+ const UInt64 * /* maxCheckStartPosition */,
+ IArchiveOpenCallback *openArchiveCallback)
+{
+ COM_TRY_BEGIN
+ Close();
+ {
+ CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
+
+ CVolumeName seqName;
+ if (openArchiveCallback != NULL)
+ openArchiveCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
+
+ UInt32 numVolumes = 1;
+ int firstVolumeIndex = -1;
+ for (UInt32 i = 1; i <= numVolumes; i++)
+ {
+ CMyComPtr<IInStream> curStream;
+ if (i != 1)
+ {
+ UString fullName = seqName.GetNextName(i);
+ HRESULT result = openVolumeCallback->GetStream(fullName, &curStream);
+ if (result == S_FALSE)
+ continue;
+ if (result != S_OK)
+ return result;
+ if (!curStream)
+ break;
+ }
+ else
+ curStream = inStream;
+ CHeader header;
+ HRESULT res = NWim::ReadHeader(curStream, header);
+ if (res != S_OK)
+ {
+ if (i == 1)
+ return res;
+ if (res == S_FALSE)
+ continue;
+ return res;
+ }
+ _version = header.Version;
+ _isOldVersion = header.IsOldVersion();
+ if (firstVolumeIndex >= 0)
+ if (!header.AreFromOnArchive(_volumes[firstVolumeIndex].Header))
+ break;
+ if (_volumes.Size() > header.PartNumber && _volumes[header.PartNumber].Stream)
+ break;
+ CXml xml;
+ xml.VolIndex = header.PartNumber;
+ res = _db.Open(curStream, header, xml.Data, openArchiveCallback);
+ if (res != S_OK)
+ {
+ if (i == 1)
+ return res;
+ if (res == S_FALSE)
+ continue;
+ return res;
+ }
+
+ while (_volumes.Size() <= header.PartNumber)
+ _volumes.Add(CVolume());
+ CVolume &volume = _volumes[header.PartNumber];
+ volume.Header = header;
+ volume.Stream = curStream;
+
+ firstVolumeIndex = header.PartNumber;
+
+ bool needAddXml = true;
+ if (_xmls.Size() != 0)
+ if (xml.Data == _xmls[0].Data)
+ needAddXml = false;
+ if (needAddXml)
+ {
+ xml.Parse();
+ _xmls.Add(xml);
+ }
+
+ if (i == 1)
+ {
+ if (header.PartNumber != 1)
+ break;
+ if (!openVolumeCallback)
+ break;
+ numVolumes = header.NumParts;
+ {
+ NCOM::CPropVariant prop;
+ RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
+ if (prop.vt != VT_BSTR)
+ break;
+ seqName.InitName(prop.bstrVal);
+ }
+ }
+ }
+
+ _db.DetectPathMode();
+ RINOK(_db.Sort(_db.SkipRoot));
+
+ wchar_t sz[16];
+ ConvertUInt32ToString(_db.Streams.Size(), sz);
+ _nameLenForStreams = MyStringLen(sz);
+
+ _xmlInComments = (_xmls.Size() == 1 && !_db.ShowImageNumber);
+ }
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Close()
+{
+ _db.Clear();
+ _volumes.Clear();
+ _xmls.Clear();
+ _nameLenForStreams = 0;
+ return S_OK;
+}
+
+STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
+ Int32 testMode, IArchiveExtractCallback *extractCallback)
+{
+ COM_TRY_BEGIN
+ bool allFilesMode = (numItems == (UInt32)-1);
+
+ if (allFilesMode)
+ numItems = _db.SortedItems.Size() + _xmls.Size();
+ if (numItems == 0)
+ return S_OK;
+
+ UInt32 i;
+ UInt64 totalSize = 0;
+ for (i = 0; i < numItems; i++)
+ {
+ UInt32 index = allFilesMode ? i : indices[i];
+ if (index < (UInt32)_db.SortedItems.Size())
+ {
+ int streamIndex = _db.Items[_db.SortedItems[index]].StreamIndex;
+ if (streamIndex >= 0)
+ {
+ const CStreamInfo &si = _db.Streams[streamIndex];
+ totalSize += si.Resource.UnpackSize;
+ }
+ }
+ else
+ totalSize += _xmls[index - (UInt32)_db.SortedItems.Size()].Data.GetCapacity();
+ }
+
+ RINOK(extractCallback->SetTotal(totalSize));
+
+ UInt64 currentTotalPacked = 0;
+ UInt64 currentTotalUnPacked = 0;
+ UInt64 currentItemUnPacked, currentItemPacked;
+
+ int prevSuccessStreamIndex = -1;
+
+ CUnpacker unpacker;
+
+ CLocalProgress *lps = new CLocalProgress;
+ CMyComPtr<ICompressProgressInfo> progress = lps;
+ lps->Init(extractCallback, false);
+
+ for (i = 0; i < numItems; currentTotalUnPacked += currentItemUnPacked,
+ currentTotalPacked += currentItemPacked)
+ {
+ currentItemUnPacked = 0;
+ currentItemPacked = 0;
+
+ lps->InSize = currentTotalPacked;
+ lps->OutSize = currentTotalUnPacked;
+
+ RINOK(lps->SetCur());
+ UInt32 index = allFilesMode ? i : indices[i];
+ i++;
+ Int32 askMode = testMode ?
+ NExtract::NAskMode::kTest :
+ NExtract::NAskMode::kExtract;
+
+ CMyComPtr<ISequentialOutStream> realOutStream;
+ RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
+ if (index >= (UInt32)_db.SortedItems.Size())
+ {
+ if (!testMode && !realOutStream)
+ continue;
+ RINOK(extractCallback->PrepareOperation(askMode));
+ const CByteBuffer &data = _xmls[index - (UInt32)_db.SortedItems.Size()].Data;
+ currentItemUnPacked = data.GetCapacity();
+ if (realOutStream)
+ {
+ RINOK(WriteStream(realOutStream, (const Byte *)data, data.GetCapacity()));
+ realOutStream.Release();
+ }
+ RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
+ continue;
+ }
+
+ const CItem &item = _db.Items[_db.SortedItems[index]];
+ int streamIndex = item.StreamIndex;
+ if (streamIndex < 0)
+ {
+ if (!testMode && !realOutStream)
+ continue;
+ RINOK(extractCallback->PrepareOperation(askMode));
+ realOutStream.Release();
+ RINOK(extractCallback->SetOperationResult(item.HasStream() ?
+ NExtract::NOperationResult::kDataError :
+ NExtract::NOperationResult::kOK));
+ continue;
+ }
+
+ const CStreamInfo &si = _db.Streams[streamIndex];
+ currentItemUnPacked = si.Resource.UnpackSize;
+ currentItemPacked = si.Resource.PackSize;
+
+ if (!testMode && !realOutStream)
+ continue;
+ RINOK(extractCallback->PrepareOperation(askMode));
+ Int32 opRes = NExtract::NOperationResult::kOK;
+ if (streamIndex != prevSuccessStreamIndex || realOutStream)
+ {
+ Byte digest[20];
+ const CVolume &vol = _volumes[si.PartNumber];
+ HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header.IsLzxMode(),
+ realOutStream, progress, digest);
+ if (res == S_OK)
+ {
+ if (memcmp(digest, si.Hash, kHashSize) == 0)
+ prevSuccessStreamIndex = streamIndex;
+ else
+ opRes = NExtract::NOperationResult::kCRCError;
+ }
+ else if (res == S_FALSE)
+ opRes = NExtract::NOperationResult::kDataError;
+ else
+ return res;
+ }
+ realOutStream.Release();
+ RINOK(extractCallback->SetOperationResult(opRes));
+ }
+ return S_OK;
+ COM_TRY_END
+}
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+ *numItems = _db.SortedItems.Size();
+ if (!_xmlInComments)
+ *numItems += _xmls.Size();
+ return S_OK;
+}
+
+}}