diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2007-07-11 04:00:00 +0400 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:15:52 +0300 |
commit | 7038848692e7049234f223703522681a19db49a5 (patch) | |
tree | 38c5acef39a775a1f58f81b13be81fc6ef8c72e3 /CPP/7zip/Archive/Wim | |
parent | fd8b1d78b496fe38193bf8c5e86af3b43f0b022d (diff) |
4.49 beta
Diffstat (limited to 'CPP/7zip/Archive/Wim')
-rwxr-xr-x | CPP/7zip/Archive/Wim/StdAfx.h | 8 | ||||
-rwxr-xr-x | CPP/7zip/Archive/Wim/WimHandler.cpp | 477 | ||||
-rwxr-xr-x | CPP/7zip/Archive/Wim/WimHandler.h | 60 | ||||
-rwxr-xr-x | CPP/7zip/Archive/Wim/WimIn.cpp | 467 | ||||
-rwxr-xr-x | CPP/7zip/Archive/Wim/WimIn.h | 139 | ||||
-rwxr-xr-x | CPP/7zip/Archive/Wim/WimRegister.cpp | 13 | ||||
-rwxr-xr-x | CPP/7zip/Archive/Wim/wim.ico | bin | 0 -> 3638 bytes |
7 files changed, 1164 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Wim/StdAfx.h b/CPP/7zip/Archive/Wim/StdAfx.h new file mode 100755 index 00000000..e7fb6986 --- /dev/null +++ b/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/CPP/7zip/Archive/Wim/WimHandler.cpp b/CPP/7zip/Archive/Wim/WimHandler.cpp new file mode 100755 index 00000000..aa9300a2 --- /dev/null +++ b/CPP/7zip/Archive/Wim/WimHandler.cpp @@ -0,0 +1,477 @@ +// WimHandler.cpp + +#include "StdAfx.h" + +#include "Common/IntToString.h" +#include "Common/Defs.h" +#include "Common/ComTry.h" + +#include "Windows/PropVariant.h" + +#include "../../Common/StreamUtils.h" +#include "../../Common/ProgressUtils.h" + +#include "WimHandler.h" + +using namespace NWindows; + +namespace NArchive { +namespace NWim { + +#define WIM_DETAILS + +#ifdef WIM_DETAILS + +enum +{ + kpidVolume = kpidUserDefined, + kpidOffset, + kpidLinks +}; + +#endif + +STATPROPSTG kProperties[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsFolder, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackedSize, VT_UI8}, + { NULL, kpidAttributes, VT_UI8}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidCreationTime, VT_FILETIME}, + { NULL, kpidLastAccessTime, VT_FILETIME}, + { NULL, kpidLastWriteTime, VT_FILETIME} + + #ifdef WIM_DETAILS + , { L"Volume", kpidVolume, VT_UI4} + , { L"Offset", kpidOffset, VT_UI8} + , { L"Links", kpidLinks, VT_UI4} + #endif +}; + +static const wchar_t *kStreamsNamePrefix = L"Files" WSTRING_PATH_SEPARATOR; +static const wchar_t *kMethodLZX = L"LZX"; +static const wchar_t *kMethodCopy = L"Copy"; + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID /* propID */, PROPVARIANT *value) +{ + value->vt = VT_EMPTY; + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) +{ + *numProperties = sizeof(kProperties) / sizeof(kProperties[0]); + return S_OK; +} + +STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, + BSTR *name, PROPID *propID, VARTYPE *varType) +{ + if(index >= sizeof(kProperties) / sizeof(kProperties[0])) + return E_INVALIDARG; + const STATPROPSTG &srcItem = kProperties[index]; + *propID = srcItem.propid; + *varType = srcItem.vt; + if (srcItem.lpwstrName == 0) + *name = 0; + else + *name = ::SysAllocString(srcItem.lpwstrName); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) +{ + *numProperties = 0; + return S_OK; +} + +STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32 /* index */, + BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */) +{ + return E_INVALIDARG; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant propVariant; + if (index < (UInt32)m_Database.Items.Size()) + { + const CItem &item = m_Database.Items[index]; + const CStreamInfo *si = NULL; + if (item.StreamIndex >= 0) + si = &m_Database.Streams[item.StreamIndex]; + + switch(propID) + { + case kpidPath: + if (item.HasMetadata) + propVariant = item.Name; + else + { + wchar_t sz[32]; + ConvertUInt64ToString(item.StreamIndex, sz); + UString s = sz; + while (s.Length() < m_NameLenForStreams) + s = L'0' + s; + s = UString(kStreamsNamePrefix) + s; + propVariant = s; + break; + } + break; + case kpidIsFolder: + propVariant = item.IsDirectory(); + break; + case kpidAttributes: + if (item.HasMetadata) + propVariant = item.Attributes; + break; + case kpidCreationTime: + if (item.HasMetadata) + propVariant = item.CreationTime; + break; + case kpidLastAccessTime: + if (item.HasMetadata) + propVariant = item.LastAccessTime; + break; + case kpidLastWriteTime: + if (item.HasMetadata) + propVariant = item.LastWriteTime; + break; + case kpidPackedSize: + if (si) + propVariant = si->Resource.PackSize; + else + propVariant = (UInt64)0; + break; + case kpidSize: + if (si) + propVariant = si->Resource.UnpackSize; + else + propVariant = (UInt64)0; + break; + case kpidMethod: + if (si) + if (si->Resource.IsCompressed()) + propVariant = kMethodLZX; + else + propVariant = kMethodCopy; + break; + #ifdef WIM_DETAILS + case kpidVolume: + if (si) + propVariant = (UInt32)si->PartNumber; + break; + case kpidOffset: + if (si) + propVariant = (UInt64)si->Resource.Offset; + break; + case kpidLinks: + if (si) + propVariant = (UInt32)si->RefCount; + else + propVariant = (UInt64)0; + break; + #endif + } + } + else + { + index -= m_Database.Items.Size(); + { + switch(propID) + { + case kpidPath: + { + wchar_t sz[32]; + ConvertUInt64ToString(m_Xmls[index].VolIndex, sz); + UString s = (UString)sz + L".xml"; + propVariant = s; + break; + } + case kpidIsFolder: + propVariant = false; + break; + case kpidPackedSize: + case kpidSize: + propVariant = (UInt64)m_Xmls[index].Data.GetCapacity(); + break; + case kpidMethod: + propVariant = L"Copy"; + break; + } + } + } + propVariant.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[32]; + ConvertUInt64ToString((index), s); + return _before + (UString)s + _after; + } +}; + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + Close(); + try + { + 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; + } + if (firstVolumeIndex >= 0) + if (!header.AreFromOnArchive(m_Volumes[firstVolumeIndex].Header)) + break; + if (m_Volumes.Size() > header.PartNumber && m_Volumes[header.PartNumber].Stream) + break; + CXml xml; + xml.VolIndex = header.PartNumber; + res = OpenArchive(curStream, header, xml.Data, m_Database); + if (res != S_OK) + { + if (i == 1) + return res; + if (res == S_FALSE) + continue; + return res; + } + + while (m_Volumes.Size() <= header.PartNumber) + m_Volumes.Add(CVolume()); + CVolume &volume = m_Volumes[header.PartNumber]; + volume.Header = header; + volume.Stream = curStream; + + firstVolumeIndex = header.PartNumber; + + bool needAddXml = true; + if (m_Xmls.Size() != 0) + if (xml.Data == m_Xmls[0].Data) + needAddXml = false; + if (needAddXml) + m_Xmls.Add(xml); + + if (i == 1) + { + if (header.PartNumber != 1) + break; + if (!openVolumeCallback) + break; + numVolumes = header.NumParts; + { + NCOM::CPropVariant propVariant; + RINOK(openVolumeCallback->GetProperty(kpidName, &propVariant)); + if (propVariant.vt != VT_BSTR) + break; + seqName.InitName(propVariant.bstrVal); + } + } + } + + RINOK(SortDatabase(m_Database)); + + wchar_t sz[32]; + ConvertUInt64ToString(m_Database.Streams.Size(), sz); + m_NameLenForStreams = MyStringLen(sz); + } + catch(...) + { + return S_FALSE; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + m_Database.Clear(); + m_Volumes.Clear(); + m_Xmls.Clear(); + m_NameLenForStreams = 0; + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems, + Int32 _aTestMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == UInt32(-1)); + + if (allFilesMode) + numItems = m_Database.Items.Size() + m_Xmls.Size(); + if (numItems == 0) + return S_OK; + bool testMode = (_aTestMode != 0); + + UInt32 i; + UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) + { + UInt32 index = allFilesMode ? i : indices[i]; + if (index < (UInt32)m_Database.Items.Size()) + { + int streamIndex = m_Database.Items[index].StreamIndex; + if (streamIndex >= 0) + { + const CStreamInfo &si = m_Database.Streams[streamIndex]; + totalSize += si.Resource.UnpackSize; + } + } + else + totalSize += m_Xmls[index - (UInt32)m_Database.Items.Size()].Data.GetCapacity(); + } + + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize = 0; + + int prevSuccessStreamIndex = -1; + + CUnpacker unpacker; + + CLocalProgress *localProgressSpec = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = localProgressSpec; + localProgressSpec->Init(extractCallback, false); + + CLocalCompressProgressInfo *localCompressProgressSpec = new CLocalCompressProgressInfo; + CMyComPtr<ICompressProgressInfo> compressProgress = localCompressProgressSpec; + + for (i = 0; i < numItems; currentTotalSize += currentItemSize) + { + currentItemSize = 0; + RINOK(extractCallback->SetCompleted(¤tTotalSize)); + UInt32 index = allFilesMode ? i : indices[i]; + i++; + Int32 askMode = testMode ? + NArchive::NExtract::NAskMode::kTest : + NArchive::NExtract::NAskMode::kExtract; + + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + if (index >= (UInt32)m_Database.Items.Size()) + { + if(!testMode && (!realOutStream)) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + const CByteBuffer &data = m_Xmls[index - (UInt32)m_Database.Items.Size()].Data; + currentItemSize = data.GetCapacity(); + if (realOutStream) + { + RINOK(WriteStream(realOutStream, (const Byte *)data, (UInt32)data.GetCapacity(), NULL)); + realOutStream.Release(); + } + RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK)); + continue; + } + + const CItem &item = m_Database.Items[index]; + int streamIndex = item.StreamIndex; + if (streamIndex < 0) + { + if(!testMode && (!realOutStream)) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(item.HasStream() ? + NArchive::NExtract::NOperationResult::kDataError : + NArchive::NExtract::NOperationResult::kOK)); + continue; + } + + const CStreamInfo &si = m_Database.Streams[streamIndex]; + currentItemSize = si.Resource.UnpackSize; + + if(!testMode && (!realOutStream)) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + Int32 opRes = NArchive::NExtract::NOperationResult::kOK; + if (streamIndex != prevSuccessStreamIndex || realOutStream) + { + Byte digest[20]; + localCompressProgressSpec->Init(progress, ¤tTotalSize, ¤tTotalSize); + HRESULT res = unpacker.Unpack(m_Volumes[si.PartNumber].Stream, si.Resource, realOutStream, compressProgress, digest); + if (res == S_OK) + { + if (memcmp(digest, si.Hash, kHashSize) == 0) + prevSuccessStreamIndex = streamIndex; + else + opRes = NArchive::NExtract::NOperationResult::kCRCError; + } + else if (res == S_FALSE) + opRes = NArchive::NExtract::NOperationResult::kDataError; + else + return res; + } + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = m_Database.Items.Size() + m_Xmls.Size(); + return S_OK; +} + +}} diff --git a/CPP/7zip/Archive/Wim/WimHandler.h b/CPP/7zip/Archive/Wim/WimHandler.h new file mode 100755 index 00000000..5142d785 --- /dev/null +++ b/CPP/7zip/Archive/Wim/WimHandler.h @@ -0,0 +1,60 @@ +// WimHandler.h + +#ifndef __ARCHIVE_WIM_HANDLER_H +#define __ARCHIVE_WIM_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" +#include "WimIn.h" + +namespace NArchive { +namespace NWim { + +struct CVolume +{ + CHeader Header; + CMyComPtr<IInStream> Stream; +}; + +struct CXml +{ + CByteBuffer Data; + UInt16 VolIndex; +}; + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IInArchive) + + STDMETHOD(Open)(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openArchiveCallback); + STDMETHOD(Close)(); + STDMETHOD(GetNumberOfItems)(UInt32 *numItems); + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); + STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback); + + STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value); + + STDMETHOD(GetNumberOfProperties)(UInt32 *numProperties); + STDMETHOD(GetPropertyInfo)(UInt32 index, + BSTR *name, PROPID *propID, VARTYPE *varType); + + STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProperties); + STDMETHOD(GetArchivePropertyInfo)(UInt32 index, + BSTR *name, PROPID *propID, VARTYPE *varType); + +private: + CDatabase m_Database; + CObjectVector<CVolume> m_Volumes; + CObjectVector<CXml> m_Xmls; + int m_NameLenForStreams; +}; + +}} + +#endif diff --git a/CPP/7zip/Archive/Wim/WimIn.cpp b/CPP/7zip/Archive/Wim/WimIn.cpp new file mode 100755 index 00000000..e99cb37b --- /dev/null +++ b/CPP/7zip/Archive/Wim/WimIn.cpp @@ -0,0 +1,467 @@ +// Archive/WimIn.cpp + +#include "StdAfx.h" + +#include "Common/MyCom.h" +#include "Common/IntToString.h" + +#include "../../Common/StreamUtils.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/LimitedStreams.h" + +#include "../Common/OutStreamWithSha1.h" + +#include "WimIn.h" + +namespace NArchive{ +namespace NWim{ + +static const int kChunkSizeBits = 15; +static const UInt32 kChunkSize = (1 << kChunkSizeBits); + +static HRESULT ReadBytes(ISequentialInStream *inStream, void *data, UInt32 size) +{ + UInt32 realProcessedSize; + RINOK(ReadStream(inStream, data, size, &realProcessedSize)); + return (realProcessedSize == size) ? S_OK : S_FALSE; +} + +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__) +#define WIM_LITTLE_ENDIAN_UNALIGN +#endif + +#ifdef WIM_LITTLE_ENDIAN_UNALIGN +static inline UInt16 GetUInt16FromMem(const Byte *p) { return *(const UInt16 *)p; } +static inline UInt32 GetUInt32FromMem(const Byte *p) { return *(const UInt32 *)p; } +static inline UInt64 GetUInt64FromMem(const Byte *p) { return *(const UInt64 *)p; } +#else +static UInt16 GetUInt16FromMem(const Byte *p) { return p[0] | ((UInt16)p[1] << 8); } +static UInt32 GetUInt32FromMem(const Byte *p) { return p[0] | ((UInt32)p[1] << 8) | ((UInt32)p[2] << 16) | ((UInt32)p[3] << 24); } +static UInt64 GetUInt64FromMem(const Byte *p) { return GetUInt32FromMem(p) | ((UInt64)GetUInt32FromMem(p + 4) << 32); } +#endif + +static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) +{ + ft->dwLowDateTime = GetUInt32FromMem(p); + ft->dwHighDateTime = GetUInt32FromMem(p + 4); +} + +HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, + 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); + UInt32 sizesBufSize = (UInt32)sizesBufSize64; + if (sizesBufSize != sizesBufSize64) + return E_OUTOFMEMORY; + if (sizesBufSize > sizesBuf.GetCapacity()) + { + sizesBuf.Free(); + sizesBuf.SetCapacity(sizesBufSize); + } + RINOK(ReadBytes(inStream, (Byte *)sizesBuf, sizesBufSize)); + const Byte *p = (const Byte *)sizesBuf; + + if (!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) + { + if (entrySize == 4) + offset = GetUInt32FromMem(p); + else + offset = GetUInt64FromMem(p); + p += entrySize; + } + UInt64 nextOffset = resource.PackSize - sizesBufSize64; + if (i + 1 < (UInt32)numChunks) + if (entrySize == 4) + nextOffset = GetUInt32FromMem(p); + else + nextOffset = GetUInt64FromMem(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; + lzxDecoderSpec->SetKeepHistory(false, 0); + ICompressCoder *coder = (inSize == outSize) ? copyCoder : lzxDecoder; + RINOK(coder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); + outProcessed += outSize; + } + return S_OK; +} + +HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, + 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, shaStream, progress); + if (digest) + shaStreamSpec->Final(digest); + return result; +} + +static HRESULT UnpackData(IInStream *inStream, const CResource &resource, CByteBuffer &buf, Byte *digest) +{ + size_t size = (size_t)resource.UnpackSize; + if (size != resource.UnpackSize) + return E_OUTOFMEMORY; + buf.Free(); + buf.SetCapacity(size); + + CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2(); + CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; + outStreamSpec->Init((Byte *)buf, size); + + CUnpacker unpacker; + return unpacker.Unpack(inStream, resource, outStream, NULL, digest); +} + +static const UInt32 kSignatureSize = 8; +static const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }; + +static void GetResource(const Byte *p, CResource &res) +{ + res.Flags = p[7]; + res.PackSize = GetUInt64FromMem(p) & (((UInt64)1 << 56) - 1); + res.Offset = GetUInt64FromMem(p + 8); + res.UnpackSize = GetUInt64FromMem(p + 16); +} + +static void GetStream(const Byte *p, CStreamInfo &s) +{ + GetResource(p, s.Resource); + s.PartNumber = GetUInt16FromMem(p + 24); + s.RefCount = GetUInt32FromMem(p + 26); + memcpy(s.Hash, p + 30, kHashSize); +} + +static HRESULT ParseDirItem(const Byte *base, size_t pos, size_t size, + const UString &prefix, CObjectVector<CItem> &items) +{ + for (;;) + { + if (pos + 8 > size) + return S_FALSE; + const Byte *p = base + pos; + UInt64 length = GetUInt64FromMem(p); + if (length == 0) + return S_OK; + if (pos + 102 > size || pos + length + 8 > size || length > ((UInt64)1 << 62)) + return S_FALSE; + CItem item; + item.Attributes = GetUInt32FromMem(p + 8); + // item.SecurityId = GetUInt32FromMem(p + 0xC); + UInt64 subdirOffset = GetUInt64FromMem(p + 0x10); + GetFileTimeFromMem(p + 0x28, &item.CreationTime); + GetFileTimeFromMem(p + 0x30, &item.LastAccessTime); + GetFileTimeFromMem(p + 0x38, &item.LastWriteTime); + memcpy(item.Hash, p + 0x40, kHashSize); + + // UInt16 shortNameLen = GetUInt16FromMem(p + 98); + UInt16 fileNameLen = GetUInt16FromMem(p + 100); + + size_t tempPos = pos + 102; + if (tempPos + fileNameLen > size) + return S_FALSE; + + wchar_t *sz = item.Name.GetBuffer(prefix.Length() + fileNameLen / 2 + 1); + MyStringCopy(sz, (const wchar_t *)prefix); + sz += prefix.Length(); + for (UInt16 i = 0; i + 2 <= fileNameLen; i += 2) + *sz++ = GetUInt16FromMem(base + tempPos + i); + *sz++ = '\0'; + item.Name.ReleaseBuffer(); + if (fileNameLen == 0 && item.IsDirectory() && !item.HasStream()) + { + item.Attributes = 0x10; // some swm archives have system/hidden attributes for root + item.Name.Delete(item.Name.Length() - 1); + } + items.Add(item); + pos += (size_t)length; + if (item.IsDirectory() && (subdirOffset != 0)) + { + if (subdirOffset >= size) + return S_FALSE; + RINOK(ParseDirItem(base, (size_t)subdirOffset, size, item.Name + WCHAR_PATH_SEPARATOR, items)); + } + } +} + +static HRESULT ParseDir(const Byte *base, size_t size, + const UString &prefix, CObjectVector<CItem> &items) +{ + size_t pos = 0; + if (pos + 8 > size) + return S_FALSE; + const Byte *p = base + pos; + UInt32 totalLength = GetUInt32FromMem(p); + // UInt32 numEntries = GetUInt32FromMem(p + 4); + pos += 8; + { + /* + CRecordVector<UInt64> entryLens; + UInt64 sum = 0; + for (UInt32 i = 0; i < numEntries; i++) + { + if (pos + 8 > size) + return S_FALSE; + UInt64 len = GetUInt64FromMem(p + pos); + entryLens.Add(len); + sum += len; + pos += 8; + } + pos += sum; // skeep security descriptors + while ((pos & 7) != 0) + pos++; + if (pos != totalLength) + return S_FALSE; + */ + pos = totalLength; + } + return ParseDirItem(base, pos, size, prefix, items); +} + +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 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); +} + +int CompareItems(void *const *a1, void *const *a2, void * /* param */) +{ + const CItem &i1 = **((const CItem **)a1); + const CItem &i2 = **((const CItem **)a2); + + if (i1.IsDirectory() != i2.IsDirectory()) + return (i1.IsDirectory()) ? 1 : -1; + if (i1.IsDirectory()) + return -MyStringCompareNoCase(i1.Name, i2.Name); + + int res = MyCompare(i1.StreamIndex, i2.StreamIndex); + if (res != 0) + return res; + return MyStringCompareNoCase(i1.Name, i2.Name); +} + +static int FindHash(const CRecordVector<CStreamInfo> &streams, + const CRecordVector<int> &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; +} + +HRESULT ReadHeader(IInStream *inStream, CHeader &h) +{ + const UInt32 kHeaderSizeMax = 0xD0; + Byte p[kHeaderSizeMax]; + RINOK(ReadBytes(inStream, p, kHeaderSizeMax)); + UInt32 haderSize = GetUInt32FromMem(p + 8); + if (memcmp(p, kSignature, kSignatureSize) != 0) + return S_FALSE; + if (haderSize < 0x74) + return S_FALSE; + h.Version = GetUInt32FromMem(p + 0x0C); + h.Flags = GetUInt32FromMem(p + 0x10); + if (!h.IsSupported()) + return S_FALSE; + if (GetUInt32FromMem(p + 0x14) != kChunkSize) + return S_FALSE; + memcpy(h.Guid, p + 0x18, 16); + h.PartNumber = GetUInt16FromMem(p + 0x28); + h.NumParts = GetUInt16FromMem(p + 0x2A); + int offset = 0x2C; + if (h.IsNewVersion()) + { + h.NumImages = GetUInt32FromMem(p + offset); + offset += 4; + } + GetResource(p + offset, h.OffsetResource); + GetResource(p + offset + 0x18, h.XmlResource); + GetResource(p + offset + 0x30, h.MetadataResource); + /* + if (h.IsNewVersion()) + { + if (haderSize < 0xD0) + return S_FALSE; + GetResource(p + offset + 0x4C, h.IntegrityResource); + h.BootIndex = GetUInt32FromMem(p + 0x48); + } + */ + return S_OK; +} + +HRESULT ReadStreams(IInStream *inStream, const CHeader &h, CDatabase &db) +{ + CByteBuffer offsetBuf; + RINOK(UnpackData(inStream, h.OffsetResource, offsetBuf, NULL)); + for (size_t i = 0; i + kStreamInfoSize <= offsetBuf.GetCapacity(); i += kStreamInfoSize) + { + CStreamInfo s; + GetStream((const Byte *)offsetBuf + i, s); + if (s.PartNumber == h.PartNumber) + db.Streams.Add(s); + } + return S_OK; +} + +HRESULT OpenArchive(IInStream *inStream, const CHeader &h, CByteBuffer &xml, CDatabase &db) +{ + RINOK(UnpackData(inStream, h.XmlResource, xml, NULL)); + + RINOK(ReadStreams(inStream, h, db)); + bool needBootMetadata = !h.MetadataResource.IsEmpty(); + if (h.PartNumber == 1) + { + int imageIndex = 1; + for (int j = 0; j < db.Streams.Size(); j++) + { + // if (imageIndex > 1) break; + const CStreamInfo &si = db.Streams[j]; + if (!si.Resource.IsMetadata() || si.PartNumber != h.PartNumber) + continue; + Byte hash[kHashSize]; + CByteBuffer metadata; + RINOK(UnpackData(inStream, si.Resource, metadata, hash)); + if (memcmp(hash, si.Hash, kHashSize) != 0) + return S_FALSE; + wchar_t sz[32]; + ConvertUInt64ToString(imageIndex++, sz); + UString s = sz; + s += WCHAR_PATH_SEPARATOR; + RINOK(ParseDir(metadata, metadata.GetCapacity(), s, db.Items)); + if (needBootMetadata) + if (h.MetadataResource.Offset == si.Resource.Offset) + needBootMetadata = false; + } + } + + if (needBootMetadata) + { + CByteBuffer metadata; + RINOK(UnpackData(inStream, h.MetadataResource, metadata, NULL)); + RINOK(ParseDir(metadata, metadata.GetCapacity(), L"0" WSTRING_PATH_SEPARATOR, db.Items)); + } + return S_OK; +} + + +HRESULT SortDatabase(CDatabase &db) +{ + db.Streams.Sort(CompareStreamsByPos, NULL); + + { + CRecordVector<int> sortedByHash; + { + for (int j = 0; j < db.Streams.Size(); j++) + sortedByHash.Add(j); + sortedByHash.Sort(CompareHashRefs, &db.Streams); + } + + for (int i = 0; i < db.Items.Size(); i++) + { + CItem &item = db.Items[i]; + item.StreamIndex = -1; + if (item.HasStream()) + item.StreamIndex = FindHash(db.Streams, sortedByHash, item.Hash); + } + } + + { + CRecordVector<bool> used; + int j; + for (j = 0; j < db.Streams.Size(); j++) + { + const CStreamInfo &s = db.Streams[j]; + used.Add(s.Resource.IsMetadata() && s.PartNumber == 1); + } + for (int i = 0; i < db.Items.Size(); i++) + { + CItem &item = db.Items[i]; + if (item.StreamIndex >= 0) + used[item.StreamIndex] = true; + } + for (j = 0; j < db.Streams.Size(); j++) + if (!used[j]) + { + CItem item; + item.StreamIndex = j; + item.HasMetadata = false; + db.Items.Add(item); + } + } + + db.Items.Sort(CompareItems, NULL); + return S_OK; +} + +}} diff --git a/CPP/7zip/Archive/Wim/WimIn.h b/CPP/7zip/Archive/Wim/WimIn.h new file mode 100755 index 00000000..bc74f61b --- /dev/null +++ b/CPP/7zip/Archive/Wim/WimIn.h @@ -0,0 +1,139 @@ +// Archive/WimIn.h + +#ifndef __ARCHIVE_WIM_IN_H +#define __ARCHIVE_WIM_IN_H + +#include "Common/MyString.h" +#include "Common/Buffer.h" + +#include "../../Compress/Lzx/LzxDecoder.h" +#include "../../Compress/Copy/CopyCoder.h" + +namespace NArchive { +namespace NWim { + +namespace NResourceFlags +{ + const Byte Compressed = 4; + const Byte kMetadata = 2; +} + +struct CResource +{ + UInt64 PackSize; + UInt64 Offset; + UInt64 UnpackSize; + Byte Flags; + bool IsCompressed() const { return (Flags & NResourceFlags::Compressed) != 0; } + bool IsMetadata() const { return (Flags & NResourceFlags::kMetadata) != 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; +} + +struct CHeader +{ + UInt32 Flags; + UInt32 Version; + // UInt32 ChunkSize; + UInt16 PartNumber; + UInt16 NumParts; + UInt32 NumImages; + Byte Guid[16]; + CResource OffsetResource; + CResource XmlResource; + CResource MetadataResource; + /* + CResource IntegrityResource; + UInt32 BootIndex; + */ + bool IsCompressed() const { return (Flags & NHeaderFlags::kCompression) != 0; } + bool IsSupported() const { return (!IsCompressed() || (Flags & NHeaderFlags::kLZX) != 0); } + bool IsSpanned() const { return (!IsCompressed() || (Flags & NHeaderFlags::kSpanned) != 0); } + + 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; + BYTE Hash[kHashSize]; +}; + +struct CItem +{ + UString Name; + UInt32 Attributes; + // UInt32 SecurityId; + BYTE Hash[kHashSize]; + FILETIME CreationTime; + FILETIME LastAccessTime; + FILETIME LastWriteTime; + // UInt32 ReparseTag; + // UInt64 HardLink; + // UInt16 NumStreams; + // UInt16 ShortNameLen; + int StreamIndex; + bool HasMetadata; + CItem(): HasMetadata(true), StreamIndex(-1) {} + bool IsDirectory() const { return HasMetadata && ((Attributes & 0x10) != 0); } + bool HasStream() const + { + for (int i = 0; i < kHashSize; i++) + if (Hash[i] != 0) + return true; + return false; + } +}; + +struct CDatabase +{ + CRecordVector<CStreamInfo> Streams; + CObjectVector<CItem> Items; + void Clear() + { + Streams.Clear(); + Items.Clear(); + } +}; + +HRESULT ReadHeader(IInStream *inStream, CHeader &header); +HRESULT OpenArchive(IInStream *inStream, const CHeader &header, CByteBuffer &xml, CDatabase &database); +HRESULT SortDatabase(CDatabase &database); + +class CUnpacker +{ + NCompress::CCopyCoder *copyCoderSpec; + CMyComPtr<ICompressCoder> copyCoder; + + NCompress::NLzx::CDecoder *lzxDecoderSpec; + CMyComPtr<ICompressCoder> lzxDecoder; + + CByteBuffer sizesBuf; + HRESULT Unpack(IInStream *inStream, const CResource &res, + ISequentialOutStream *outStream, ICompressProgressInfo *progress); +public: + HRESULT Unpack(IInStream *inStream, const CResource &res, + ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest); +}; + +}} + +#endif diff --git a/CPP/7zip/Archive/Wim/WimRegister.cpp b/CPP/7zip/Archive/Wim/WimRegister.cpp new file mode 100755 index 00000000..22344b3a --- /dev/null +++ b/CPP/7zip/Archive/Wim/WimRegister.cpp @@ -0,0 +1,13 @@ +// WimRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "WimHandler.h" +static IInArchive *CreateArc() { return new NArchive::NWim::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Wim", L"wim swm", 0, 0xE6, { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }, 8, false, CreateArc, 0 }; + +REGISTER_ARC(Wim) diff --git a/CPP/7zip/Archive/Wim/wim.ico b/CPP/7zip/Archive/Wim/wim.ico Binary files differnew file mode 100755 index 00000000..887975e6 --- /dev/null +++ b/CPP/7zip/Archive/Wim/wim.ico |