summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Archive/Wim
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/Wim')
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/StdAfx.h8
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.cpp660
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.h77
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandlerOut.cpp639
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.cpp855
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.h297
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/Wim/WimRegister.cpp18
7 files changed, 2554 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/StdAfx.h b/src/libs/7zip/win/CPP/7zip/Archive/Wim/StdAfx.h
new file mode 100644
index 000000000..e7fb6986d
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/Wim/StdAfx.h
@@ -0,0 +1,8 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+
+#endif
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.cpp
new file mode 100644
index 000000000..eaad1e7ca
--- /dev/null
+++ b/src/libs/7zip/win/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;
+}
+
+}}
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.h b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.h
new file mode 100644
index 000000000..aa92069a5
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.h
@@ -0,0 +1,77 @@
+// WimHandler.h
+
+#ifndef __ARCHIVE_WIM_HANDLER_H
+#define __ARCHIVE_WIM_HANDLER_H
+
+#include "Common/MyCom.h"
+#include "Common/MyXml.h"
+
+#include "WimIn.h"
+
+namespace NArchive {
+namespace NWim {
+
+struct CVolume
+{
+ CHeader Header;
+ CMyComPtr<IInStream> Stream;
+};
+
+struct CImageInfo
+{
+ bool CTimeDefined;
+ bool MTimeDefined;
+ bool NameDefined;
+ // bool IndexDefined;
+
+ FILETIME CTime;
+ FILETIME MTime;
+ UString Name;
+ // UInt32 Index;
+
+ CImageInfo(): CTimeDefined(false), MTimeDefined(false), NameDefined(false)
+ // , IndexDefined(false)
+ {}
+ void Parse(const CXmlItem &item);
+};
+
+struct CXml
+{
+ CByteBuffer Data;
+ UInt16 VolIndex;
+ CObjectVector<CImageInfo> Images;
+
+ void ToUnicode(UString &s);
+ void Parse();
+};
+
+
+class CHandler:
+ public IInArchive,
+ public CMyUnknownImp
+{
+ CDatabase _db;
+ UInt32 _version;
+ bool _isOldVersion;
+ CObjectVector<CVolume> _volumes;
+ CObjectVector<CXml> _xmls;
+ int _nameLenForStreams;
+ bool _xmlInComments;
+
+public:
+ MY_UNKNOWN_IMP1(IInArchive)
+ INTERFACE_IInArchive(;)
+};
+
+class COutHandler:
+ public IOutArchive,
+ public CMyUnknownImp
+{
+public:
+ MY_UNKNOWN_IMP1(IOutArchive)
+ INTERFACE_IOutArchive(;)
+};
+
+}}
+
+#endif
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandlerOut.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandlerOut.cpp
new file mode 100644
index 000000000..50b879e79
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandlerOut.cpp
@@ -0,0 +1,639 @@
+// WimHandlerOut.cpp
+
+#include "StdAfx.h"
+
+#include "../../../../C/CpuArch.h"
+
+#include "Common/ComTry.h"
+#include "Common/IntToString.h"
+
+#include "Windows/PropVariant.h"
+#include "Windows/Time.h"
+
+#include "../../Common/LimitedStreams.h"
+#include "../../Common/ProgressUtils.h"
+#include "../../Common/StreamUtils.h"
+
+#include "../../Crypto/RandGen.h"
+#include "../../Crypto/Sha1.h"
+
+#include "WimHandler.h"
+
+using namespace NWindows;
+
+namespace NArchive {
+namespace NWim {
+
+struct CSha1Hash
+{
+ Byte Hash[kHashSize];
+};
+
+struct CHashList
+{
+ CRecordVector<CSha1Hash> Digests;
+ CIntVector Sorted;
+
+ int AddUnique(const CSha1Hash &h);
+};
+
+int CHashList::AddUnique(const CSha1Hash &h)
+{
+ int left = 0, right = Sorted.Size();
+ while (left != right)
+ {
+ int mid = (left + right) / 2;
+ int index = Sorted[mid];
+ UInt32 i;
+ const Byte *hash2 = Digests[index].Hash;
+ for (i = 0; i < kHashSize; i++)
+ if (h.Hash[i] != hash2[i])
+ break;
+ if (i == kHashSize)
+ return index;
+ if (h.Hash[i] < hash2[i])
+ right = mid;
+ else
+ left = mid + 1;
+ }
+ Sorted.Insert(left, Digests.Add(h));
+ return -1;
+}
+
+struct CUpdateItem
+{
+ UString Name;
+ UInt64 Size;
+ FILETIME CTime;
+ FILETIME ATime;
+ FILETIME MTime;
+ UInt32 Attrib;
+ bool IsDir;
+ int HashIndex;
+
+ CUpdateItem(): HashIndex(-1) {}
+};
+
+struct CDir
+{
+ int Index;
+ UString Name;
+ CObjectVector<CDir> Dirs;
+ CIntVector Files;
+
+ CDir(): Index(-1) {}
+ bool IsLeaf() const { return Index >= 0; }
+ UInt64 GetNumDirs() const;
+ UInt64 GetNumFiles() const;
+ CDir* AddDir(CObjectVector<CUpdateItem> &items, const UString &name, int index);
+};
+
+UInt64 CDir::GetNumDirs() const
+{
+ UInt64 num = Dirs.Size();
+ for (int i = 0; i < Dirs.Size(); i++)
+ num += Dirs[i].GetNumDirs();
+ return num;
+}
+
+UInt64 CDir::GetNumFiles() const
+{
+ UInt64 num = Files.Size();
+ for (int i = 0; i < Dirs.Size(); i++)
+ num += Dirs[i].GetNumFiles();
+ return num;
+}
+
+CDir* CDir::AddDir(CObjectVector<CUpdateItem> &items, const UString &name, int index)
+{
+ int left = 0, right = Dirs.Size();
+ while (left != right)
+ {
+ int mid = (left + right) / 2;
+ CDir &d = Dirs[mid];
+ int compare = name.CompareNoCase(d.IsLeaf() ? items[Dirs[mid].Index].Name : d.Name);
+ if (compare == 0)
+ {
+ if (index >= 0)
+ d.Index = index;
+ return &d;
+ }
+ if (compare < 0)
+ right = mid;
+ else
+ left = mid + 1;
+ }
+ Dirs.Insert(left, CDir());
+ CDir &d = Dirs[left];
+ d.Index = index;
+ if (index < 0)
+ d.Name = name;
+ return &d;
+}
+
+
+STDMETHODIMP COutHandler::GetFileTimeType(UInt32 *type)
+{
+ *type = NFileTimeType::kWindows;
+ return S_OK;
+}
+
+static HRESULT GetTime(IArchiveUpdateCallback *callback, int index, PROPID propID, FILETIME &ft)
+{
+ ft.dwLowDateTime = ft.dwHighDateTime = 0;
+ NCOM::CPropVariant prop;
+ RINOK(callback->GetProperty(index, propID, &prop));
+ if (prop.vt == VT_FILETIME)
+ ft = prop.filetime;
+ else if (prop.vt != VT_EMPTY)
+ return E_INVALIDARG;
+ return S_OK;
+}
+
+#define Set16(p, d) SetUi16(p, d)
+#define Set32(p, d) SetUi32(p, d)
+#define Set64(p, d) SetUi64(p, d)
+
+void CResource::WriteTo(Byte *p) const
+{
+ Set64(p, PackSize);
+ p[7] = Flags;
+ Set64(p + 8, Offset);
+ Set64(p + 16, UnpackSize);
+}
+
+void CHeader::WriteTo(Byte *p) const
+{
+ memcpy(p, kSignature, kSignatureSize);
+ Set32(p + 8, kHeaderSizeMax);
+ Set32(p + 0xC, Version);
+ Set32(p + 0x10, Flags);
+ Set32(p + 0x14, ChunkSize);
+ memcpy(p + 0x18, Guid, 16);
+ Set16(p + 0x28, PartNumber);
+ Set16(p + 0x2A, NumParts);
+ Set32(p + 0x2C, NumImages);
+ OffsetResource.WriteTo(p + 0x30);
+ XmlResource.WriteTo(p + 0x48);
+ MetadataResource.WriteTo(p + 0x60);
+ IntegrityResource.WriteTo(p + 0x7C);
+ Set32(p + 0x78, BootIndex);
+ memset(p + 0x94, 0, 60);
+}
+
+void CStreamInfo::WriteTo(Byte *p) const
+{
+ Resource.WriteTo(p);
+ Set16(p + 0x18, PartNumber);
+ Set32(p + 0x1A, RefCount);
+ memcpy(p + 0x1E, Hash, kHashSize);
+}
+
+class CInStreamWithSha1:
+ public ISequentialInStream,
+ public CMyUnknownImp
+{
+ CMyComPtr<ISequentialInStream> _stream;
+ UInt64 _size;
+ NCrypto::NSha1::CContext _sha;
+public:
+ MY_UNKNOWN_IMP1(IInStream)
+ STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+
+ void SetStream(ISequentialInStream *stream) { _stream = stream; }
+ void Init()
+ {
+ _size = 0;
+ _sha.Init();
+ }
+ void ReleaseStream() { _stream.Release(); }
+ UInt64 GetSize() const { return _size; }
+ void Final(Byte *digest) { _sha.Final(digest); }
+};
+
+STDMETHODIMP CInStreamWithSha1::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+ UInt32 realProcessedSize;
+ HRESULT result = _stream->Read(data, size, &realProcessedSize);
+ _size += realProcessedSize;
+ _sha.Update((const Byte *)data, realProcessedSize);
+ if (processedSize != NULL)
+ *processedSize = realProcessedSize;
+ return result;
+}
+
+static void SetFileTimeToMem(Byte *p, const FILETIME &ft)
+{
+ Set32(p, ft.dwLowDateTime);
+ Set32(p + 4, ft.dwHighDateTime);
+}
+
+static size_t WriteItem(const CUpdateItem &item, Byte *p, const Byte *hash)
+{
+ int fileNameLen = item.Name.Length() * 2;
+ int fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2);
+
+ size_t totalLen = ((kDirRecordSize + fileNameLen2 + 6) & ~7);
+ if (p)
+ {
+ memset(p, 0, totalLen);
+ Set64(p, totalLen);
+ Set64(p + 8, item.Attrib);
+ Set32(p + 0xC, (UInt32)(Int32)-1); // item.SecurityId
+ // Set64(p + 0x10, 0); // subdirOffset
+ SetFileTimeToMem(p + 0x28, item.CTime);
+ SetFileTimeToMem(p + 0x30, item.ATime);
+ SetFileTimeToMem(p + 0x38, item.MTime);
+ if (hash)
+ memcpy(p + 0x40, hash, kHashSize);
+ /*
+ else
+ memset(p + 0x40, 0, kHashSize);
+ */
+ // Set16(p + 98, 0); // shortNameLen
+ Set16(p + 100, (UInt16)fileNameLen);
+ for (int i = 0; i * 2 < fileNameLen; i++)
+ Set16(p + kDirRecordSize + i * 2, item.Name[i]);
+ }
+ return totalLen;
+}
+
+static void WriteTree(const CDir &tree, CRecordVector<CSha1Hash> &digests,
+ CUpdateItem &defaultDirItem,
+ CObjectVector<CUpdateItem> &updateItems, Byte *dest, size_t &pos)
+{
+ int i;
+ for (i = 0; i < tree.Files.Size(); i++)
+ {
+ const CUpdateItem &ui = updateItems[tree.Files[i]];
+ pos += WriteItem(ui, dest ? dest + pos : NULL,
+ ui.HashIndex >= 0 ? digests[ui.HashIndex].Hash : NULL);
+ }
+
+ size_t posStart = pos;
+ for (i = 0; i < tree.Dirs.Size(); i++)
+ {
+ const CDir &subfolder = tree.Dirs[i];
+ CUpdateItem *item = &defaultDirItem;
+ if (subfolder.IsLeaf())
+ item = &updateItems[subfolder.Index];
+ else
+ defaultDirItem.Name = subfolder.Name;
+ pos += WriteItem(*item, NULL, NULL);
+ }
+
+ if (dest)
+ Set64(dest + pos, 0);
+
+ pos += 8;
+
+ for (i = 0; i < tree.Dirs.Size(); i++)
+ {
+ const CDir &subfolder = tree.Dirs[i];
+ if (dest)
+ {
+ CUpdateItem *item = &defaultDirItem;
+ if (subfolder.IsLeaf())
+ item = &updateItems[subfolder.Index];
+ else
+ defaultDirItem.Name = subfolder.Name;
+ size_t len = WriteItem(*item, dest + posStart, NULL);
+ Set64(dest + posStart + 0x10, pos);
+ posStart += len;
+ }
+ WriteTree(subfolder, digests, defaultDirItem, updateItems, dest, pos);
+ }
+}
+
+static void AddTag(AString &s, const char *name, const AString &value)
+{
+ s += "<";
+ s += name;
+ s += ">";
+ s += value;
+ s += "</";
+ s += name;
+ s += ">";
+}
+
+static void AddTagUInt64(AString &s, const char *name, UInt64 value)
+{
+ char temp[32];
+ ConvertUInt64ToString(value, temp);
+ AddTag(s, name, temp);
+}
+
+static AString TimeToXml(FILETIME &ft)
+{
+ AString res;
+ char temp[16] = { '0', 'x' };
+ ConvertUInt32ToHexWithZeros(ft.dwHighDateTime, temp + 2);
+ AddTag(res, "HIGHPART", temp);
+ ConvertUInt32ToHexWithZeros(ft.dwLowDateTime, temp + 2);
+ AddTag(res, "LOWPART", temp);
+ return res;
+}
+
+void CHeader::SetDefaultFields(bool useLZX)
+{
+ Version = kWimVersion;
+ Flags = NHeaderFlags::kRpFix;
+ ChunkSize = 0;
+ if (useLZX)
+ {
+ Flags |= NHeaderFlags::kCompression | NHeaderFlags::kLZX;
+ ChunkSize = kChunkSize;
+ }
+ g_RandomGenerator.Generate(Guid, 16);
+ PartNumber = 1;
+ NumParts = 1;
+ NumImages = 1;
+ BootIndex = 0;
+ OffsetResource.Clear();
+ XmlResource.Clear();
+ MetadataResource.Clear();
+ IntegrityResource.Clear();
+}
+
+static HRESULT UpdateArchive(ISequentialOutStream *seqOutStream,
+ CDir &rootFolder,
+ CObjectVector<CUpdateItem> &updateItems,
+ IArchiveUpdateCallback *callback)
+{
+ CMyComPtr<IOutStream> outStream;
+ RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
+ if (!outStream)
+ return E_NOTIMPL;
+
+ UInt64 complexity = 0;
+
+ int i;
+ for (i = 0; i < updateItems.Size(); i++)
+ complexity += updateItems[i].Size;
+
+ RINOK(callback->SetTotal(complexity));
+
+ NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
+ CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
+
+ CLocalProgress *lps = new CLocalProgress;
+ CMyComPtr<ICompressProgressInfo> progress = lps;
+ lps->Init(callback, true);
+
+ complexity = 0;
+
+ bool useCompression = false;
+
+ CHeader header;
+ header.SetDefaultFields(useCompression);
+ Byte buf[kHeaderSizeMax];
+ header.WriteTo(buf);
+ RINOK(WriteStream(outStream, buf, kHeaderSizeMax));
+
+ CHashList hashes;
+ CObjectVector<CStreamInfo> streams;
+
+ UInt64 curPos = kHeaderSizeMax;
+ UInt64 unpackTotalSize = 0;
+ for (i = 0; i < updateItems.Size(); i++)
+ {
+ lps->InSize = lps->OutSize = complexity;
+ RINOK(lps->SetCur());
+
+ CUpdateItem &ui = updateItems[i];
+ if (ui.IsDir || ui.Size == 0)
+ continue;
+
+ CInStreamWithSha1 *inShaStreamSpec = new CInStreamWithSha1;
+ CMyComPtr<ISequentialInStream> inShaStream = inShaStreamSpec;
+
+ {
+ CMyComPtr<ISequentialInStream> fileInStream;
+ HRESULT res = callback->GetStream(i, &fileInStream);
+ if (res != S_FALSE)
+ {
+ RINOK(res);
+ inShaStreamSpec->SetStream(fileInStream);
+ fileInStream.Release();
+ inShaStreamSpec->Init();
+ UInt64 offsetBlockSize = 0;
+ if (useCompression)
+ {
+ for (UInt64 t = kChunkSize; t < ui.Size; t += kChunkSize)
+ {
+ Byte buf[8];
+ SetUi32(buf, (UInt32)t);
+ RINOK(WriteStream(outStream, buf, 4));
+ offsetBlockSize += 4;
+ }
+ }
+
+ RINOK(copyCoder->Code(inShaStream, outStream, NULL, NULL, progress));
+ ui.Size = copyCoderSpec->TotalSize;
+
+ CSha1Hash hash;
+ unpackTotalSize += ui.Size;
+ UInt64 packSize = offsetBlockSize + ui.Size;
+ inShaStreamSpec->Final(hash.Hash);
+ int index = hashes.AddUnique(hash);
+ if (index >= 0)
+ {
+ ui.HashIndex = index;
+ streams[index].RefCount++;
+ outStream->Seek(-(Int64)packSize, STREAM_SEEK_CUR, &curPos);
+ outStream->SetSize(curPos);
+ }
+ else
+ {
+ ui.HashIndex = hashes.Digests.Size() - 1;
+ CStreamInfo s;
+ s.Resource.PackSize = packSize;
+ s.Resource.Offset = curPos;
+ s.Resource.UnpackSize = ui.Size;
+ s.Resource.Flags = 0;
+ if (useCompression)
+ s.Resource.Flags = NResourceFlags::Compressed;
+ s.PartNumber = 1;
+ s.RefCount = 1;
+ memcpy(s.Hash, hash.Hash, kHashSize);
+ streams.Add(s);
+ curPos += packSize;
+ }
+ }
+ fileInStream.Release();
+ complexity += ui.Size;
+ RINOK(callback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
+ }
+ }
+
+
+ CUpdateItem ri;
+ FILETIME ft;
+ NTime::GetCurUtcFileTime(ft);
+ ri.MTime = ri.ATime = ri.CTime = ft;
+ ri.Attrib = FILE_ATTRIBUTE_DIRECTORY;
+
+ const UInt32 kSecuritySize = 8;
+ size_t pos = kSecuritySize;
+ WriteTree(rootFolder, hashes.Digests, ri, updateItems, NULL, pos);
+
+ CByteBuffer meta;
+ meta.SetCapacity(pos);
+
+ // we can write 0 here only if there is no security data, imageX does it,
+ // but some programs expect size = 8
+ Set32((Byte *)meta, 8); // size of security data
+ Set32((Byte *)meta + 4, 0); // num security entries
+
+ pos = kSecuritySize;
+ WriteTree(rootFolder, hashes.Digests, ri, updateItems, (Byte *)meta, pos);
+
+ {
+ NCrypto::NSha1::CContext sha;
+ sha.Init();
+ sha.Update((const Byte *)meta, pos);
+ CSha1Hash digest;
+ sha.Final(digest.Hash);
+
+ CStreamInfo s;
+ s.Resource.PackSize = pos;
+ s.Resource.Offset = curPos;
+ s.Resource.UnpackSize = pos;
+ s.Resource.Flags = NResourceFlags::kMetadata;
+ s.PartNumber = 1;
+ s.RefCount = 1;
+ memcpy(s.Hash, digest.Hash, kHashSize);
+ streams.Add(s);
+ RINOK(WriteStream(outStream, (const Byte *)meta, pos));
+ meta.Free();
+ curPos += pos;
+ }
+
+
+ header.OffsetResource.UnpackSize = header.OffsetResource.PackSize = (UInt64)streams.Size() * kStreamInfoSize;
+ header.OffsetResource.Offset = curPos;
+ header.OffsetResource.Flags = NResourceFlags::kMetadata;
+
+ for (i = 0; i < streams.Size(); i++)
+ {
+ Byte buf[kStreamInfoSize];
+ streams[i].WriteTo(buf);
+ RINOK(WriteStream(outStream, buf, kStreamInfoSize));
+ curPos += kStreamInfoSize;
+ }
+
+ AString xml = "<WIM>";
+ AddTagUInt64(xml, "TOTALBYTES", curPos);
+ xml += "<IMAGE INDEX=\"1\"><NAME>1</NAME>";
+ AddTagUInt64(xml, "DIRCOUNT", rootFolder.GetNumDirs());
+ AddTagUInt64(xml, "FILECOUNT", rootFolder.GetNumFiles());
+ AddTagUInt64(xml, "TOTALBYTES", unpackTotalSize);
+ NTime::GetCurUtcFileTime(ft);
+ AddTag(xml, "CREATIONTIME", TimeToXml(ft));
+ AddTag(xml, "LASTMODIFICATIONTIME", TimeToXml(ft));
+ xml += "</IMAGE></WIM>";
+
+ size_t xmlSize = (xml.Length() + 1) * 2;
+ meta.SetCapacity(xmlSize);
+ Set16((Byte *)meta, 0xFEFF);
+ for (i = 0; i < xml.Length(); i++)
+ Set16((Byte *)meta + 2 + i * 2, xml[i]);
+ RINOK(WriteStream(outStream, (const Byte *)meta, xmlSize));
+ meta.Free();
+
+ header.XmlResource.UnpackSize = header.XmlResource.PackSize = xmlSize;
+ header.XmlResource.Offset = curPos;
+ header.XmlResource.Flags = NResourceFlags::kMetadata;
+
+ outStream->Seek(0, STREAM_SEEK_SET, NULL);
+ header.WriteTo(buf);
+ return WriteStream(outStream, buf, kHeaderSizeMax);
+}
+
+STDMETHODIMP COutHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
+ IArchiveUpdateCallback *callback)
+{
+ COM_TRY_BEGIN
+ CObjectVector<CUpdateItem> updateItems;
+ CDir tree;
+ tree.Dirs.Add(CDir());
+ CDir &rootFolder = tree.Dirs.Back();
+
+ for (UInt32 i = 0; i < numItems; i++)
+ {
+ CUpdateItem ui;
+ Int32 newData, newProps;
+ UInt32 indexInArchive;
+ if (!callback)
+ return E_FAIL;
+ RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
+
+ {
+ NCOM::CPropVariant prop;
+ RINOK(callback->GetProperty(i, kpidIsDir, &prop));
+ if (prop.vt == VT_EMPTY)
+ ui.IsDir = false;
+ else if (prop.vt != VT_BOOL)
+ return E_INVALIDARG;
+ else
+ ui.IsDir = (prop.boolVal != VARIANT_FALSE);
+ }
+
+ {
+ NCOM::CPropVariant prop;
+ RINOK(callback->GetProperty(i, kpidAttrib, &prop));
+ if (prop.vt == VT_EMPTY)
+ ui.Attrib = (ui.IsDir ? FILE_ATTRIBUTE_DIRECTORY : 0);
+ else if (prop.vt != VT_UI4)
+ return E_INVALIDARG;
+ else
+ ui.Attrib = prop.ulVal;
+ }
+
+ RINOK(GetTime(callback, i, kpidCTime, ui.CTime));
+ RINOK(GetTime(callback, i, kpidATime, ui.ATime));
+ RINOK(GetTime(callback, i, kpidMTime, ui.MTime));
+
+ {
+ NCOM::CPropVariant prop;
+ RINOK(callback->GetProperty(i, kpidSize, &prop));
+ if (prop.vt != VT_UI8)
+ return E_INVALIDARG;
+ ui.Size = prop.uhVal.QuadPart;
+ }
+
+ UString path;
+ NCOM::CPropVariant prop;
+ RINOK(callback->GetProperty(i, kpidPath, &prop));
+ if (prop.vt == VT_BSTR)
+ path = prop.bstrVal;
+ else if (prop.vt != VT_EMPTY)
+ return E_INVALIDARG;
+
+ CDir *curItem = &rootFolder;
+ int len = path.Length();
+ UString fileName;
+ for (int j = 0; j < len; j++)
+ {
+ wchar_t c = path[j];
+ if (c == WCHAR_PATH_SEPARATOR || c == L'/')
+ {
+ curItem = curItem->AddDir(updateItems, fileName, -1);
+ fileName.Empty();
+ }
+ else
+ fileName += c;
+ }
+
+ ui.Name = fileName;
+ updateItems.Add(ui);
+ if (ui.IsDir)
+ curItem->AddDir(updateItems, fileName, (int)i);
+ else
+ curItem->Files.Add(i);
+ }
+ return UpdateArchive(outStream, tree, updateItems, callback);
+ COM_TRY_END
+}
+
+}}
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.cpp
new file mode 100644
index 000000000..c210804df
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.cpp
@@ -0,0 +1,855 @@
+// Archive/WimIn.cpp
+
+#include "StdAfx.h"
+
+#include "../../../../C/CpuArch.h"
+
+#include "Common/IntToString.h"
+
+#include "../../Common/StreamUtils.h"
+#include "../../Common/StreamObjects.h"
+#include "../../Common/LimitedStreams.h"
+
+#include "../Common/OutStreamWithSha1.h"
+
+#include "WimIn.h"
+
+#define Get16(p) GetUi16(p)
+#define Get32(p) GetUi32(p)
+#define Get64(p) GetUi64(p)
+
+namespace NArchive {
+namespace NWim {
+
+namespace NXpress {
+
+class CDecoderFlusher
+{
+ CDecoder *m_Decoder;
+public:
+ bool NeedFlush;
+ CDecoderFlusher(CDecoder *decoder): m_Decoder(decoder), NeedFlush(true) {}
+ ~CDecoderFlusher()
+ {
+ if (NeedFlush)
+ m_Decoder->Flush();
+ m_Decoder->ReleaseStreams();
+ }
+};
+
+HRESULT CDecoder::CodeSpec(UInt32 outSize)
+{
+ {
+ Byte levels[kMainTableSize];
+ for (unsigned i = 0; i < kMainTableSize; i += 2)
+ {
+ Byte b = m_InBitStream.DirectReadByte();
+ levels[i] = b & 0xF;
+ levels[i + 1] = b >> 4;
+ }
+ if (!m_MainDecoder.SetCodeLengths(levels))
+ return S_FALSE;
+ }
+
+ while (outSize > 0)
+ {
+ UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream);
+ if (number < 256)
+ {
+ m_OutWindowStream.PutByte((Byte)number);
+ outSize--;
+ }
+ else
+ {
+ if (number >= kMainTableSize)
+ return S_FALSE;
+ UInt32 posLenSlot = number - 256;
+ UInt32 posSlot = posLenSlot / kNumLenSlots;
+ UInt32 len = posLenSlot % kNumLenSlots;
+ UInt32 distance = (1 << posSlot) - 1 + m_InBitStream.ReadBits(posSlot);
+
+ if (len == kNumLenSlots - 1)
+ {
+ len = m_InBitStream.DirectReadByte();
+ if (len == 0xFF)
+ {
+ len = m_InBitStream.DirectReadByte();
+ len |= (UInt32)m_InBitStream.DirectReadByte() << 8;
+ }
+ else
+ len += kNumLenSlots - 1;
+ }
+
+ len += kMatchMinLen;
+ UInt32 locLen = (len <= outSize ? len : outSize);
+
+ if (!m_OutWindowStream.CopyBlock(distance, locLen))
+ return S_FALSE;
+
+ len -= locLen;
+ outSize -= locLen;
+ if (len != 0)
+ return S_FALSE;
+ }
+ }
+ return S_OK;
+}
+
+const UInt32 kDictSize = (1 << kNumPosSlots);
+
+HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize)
+{
+ if (!m_OutWindowStream.Create(kDictSize) || !m_InBitStream.Create(1 << 16))
+ return E_OUTOFMEMORY;
+
+ CDecoderFlusher flusher(this);
+
+ m_InBitStream.SetStream(inStream);
+ m_OutWindowStream.SetStream(outStream);
+ m_InBitStream.Init();
+ m_OutWindowStream.Init(false);
+
+ RINOK(CodeSpec(outSize));
+
+ flusher.NeedFlush = false;
+ return Flush();
+}
+
+HRESULT CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize)
+{
+ try { return CodeReal(inStream, outStream, outSize); }
+ catch(const CInBufferException &e) { return e.ErrorCode; } \
+ catch(const CLzOutWindowException &e) { return e.ErrorCode; }
+ catch(...) { return S_FALSE; }
+}
+
+}
+
+HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode,
+ ISequentialOutStream *outStream, ICompressProgressInfo *progress)
+{
+ RINOK(inStream->Seek(resource.Offset, STREAM_SEEK_SET, NULL));
+
+ CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream();
+ CMyComPtr<ISequentialInStream> limitedStream = limitedStreamSpec;
+ limitedStreamSpec->SetStream(inStream);
+
+ if (!copyCoder)
+ {
+ copyCoderSpec = new NCompress::CCopyCoder;
+ copyCoder = copyCoderSpec;
+ }
+ if (!resource.IsCompressed())
+ {
+ if (resource.PackSize != resource.UnpackSize)
+ return S_FALSE;
+ limitedStreamSpec->Init(resource.PackSize);
+ return copyCoder->Code(limitedStreamSpec, outStream, NULL, NULL, progress);
+ }
+ if (resource.UnpackSize == 0)
+ return S_OK;
+ UInt64 numChunks = (resource.UnpackSize + kChunkSize - 1) >> kChunkSizeBits;
+ unsigned entrySize = ((resource.UnpackSize > (UInt64)1 << 32) ? 8 : 4);
+ UInt64 sizesBufSize64 = entrySize * (numChunks - 1);
+ size_t sizesBufSize = (size_t)sizesBufSize64;
+ if (sizesBufSize != sizesBufSize64)
+ return E_OUTOFMEMORY;
+ if (sizesBufSize > sizesBuf.GetCapacity())
+ {
+ sizesBuf.Free();
+ sizesBuf.SetCapacity(sizesBufSize);
+ }
+ RINOK(ReadStream_FALSE(inStream, (Byte *)sizesBuf, sizesBufSize));
+ const Byte *p = (const Byte *)sizesBuf;
+
+ if (lzxMode && !lzxDecoder)
+ {
+ lzxDecoderSpec = new NCompress::NLzx::CDecoder(true);
+ lzxDecoder = lzxDecoderSpec;
+ RINOK(lzxDecoderSpec->SetParams(kChunkSizeBits));
+ }
+
+ UInt64 baseOffset = resource.Offset + sizesBufSize64;
+ UInt64 outProcessed = 0;
+ for (UInt32 i = 0; i < (UInt32)numChunks; i++)
+ {
+ UInt64 offset = 0;
+ if (i > 0)
+ {
+ offset = (entrySize == 4) ? Get32(p): Get64(p);
+ p += entrySize;
+ }
+ UInt64 nextOffset = resource.PackSize - sizesBufSize64;
+ if (i + 1 < (UInt32)numChunks)
+ nextOffset = (entrySize == 4) ? Get32(p): Get64(p);
+ if (nextOffset < offset)
+ return S_FALSE;
+
+ RINOK(inStream->Seek(baseOffset + offset, STREAM_SEEK_SET, NULL));
+ UInt64 inSize = nextOffset - offset;
+ limitedStreamSpec->Init(inSize);
+
+ if (progress)
+ {
+ RINOK(progress->SetRatioInfo(&offset, &outProcessed));
+ }
+
+ UInt32 outSize = kChunkSize;
+ if (outProcessed + outSize > resource.UnpackSize)
+ outSize = (UInt32)(resource.UnpackSize - outProcessed);
+ UInt64 outSize64 = outSize;
+ if (inSize == outSize)
+ {
+ RINOK(copyCoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL));
+ }
+ else
+ {
+ if (lzxMode)
+ {
+ lzxDecoderSpec->SetKeepHistory(false);
+ RINOK(lzxDecoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL));
+ }
+ else
+ {
+ RINOK(xpressDecoder.Code(limitedStreamSpec, outStream, outSize));
+ }
+ }
+ outProcessed += outSize;
+ }
+ return S_OK;
+}
+
+HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode,
+ ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest)
+{
+ COutStreamWithSha1 *shaStreamSpec = new COutStreamWithSha1();
+ CMyComPtr<ISequentialOutStream> shaStream = shaStreamSpec;
+ shaStreamSpec->SetStream(outStream);
+ shaStreamSpec->Init(digest != NULL);
+ HRESULT result = Unpack(inStream, resource, lzxMode, shaStream, progress);
+ if (digest)
+ shaStreamSpec->Final(digest);
+ return result;
+}
+
+static HRESULT UnpackData(IInStream *inStream, const CResource &resource, bool lzxMode, CByteBuffer &buf, Byte *digest)
+{
+ size_t size = (size_t)resource.UnpackSize;
+ if (size != resource.UnpackSize)
+ return E_OUTOFMEMORY;
+ buf.Free();
+ buf.SetCapacity(size);
+
+ CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream();
+ CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
+ outStreamSpec->Init((Byte *)buf, size);
+
+ CUnpacker unpacker;
+ return unpacker.Unpack(inStream, resource, lzxMode, outStream, NULL, digest);
+}
+
+void CResource::Parse(const Byte *p)
+{
+ Flags = p[7];
+ PackSize = Get64(p) & (((UInt64)1 << 56) - 1);
+ Offset = Get64(p + 8);
+ UnpackSize = Get64(p + 16);
+}
+
+#define GetResource(p, res) res.Parse(p)
+
+static void GetStream(bool oldVersion, const Byte *p, CStreamInfo &s)
+{
+ s.Resource.Parse(p);
+ if (oldVersion)
+ {
+ s.PartNumber = 1;
+ s.Id = Get32(p + 24);
+ s.RefCount = Get32(p + 28);
+ memcpy(s.Hash, p + 32, kHashSize);
+ }
+ else
+ {
+ s.PartNumber = Get16(p + 24);
+ s.RefCount = Get32(p + 26);
+ memcpy(s.Hash, p + 30, kHashSize);
+ }
+}
+
+static const wchar_t *kLongPath = L"[LongPath]";
+
+UString CDatabase::GetItemPath(const int index1) const
+{
+ int size = 0;
+ int index = index1;
+ int newLevel;
+ for (newLevel = 0;; newLevel = 1)
+ {
+ const CItem &item = Items[index];
+ index = item.Parent;
+ if (index >= 0 || !SkipRoot)
+ size += item.Name.Length() + newLevel;
+ if (index < 0)
+ break;
+ if ((UInt32)size >= ((UInt32)1 << 16))
+ return kLongPath;
+ }
+
+ wchar_t temp[16];
+ int imageLen = 0;
+ if (ShowImageNumber)
+ {
+ ConvertUInt32ToString(-1 - index, temp);
+ imageLen = MyStringLen(temp);
+ size += imageLen + 1;
+ }
+ if ((UInt32)size >= ((UInt32)1 << 16))
+ return kLongPath;
+
+ UString path;
+ wchar_t *s = path.GetBuffer(size);
+ s[size] = 0;
+ if (ShowImageNumber)
+ {
+ memcpy(s, temp, imageLen * sizeof(wchar_t));
+ s[imageLen] = WCHAR_PATH_SEPARATOR;
+ }
+
+ index = index1;
+
+ for (newLevel = 0;; newLevel = 1)
+ {
+ const CItem &item = Items[index];
+ index = item.Parent;
+ if (index >= 0 || !SkipRoot)
+ {
+ if (newLevel)
+ s[--size] = WCHAR_PATH_SEPARATOR;
+ size -= item.Name.Length();
+ memcpy(s + size, item.Name, sizeof(wchar_t) * item.Name.Length());
+ }
+ if (index < 0)
+ {
+ path.ReleaseBuffer();
+ return path;
+ }
+ }
+}
+
+static void GetFileTimeFromMem(const Byte *p, FILETIME *ft)
+{
+ ft->dwLowDateTime = Get32(p);
+ ft->dwHighDateTime = Get32(p + 4);
+}
+
+static HRESULT ReadName(const Byte *p, int size, UString &dest)
+{
+ if (size == 0)
+ return S_OK;
+ if (Get16(p + size) != 0)
+ return S_FALSE;
+ wchar_t *s = dest.GetBuffer(size / 2);
+ for (int i = 0; i <= size; i += 2)
+ *s++ = Get16(p + i);
+ dest.ReleaseBuffer();
+ return S_OK;
+}
+
+HRESULT CDatabase::ParseDirItem(size_t pos, int parent)
+{
+ if ((pos & 7) != 0)
+ return S_FALSE;
+
+ int prevIndex = -1;
+ for (int numItems = 0;; numItems++)
+ {
+ if (OpenCallback)
+ {
+ UInt64 numFiles = Items.Size();
+ if ((numFiles & 0x3FF) == 0)
+ {
+ RINOK(OpenCallback->SetCompleted(&numFiles, NULL));
+ }
+ }
+ size_t rem = DirSize - pos;
+ if (pos < DirStartOffset || pos > DirSize || rem < 8)
+ return S_FALSE;
+ const Byte *p = DirData + pos;
+ UInt64 len = Get64(p);
+ if (len == 0)
+ {
+ if (parent < 0 && numItems != 1)
+ SkipRoot = false;
+ DirProcessed += 8;
+ return S_OK;
+ }
+ if ((len & 7) != 0 || rem < len)
+ return S_FALSE;
+ if (!IsOldVersion)
+ if (len < 0x28)
+ return S_FALSE;
+ DirProcessed += (size_t)len;
+ if (DirProcessed > DirSize)
+ return S_FALSE;
+ int extraOffset = 0;
+ if (IsOldVersion)
+ {
+ if (len < 0x40 || (/* Get32(p + 12) == 0 && */ Get32(p + 0x14) != 0))
+ {
+ extraOffset = 0x10;
+ }
+ }
+ else if (Get64(p + 8) == 0)
+ extraOffset = 0x24;
+ if (extraOffset)
+ {
+ if (prevIndex == -1)
+ return S_FALSE;
+ UInt32 fileNameLen = Get16(p + extraOffset);
+ if ((fileNameLen & 1) != 0)
+ return S_FALSE;
+ /* Probably different versions of ImageX can use different number of
+ additional ZEROs. So we don't use exact check. */
+ UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2);
+ if (((extraOffset + 2 + fileNameLen2 + 6) & ~7) > len)
+ return S_FALSE;
+
+ UString name;
+ RINOK(ReadName(p + extraOffset + 2, fileNameLen, name));
+
+ CItem &prevItem = Items[prevIndex];
+ if (name.IsEmpty() && !prevItem.HasStream())
+ {
+ if (IsOldVersion)
+ prevItem.Id = Get32(p + 8);
+ else
+ memcpy(prevItem.Hash, p + 0x10, kHashSize);
+ }
+ else
+ {
+ CItem item;
+ item.Name = prevItem.Name + L':' + name;
+ item.CTime = prevItem.CTime;
+ item.ATime = prevItem.ATime;
+ item.MTime = prevItem.MTime;
+ if (IsOldVersion)
+ {
+ item.Id = Get32(p + 8);
+ memset(item.Hash, 0, kHashSize);
+ }
+ else
+ memcpy(item.Hash, p + 0x10, kHashSize);
+ item.Attrib = 0;
+ item.Order = Order++;
+ item.Parent = parent;
+ Items.Add(item);
+ }
+ pos += (size_t)len;
+ continue;
+ }
+
+ UInt32 dirRecordSize = IsOldVersion ? kDirRecordSizeOld : kDirRecordSize;
+ if (len < dirRecordSize)
+ return S_FALSE;
+
+ CItem item;
+ item.Attrib = Get32(p + 8);
+ // item.SecurityId = Get32(p + 0xC);
+ UInt64 subdirOffset = Get64(p + 0x10);
+ UInt32 timeOffset = IsOldVersion ? 0x18: 0x28;
+ GetFileTimeFromMem(p + timeOffset, &item.CTime);
+ GetFileTimeFromMem(p + timeOffset + 8, &item.ATime);
+ GetFileTimeFromMem(p + timeOffset + 16, &item.MTime);
+ if (IsOldVersion)
+ {
+ item.Id = Get32(p + 0x10);
+ memset(item.Hash, 0, kHashSize);
+ }
+ else
+ {
+ memcpy(item.Hash, p + 0x40, kHashSize);
+ }
+ // UInt32 numStreams = Get16(p + dirRecordSize - 6);
+ UInt32 shortNameLen = Get16(p + dirRecordSize - 4);
+ UInt32 fileNameLen = Get16(p + dirRecordSize - 2);
+
+ if ((shortNameLen & 1) != 0 || (fileNameLen & 1) != 0)
+ return S_FALSE;
+
+ UInt32 shortNameLen2 = (shortNameLen == 0 ? shortNameLen : shortNameLen + 2);
+ UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2);
+
+ if (((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) > len)
+ return S_FALSE;
+ p += dirRecordSize;
+
+ RINOK(ReadName(p, fileNameLen, item.Name));
+ RINOK(ReadName(p + fileNameLen2, shortNameLen, item.ShortName));
+
+ if (parent < 0 && (shortNameLen || fileNameLen || !item.IsDir()))
+ SkipRoot = false;
+
+ /*
+ // there are some extra data for some files.
+ p -= dirRecordSize;
+ p += ((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7);
+ if (((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) != len)
+ p = p;
+ */
+
+ /*
+ if (parent >= 0)
+ {
+ UString s = GetItemPath(parent) + L"\\" + item.Name;
+ printf("\n%s %8x %S", item.IsDir() ? "D" : " ", (int)subdirOffset, (const wchar_t *)s);
+ }
+ */
+
+ if (fileNameLen == 0 && item.IsDir() && !item.HasStream())
+ item.Attrib = 0x10; // some swm archives have system/hidden attributes for root
+
+ item.Parent = parent;
+ prevIndex = Items.Add(item);
+ if (item.IsDir() && subdirOffset != 0)
+ {
+ RINOK(ParseDirItem((size_t)subdirOffset, prevIndex));
+ }
+ Items[prevIndex].Order = Order++;
+ pos += (size_t)len;
+ }
+}
+
+HRESULT CDatabase::ParseImageDirs(const CByteBuffer &buf, int parent)
+{
+ DirData = buf;
+ DirSize = buf.GetCapacity();
+
+ size_t pos = 0;
+ if (DirSize < 8)
+ return S_FALSE;
+ const Byte *p = DirData;
+ UInt32 totalLength = Get32(p);
+ if (IsOldVersion)
+ {
+ for (pos = 4;; pos += 8)
+ {
+ if (pos + 4 > DirSize)
+ return S_FALSE;
+ UInt32 n = Get32(p + pos);
+ if (n == 0)
+ break;
+ if (pos + 8 > DirSize)
+ return S_FALSE;
+ totalLength += Get32(p + pos + 4);
+ if (totalLength > DirSize)
+ return S_FALSE;
+ }
+ pos += totalLength + 4;
+ pos = (pos + 7) & ~(size_t)7;
+ if (pos > DirSize)
+ return S_FALSE;
+ }
+ else
+ {
+
+ // UInt32 numEntries = Get32(p + 4);
+ pos += 8;
+ {
+ /*
+ CRecordVector<UInt64> entryLens;
+ UInt64 sum = 0;
+ for (UInt32 i = 0; i < numEntries; i++)
+ {
+ if (pos + 8 > DirSize)
+ return S_FALSE;
+ UInt64 len = Get64(p + pos);
+ entryLens.Add(len);
+ sum += len;
+ pos += 8;
+ }
+ pos += (size_t)sum; // skip security descriptors
+ while ((pos & 7) != 0)
+ pos++;
+ if (pos != totalLength)
+ return S_FALSE;
+ */
+ if (totalLength == 0)
+ pos = 8;
+ else if (totalLength < 8)
+ return S_FALSE;
+ else
+ pos = totalLength;
+ }
+ }
+ DirStartOffset = DirProcessed = pos;
+ RINOK(ParseDirItem(pos, parent));
+ if (DirProcessed == DirSize)
+ return S_OK;
+ /* Original program writes additional 8 bytes (END_OF_ROOT_FOLDER), but
+ reference to that folder is empty */
+ if (DirProcessed == DirSize - 8 && DirProcessed - DirStartOffset == 112 &&
+ Get64(p + DirSize - 8) == 0)
+ return S_OK;
+ return S_FALSE;
+}
+
+HRESULT CHeader::Parse(const Byte *p)
+{
+ UInt32 headerSize = Get32(p + 8);
+ Version = Get32(p + 0x0C);
+ Flags = Get32(p + 0x10);
+ if (!IsSupported())
+ return S_FALSE;
+ ChunkSize = Get32(p + 0x14);
+ if (ChunkSize != kChunkSize && ChunkSize != 0)
+ return S_FALSE;
+ int offset;
+ if (IsOldVersion())
+ {
+ if (headerSize != 0x60)
+ return S_FALSE;
+ memset(Guid, 0, 16);
+ offset = 0x18;
+ PartNumber = 1;
+ NumParts = 1;
+ }
+ else
+ {
+ if (headerSize < 0x74)
+ return S_FALSE;
+ memcpy(Guid, p + 0x18, 16);
+ PartNumber = Get16(p + 0x28);
+ NumParts = Get16(p + 0x2A);
+ offset = 0x2C;
+ if (IsNewVersion())
+ {
+ NumImages = Get32(p + offset);
+ offset += 4;
+ }
+ }
+ GetResource(p + offset, OffsetResource);
+ GetResource(p + offset + 0x18, XmlResource);
+ GetResource(p + offset + 0x30, MetadataResource);
+ if (IsNewVersion())
+ {
+ if (headerSize < 0xD0)
+ return S_FALSE;
+ BootIndex = Get32(p + 0x48);
+ IntegrityResource.Parse(p + offset + 0x4C);
+ }
+ return S_OK;
+}
+
+const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 };
+
+HRESULT ReadHeader(IInStream *inStream, CHeader &h)
+{
+ Byte p[kHeaderSizeMax];
+ RINOK(ReadStream_FALSE(inStream, p, kHeaderSizeMax));
+ if (memcmp(p, kSignature, kSignatureSize) != 0)
+ return S_FALSE;
+ return h.Parse(p);
+}
+
+static HRESULT ReadStreams(bool oldVersion, IInStream *inStream, const CHeader &h, CDatabase &db)
+{
+ CByteBuffer offsetBuf;
+ RINOK(UnpackData(inStream, h.OffsetResource, h.IsLzxMode(), offsetBuf, NULL));
+ size_t i;
+ size_t streamInfoSize = oldVersion ? kStreamInfoSize + 2 : kStreamInfoSize;
+ for (i = 0; offsetBuf.GetCapacity() - i >= streamInfoSize; i += streamInfoSize)
+ {
+ CStreamInfo s;
+ GetStream(oldVersion, (const Byte *)offsetBuf + i, s);
+ if (s.PartNumber == h.PartNumber)
+ db.Streams.Add(s);
+ }
+ return (i == offsetBuf.GetCapacity()) ? S_OK : S_FALSE;
+}
+
+static bool IsEmptySha(const Byte *data)
+{
+ for (int i = 0; i < kHashSize; i++)
+ if (data[i] != 0)
+ return false;
+ return true;
+}
+
+HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, CByteBuffer &xml, IArchiveOpenCallback *openCallback)
+{
+ OpenCallback = openCallback;
+ IsOldVersion = h.IsOldVersion();
+ RINOK(UnpackData(inStream, h.XmlResource, h.IsLzxMode(), xml, NULL));
+ RINOK(ReadStreams(h.IsOldVersion(), inStream, h, *this));
+ bool needBootMetadata = !h.MetadataResource.IsEmpty();
+ Order = 0;
+ if (h.PartNumber == 1)
+ {
+ int imageIndex = 1;
+ for (int i = 0; i < Streams.Size(); i++)
+ {
+ // if (imageIndex > 1) break;
+ const CStreamInfo &si = Streams[i];
+ if (!si.Resource.IsMetadata() || si.PartNumber != h.PartNumber)
+ continue;
+ Byte hash[kHashSize];
+ CByteBuffer metadata;
+ RINOK(UnpackData(inStream, si.Resource, h.IsLzxMode(), metadata, hash));
+ if (memcmp(hash, si.Hash, kHashSize) != 0 &&
+ !(h.IsOldVersion() && IsEmptySha(si.Hash)))
+ return S_FALSE;
+ NumImages++;
+ RINOK(ParseImageDirs(metadata, -(int)(++imageIndex)));
+ if (needBootMetadata)
+ if (h.MetadataResource.Offset == si.Resource.Offset)
+ needBootMetadata = false;
+ }
+ }
+
+ if (needBootMetadata)
+ {
+ CByteBuffer metadata;
+ RINOK(UnpackData(inStream, h.MetadataResource, h.IsLzxMode(), metadata, NULL));
+ RINOK(ParseImageDirs(metadata, -1));
+ NumImages++;
+ }
+ return S_OK;
+}
+
+
+static int CompareStreamsByPos(const CStreamInfo *p1, const CStreamInfo *p2, void * /* param */)
+{
+ int res = MyCompare(p1->PartNumber, p2->PartNumber);
+ if (res != 0)
+ return res;
+ return MyCompare(p1->Resource.Offset, p2->Resource.Offset);
+}
+
+static int CompareIDs(const int *p1, const int *p2, void *param)
+{
+ const CRecordVector<CStreamInfo> &streams = *(const CRecordVector<CStreamInfo> *)param;
+ return MyCompare(streams[*p1].Id, streams[*p2].Id);
+}
+
+static int CompareHashRefs(const int *p1, const int *p2, void *param)
+{
+ const CRecordVector<CStreamInfo> &streams = *(const CRecordVector<CStreamInfo> *)param;
+ return memcmp(streams[*p1].Hash, streams[*p2].Hash, kHashSize);
+}
+
+static int FindId(const CRecordVector<CStreamInfo> &streams,
+ const CIntVector &sortedByHash, UInt32 id)
+{
+ int left = 0, right = streams.Size();
+ while (left != right)
+ {
+ int mid = (left + right) / 2;
+ int streamIndex = sortedByHash[mid];
+ UInt32 id2 = streams[streamIndex].Id;
+ if (id == id2)
+ return streamIndex;
+ if (id < id2)
+ right = mid;
+ else
+ left = mid + 1;
+ }
+ return -1;
+}
+
+static int FindHash(const CRecordVector<CStreamInfo> &streams,
+ const CIntVector &sortedByHash, const Byte *hash)
+{
+ int left = 0, right = streams.Size();
+ while (left != right)
+ {
+ int mid = (left + right) / 2;
+ int streamIndex = sortedByHash[mid];
+ UInt32 i;
+ const Byte *hash2 = streams[streamIndex].Hash;
+ for (i = 0; i < kHashSize; i++)
+ if (hash[i] != hash2[i])
+ break;
+ if (i == kHashSize)
+ return streamIndex;
+ if (hash[i] < hash2[i])
+ right = mid;
+ else
+ left = mid + 1;
+ }
+ return -1;
+}
+
+static int CompareItems(const int *a1, const int *a2, void *param)
+{
+ const CObjectVector<CItem> &items = ((CDatabase *)param)->Items;
+ const CItem &i1 = items[*a1];
+ const CItem &i2 = items[*a2];
+
+ if (i1.IsDir() != i2.IsDir())
+ return i1.IsDir() ? 1 : -1;
+ int res = MyCompare(i1.StreamIndex, i2.StreamIndex);
+ if (res != 0)
+ return res;
+ return MyCompare(i1.Order, i2.Order);
+}
+
+HRESULT CDatabase::Sort(bool skipRootDir)
+{
+ Streams.Sort(CompareStreamsByPos, NULL);
+
+ {
+ CIntVector sortedByHash;
+ {
+ for (int i = 0; i < Streams.Size(); i++)
+ sortedByHash.Add(i);
+ if (IsOldVersion)
+ sortedByHash.Sort(CompareIDs, &Streams);
+ else
+ sortedByHash.Sort(CompareHashRefs, &Streams);
+ }
+
+ for (int i = 0; i < Items.Size(); i++)
+ {
+ CItem &item = Items[i];
+ item.StreamIndex = -1;
+ if (item.HasStream())
+ if (IsOldVersion)
+ item.StreamIndex = FindId(Streams, sortedByHash, item.Id);
+ else
+ item.StreamIndex = FindHash(Streams, sortedByHash, item.Hash);
+ }
+ }
+
+ {
+ CRecordVector<bool> used;
+ int i;
+ for (i = 0; i < Streams.Size(); i++)
+ {
+ const CStreamInfo &s = Streams[i];
+ used.Add(s.Resource.IsMetadata() && s.PartNumber == 1);
+ // used.Add(false);
+ }
+ for (i = 0; i < Items.Size(); i++)
+ {
+ CItem &item = Items[i];
+ if (item.StreamIndex >= 0)
+ used[item.StreamIndex] = true;
+ }
+ for (i = 0; i < Streams.Size(); i++)
+ if (!used[i])
+ {
+ CItem item;
+ item.StreamIndex = i;
+ item.HasMetadata = false;
+ Items.Add(item);
+ }
+ }
+
+ SortedItems.Reserve(Items.Size());
+ for (int i = (skipRootDir ? 1 : 0); i < Items.Size(); i++)
+ SortedItems.Add(i);
+ SortedItems.Sort(CompareItems, this);
+ return S_OK;
+}
+
+}}
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.h b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.h
new file mode 100644
index 000000000..da3e28a56
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.h
@@ -0,0 +1,297 @@
+// Archive/WimIn.h
+
+#ifndef __ARCHIVE_WIM_IN_H
+#define __ARCHIVE_WIM_IN_H
+
+#include "Common/Buffer.h"
+#include "Common/MyString.h"
+
+#include "../../Compress/CopyCoder.h"
+#include "../../Compress/LzxDecoder.h"
+
+#include "../IArchive.h"
+
+namespace NArchive {
+namespace NWim {
+
+namespace NXpress {
+
+class CBitStream
+{
+ CInBuffer m_Stream;
+ UInt32 m_Value;
+ unsigned m_BitPos;
+public:
+ bool Create(UInt32 bufferSize) { return m_Stream.Create(bufferSize); }
+ void SetStream(ISequentialInStream *s) { m_Stream.SetStream(s); }
+ void ReleaseStream() { m_Stream.ReleaseStream(); }
+
+ void Init() { m_Stream.Init(); m_BitPos = 0; }
+ // UInt64 GetProcessedSize() const { return m_Stream.GetProcessedSize() - m_BitPos / 8; }
+ Byte DirectReadByte() { return m_Stream.ReadByte(); }
+
+ void Normalize()
+ {
+ if (m_BitPos < 16)
+ {
+ Byte b0 = m_Stream.ReadByte();
+ Byte b1 = m_Stream.ReadByte();
+ m_Value = (m_Value << 8) | b1;
+ m_Value = (m_Value << 8) | b0;
+ m_BitPos += 16;
+ }
+ }
+
+ UInt32 GetValue(unsigned numBits)
+ {
+ Normalize();
+ return (m_Value >> (m_BitPos - numBits)) & ((1 << numBits) - 1);
+ }
+
+ void MovePos(unsigned numBits) { m_BitPos -= numBits; }
+
+ UInt32 ReadBits(unsigned numBits)
+ {
+ UInt32 res = GetValue(numBits);
+ m_BitPos -= numBits;
+ return res;
+ }
+};
+
+const unsigned kNumHuffmanBits = 16;
+const UInt32 kMatchMinLen = 3;
+const UInt32 kNumLenSlots = 16;
+const UInt32 kNumPosSlots = 16;
+const UInt32 kNumPosLenSlots = kNumPosSlots * kNumLenSlots;
+const UInt32 kMainTableSize = 256 + kNumPosLenSlots;
+
+class CDecoder
+{
+ CBitStream m_InBitStream;
+ CLzOutWindow m_OutWindowStream;
+ NCompress::NHuffman::CDecoder<kNumHuffmanBits, kMainTableSize> m_MainDecoder;
+
+ HRESULT CodeSpec(UInt32 size);
+ HRESULT CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize);
+public:
+ void ReleaseStreams()
+ {
+ m_OutWindowStream.ReleaseStream();
+ m_InBitStream.ReleaseStream();
+ }
+ HRESULT Flush() { return m_OutWindowStream.Flush(); }
+ HRESULT Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize);
+};
+
+}
+
+namespace NResourceFlags
+{
+ const Byte kFree = 1;
+ const Byte kMetadata = 2;
+ const Byte Compressed = 4;
+ const Byte Spanned = 4;
+}
+
+struct CResource
+{
+ UInt64 PackSize;
+ UInt64 Offset;
+ UInt64 UnpackSize;
+ Byte Flags;
+
+ void Clear()
+ {
+ PackSize = 0;
+ Offset = 0;
+ UnpackSize = 0;
+ Flags = 0;
+ }
+ void Parse(const Byte *p);
+ void WriteTo(Byte *p) const;
+ bool IsFree() const { return (Flags & NResourceFlags::kFree) != 0; }
+ bool IsMetadata() const { return (Flags & NResourceFlags::kMetadata) != 0; }
+ bool IsCompressed() const { return (Flags & NResourceFlags::Compressed) != 0; }
+ bool IsEmpty() const { return (UnpackSize == 0); }
+};
+
+namespace NHeaderFlags
+{
+ const UInt32 kCompression = 2;
+ const UInt32 kSpanned = 8;
+ const UInt32 kRpFix = 0x80;
+ const UInt32 kXPRESS = 0x20000;
+ const UInt32 kLZX = 0x40000;
+}
+
+const UInt32 kWimVersion = 0x010D00;
+const UInt32 kHeaderSizeMax = 0xD0;
+const UInt32 kSignatureSize = 8;
+extern const Byte kSignature[kSignatureSize];
+const unsigned kChunkSizeBits = 15;
+const UInt32 kChunkSize = (1 << kChunkSizeBits);
+
+struct CHeader
+{
+ UInt32 Version;
+ UInt32 Flags;
+ UInt32 ChunkSize;
+ Byte Guid[16];
+ UInt16 PartNumber;
+ UInt16 NumParts;
+ UInt32 NumImages;
+
+ CResource OffsetResource;
+ CResource XmlResource;
+ CResource MetadataResource;
+ CResource IntegrityResource;
+ UInt32 BootIndex;
+
+ void SetDefaultFields(bool useLZX);
+
+ void WriteTo(Byte *p) const;
+ HRESULT Parse(const Byte *p);
+ bool IsCompressed() const { return (Flags & NHeaderFlags::kCompression) != 0; }
+ bool IsSupported() const { return (!IsCompressed() || (Flags & NHeaderFlags::kLZX) != 0 || (Flags & NHeaderFlags::kXPRESS) != 0 ) ; }
+ bool IsLzxMode() const { return (Flags & NHeaderFlags::kLZX) != 0; }
+ bool IsSpanned() const { return (!IsCompressed() || (Flags & NHeaderFlags::kSpanned) != 0); }
+ bool IsOldVersion() const { return (Version <= 0x010A00); }
+ bool IsNewVersion() const { return (Version > 0x010C00); }
+
+ bool AreFromOnArchive(const CHeader &h)
+ {
+ return (memcmp(Guid, h.Guid, sizeof(Guid)) == 0) && (h.NumParts == NumParts);
+ }
+};
+
+const UInt32 kHashSize = 20;
+const UInt32 kStreamInfoSize = 24 + 2 + 4 + kHashSize;
+
+struct CStreamInfo
+{
+ CResource Resource;
+ UInt16 PartNumber;
+ UInt32 RefCount;
+ UInt32 Id;
+ BYTE Hash[kHashSize];
+
+ void WriteTo(Byte *p) const;
+};
+
+const UInt32 kDirRecordSizeOld = 62;
+const UInt32 kDirRecordSize = 102;
+
+struct CItem
+{
+ UString Name;
+ UString ShortName;
+ UInt32 Attrib;
+ // UInt32 SecurityId;
+ BYTE Hash[kHashSize];
+ UInt32 Id;
+ FILETIME CTime;
+ FILETIME ATime;
+ FILETIME MTime;
+ // UInt32 ReparseTag;
+ // UInt64 HardLink;
+ // UInt16 NumStreams;
+ int StreamIndex;
+ int Parent;
+ unsigned Order;
+ bool HasMetadata;
+ CItem(): HasMetadata(true), StreamIndex(-1), Id(0) {}
+ bool IsDir() const { return HasMetadata && ((Attrib & 0x10) != 0); }
+ bool HasStream() const
+ {
+ for (unsigned i = 0; i < kHashSize; i++)
+ if (Hash[i] != 0)
+ return true;
+ return Id != 0;
+ }
+};
+
+class CDatabase
+{
+ const Byte *DirData;
+ size_t DirSize;
+ size_t DirProcessed;
+ size_t DirStartOffset;
+ int Order;
+ IArchiveOpenCallback *OpenCallback;
+
+ HRESULT ParseDirItem(size_t pos, int parent);
+ HRESULT ParseImageDirs(const CByteBuffer &buf, int parent);
+
+public:
+ CRecordVector<CStreamInfo> Streams;
+ CObjectVector<CItem> Items;
+ CIntVector SortedItems;
+ int NumImages;
+ bool SkipRoot;
+ bool ShowImageNumber;
+
+ bool IsOldVersion;
+
+ UInt64 GetUnpackSize() const
+ {
+ UInt64 res = 0;
+ for (int i = 0; i < Streams.Size(); i++)
+ res += Streams[i].Resource.UnpackSize;
+ return res;
+ }
+
+ UInt64 GetPackSize() const
+ {
+ UInt64 res = 0;
+ for (int i = 0; i < Streams.Size(); i++)
+ res += Streams[i].Resource.PackSize;
+ return res;
+ }
+
+ void Clear()
+ {
+ Streams.Clear();
+ Items.Clear();
+ SortedItems.Clear();
+ NumImages = 0;
+
+ SkipRoot = true;
+ ShowImageNumber = true;
+ IsOldVersion = false;
+ }
+
+ UString GetItemPath(int index) const;
+
+ HRESULT Open(IInStream *inStream, const CHeader &h, CByteBuffer &xml, IArchiveOpenCallback *openCallback);
+
+ void DetectPathMode()
+ {
+ ShowImageNumber = (NumImages != 1);
+ }
+
+ HRESULT Sort(bool skipRootDir);
+};
+
+HRESULT ReadHeader(IInStream *inStream, CHeader &header);
+
+class CUnpacker
+{
+ NCompress::CCopyCoder *copyCoderSpec;
+ CMyComPtr<ICompressCoder> copyCoder;
+
+ NCompress::NLzx::CDecoder *lzxDecoderSpec;
+ CMyComPtr<ICompressCoder> lzxDecoder;
+
+ NXpress::CDecoder xpressDecoder;
+
+ CByteBuffer sizesBuf;
+ HRESULT Unpack(IInStream *inStream, const CResource &res, bool lzxMode,
+ ISequentialOutStream *outStream, ICompressProgressInfo *progress);
+public:
+ HRESULT Unpack(IInStream *inStream, const CResource &res, bool lzxMode,
+ ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest);
+};
+
+}}
+
+#endif
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimRegister.cpp b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimRegister.cpp
new file mode 100644
index 000000000..8da914360
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/Wim/WimRegister.cpp
@@ -0,0 +1,18 @@
+// WimRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/RegisterArc.h"
+
+#include "WimHandler.h"
+static IInArchive *CreateArc() { return new NArchive::NWim::CHandler; }
+#ifndef EXTRACT_ONLY
+static IOutArchive *CreateArcOut() { return new NArchive::NWim::COutHandler; }
+#else
+#define CreateArcOut 0
+#endif
+
+static CArcInfo g_ArcInfo =
+ { L"wim", L"wim swm", 0, 0xE6, { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }, 8, false, CreateArc, CreateArcOut };
+
+REGISTER_ARC(Wim)