diff options
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/Wim')
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/StdAfx.h | 8 | ||||
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.cpp | 660 | ||||
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandler.h | 77 | ||||
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/WimHandlerOut.cpp | 639 | ||||
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.cpp | 855 | ||||
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/WimIn.h | 297 | ||||
-rw-r--r-- | src/libs/7zip/win/CPP/7zip/Archive/Wim/WimRegister.cpp | 18 |
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) |