diff options
Diffstat (limited to 'CPP/7zip/Archive/Wim/WimIn.cpp')
-rwxr-xr-x | CPP/7zip/Archive/Wim/WimIn.cpp | 445 |
1 files changed, 301 insertions, 144 deletions
diff --git a/CPP/7zip/Archive/Wim/WimIn.cpp b/CPP/7zip/Archive/Wim/WimIn.cpp index 90c3c2c2..b65b4c44 100755 --- a/CPP/7zip/Archive/Wim/WimIn.cpp +++ b/CPP/7zip/Archive/Wim/WimIn.cpp @@ -18,11 +18,8 @@ #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) -namespace NArchive{ -namespace NWim{ - -static const int kChunkSizeBits = 15; -static const UInt32 kChunkSize = (1 << kChunkSizeBits); +namespace NArchive { +namespace NWim { namespace NXpress { @@ -44,7 +41,7 @@ HRESULT CDecoder::CodeSpec(UInt32 outSize) { { Byte levels[kMainTableSize]; - for (int i = 0; i < kMainTableSize; i += 2) + for (unsigned i = 0; i < kMainTableSize; i += 2) { Byte b = m_InBitStream.DirectReadByte(); levels[i] = b & 0xF; @@ -128,12 +125,6 @@ HRESULT CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outS } -static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) -{ - ft->dwLowDateTime = Get32(p); - ft->dwHighDateTime = Get32(p + 4); -} - HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { @@ -257,9 +248,6 @@ static HRESULT UnpackData(IInStream *inStream, const CResource &resource, bool l return unpacker.Unpack(inStream, resource, lzxMode, outStream, NULL, digest); } -static const UInt32 kSignatureSize = 8; -static const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }; - void CResource::Parse(const Byte *p) { Flags = p[7]; @@ -278,19 +266,152 @@ static void GetStream(const Byte *p, CStreamInfo &s) memcpy(s.Hash, p + 30, kHashSize); } -static HRESULT ParseDirItem(const Byte *base, size_t pos, size_t size, - const UString &prefix, CObjectVector<CItem> &items) +static const wchar_t *kLongPath = L"[LongPath]"; + +UString CDatabase::GetItemPath(const int index1) const { - for (;;) + 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) { - if (pos + 8 > size) + 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 = base + pos; - UInt64 length = Get64(p); - if (length == 0) + 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 (pos + 102 > size || pos + length + 8 > size || length > ((UInt64)1 << 62)) + } + if ((len & 7) != 0 || len < 0x28 || rem < len) + return S_FALSE; + DirProcessed += (size_t)len; + if (DirProcessed > DirSize) + return S_FALSE; + if (Get64(p + 8) == 0) + { + if (prevIndex == -1) + return S_FALSE; + + UInt32 fileNameLen = Get16(p + 0x24); + if ((fileNameLen & 1) != 0 || ((0x26 + fileNameLen + 6) & ~7) != len) + return S_FALSE; + + UString name; + RINOK(ReadName(p + 0x26, fileNameLen, name)); + + CItem &prevItem = Items[prevIndex]; + if (name.IsEmpty() && !prevItem.HasStream()) + 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; + memcpy(item.Hash, p + 0x10, kHashSize); + item.Attrib = 0; + item.Order = Order++; + item.Parent = parent; + Items.Add(item); + } + pos += (size_t)len; + continue; + } + if (len < kDirRecordSize) return S_FALSE; + CItem item; item.Attrib = Get32(p + 8); // item.SecurityId = Get32(p + 0xC); @@ -300,43 +421,64 @@ static HRESULT ParseDirItem(const Byte *base, size_t pos, size_t size, GetFileTimeFromMem(p + 0x38, &item.MTime); memcpy(item.Hash, p + 0x40, kHashSize); - // UInt16 shortNameLen = Get16(p + 98); - UInt16 fileNameLen = Get16(p + 100); - - size_t tempPos = pos + 102; - if (tempPos + fileNameLen > size) + UInt32 shortNameLen = Get16(p + 98); + UInt32 fileNameLen = Get16(p + 100); + 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 (((kDirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) > len) + return S_FALSE; + + p += kDirRecordSize; - 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++ = Get16(base + tempPos + i); - *sz++ = '\0'; - item.Name.ReleaseBuffer(); - if (fileNameLen == 0 && item.isDir() && !item.HasStream()) + 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 -= kDirRecordSize; + p += ((kDirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7); + if (((kDirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) != len) + p = p; + */ + + /* + if (parent >= 0) { - item.Attrib = 0x10; // some swm archives have system/hidden attributes for root - item.Name.Delete(item.Name.Length() - 1); + UString s = GetItemPath(parent) + L"\\" + item.Name; + printf("\n%s %8x %S", item.IsDir() ? "D" : " ", (int)subdirOffset, (const wchar_t *)s); } - items.Add(item); - pos += (size_t)length; - if (item.isDir() && (subdirOffset != 0)) + */ + + 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) { - if (subdirOffset >= size) - return S_FALSE; - RINOK(ParseDirItem(base, (size_t)subdirOffset, size, item.Name + WCHAR_PATH_SEPARATOR, items)); + RINOK(ParseDirItem((size_t)subdirOffset, prevIndex)); } + Items[prevIndex].Order = Order++; + pos += (size_t)len; } } -static HRESULT ParseDir(const Byte *base, size_t size, - const UString &prefix, CObjectVector<CItem> &items) +HRESULT CDatabase::ParseImageDirs(const CByteBuffer &buf, int parent) { + DirData = buf; + DirSize = buf.GetCapacity(); + size_t pos = 0; - if (pos + 8 > size) + if (DirSize < 8) return S_FALSE; - const Byte *p = base + pos; + const Byte *p = DirData; UInt32 totalLength = Get32(p); // UInt32 numEntries = Get32(p + 4); pos += 8; @@ -346,7 +488,7 @@ static HRESULT ParseDir(const Byte *base, size_t size, UInt64 sum = 0; for (UInt32 i = 0; i < numEntries; i++) { - if (pos + 8 > size) + if (pos + 8 > DirSize) return S_FALSE; UInt64 len = Get64(p + pos); entryLens.Add(len); @@ -359,62 +501,23 @@ static HRESULT ParseDir(const Byte *base, size_t size, 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.isDir() != i2.isDir()) - return (i1.isDir()) ? 1 : -1; - if (i1.isDir()) - 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; + if (totalLength == 0) + pos = 8; + else if (totalLength < 8) + return S_FALSE; else - left = mid + 1; + pos = totalLength; } - return -1; + 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) @@ -426,8 +529,8 @@ HRESULT CHeader::Parse(const Byte *p) Flags = Get32(p + 0x10); if (!IsSupported()) return S_FALSE; - UInt32 chunkSize = Get32(p + 0x14); - if (chunkSize != kChunkSize && chunkSize != 0) + ChunkSize = Get32(p + 0x14); + if (ChunkSize != kChunkSize && ChunkSize != 0) return S_FALSE; memcpy(Guid, p + 0x18, 16); PartNumber = Get16(p + 0x28); @@ -441,21 +544,20 @@ HRESULT CHeader::Parse(const Byte *p) GetResource(p + offset, OffsetResource); GetResource(p + offset + 0x18, XmlResource); GetResource(p + offset + 0x30, MetadataResource); - /* if (IsNewVersion()) { if (haderSize < 0xD0) return S_FALSE; - IntegrityResource.Parse(p + offset + 0x4C); 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) { - const UInt32 kHeaderSizeMax = 0xD0; Byte p[kHeaderSizeMax]; RINOK(ReadStream_FALSE(inStream, p, kHeaderSizeMax)); if (memcmp(p, kSignature, kSignatureSize) != 0) @@ -463,33 +565,35 @@ HRESULT ReadHeader(IInStream *inStream, CHeader &h) return h.Parse(p); } -HRESULT ReadStreams(IInStream *inStream, const CHeader &h, CDatabase &db) +static HRESULT ReadStreams(IInStream *inStream, const CHeader &h, CDatabase &db) { CByteBuffer offsetBuf; RINOK(UnpackData(inStream, h.OffsetResource, h.IsLzxMode(), offsetBuf, NULL)); - for (size_t i = 0; i + kStreamInfoSize <= offsetBuf.GetCapacity(); i += kStreamInfoSize) + size_t i; + for (i = 0; offsetBuf.GetCapacity() - i >= kStreamInfoSize; i += kStreamInfoSize) { CStreamInfo s; GetStream((const Byte *)offsetBuf + i, s); if (s.PartNumber == h.PartNumber) db.Streams.Add(s); } - return S_OK; + return (i == offsetBuf.GetCapacity()) ? S_OK : S_FALSE; } -HRESULT OpenArchive(IInStream *inStream, const CHeader &h, CByteBuffer &xml, CDatabase &db) +HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, CByteBuffer &xml, IArchiveOpenCallback *openCallback) { + OpenCallback = openCallback; RINOK(UnpackData(inStream, h.XmlResource, h.IsLzxMode(), xml, NULL)); - - RINOK(ReadStreams(inStream, h, db)); + RINOK(ReadStreams(inStream, h, *this)); bool needBootMetadata = !h.MetadataResource.IsEmpty(); + Order = 0; if (h.PartNumber == 1) { int imageIndex = 1; - for (int j = 0; j < db.Streams.Size(); j++) + for (int i = 0; i < Streams.Size(); i++) { // if (imageIndex > 1) break; - const CStreamInfo &si = db.Streams[j]; + const CStreamInfo &si = Streams[i]; if (!si.Resource.IsMetadata() || si.PartNumber != h.PartNumber) continue; Byte hash[kHashSize]; @@ -497,11 +601,8 @@ HRESULT OpenArchive(IInStream *inStream, const CHeader &h, CByteBuffer &xml, CDa RINOK(UnpackData(inStream, si.Resource, h.IsLzxMode(), metadata, hash)); if (memcmp(hash, si.Hash, kHashSize) != 0) return S_FALSE; - wchar_t sz[16]; - ConvertUInt32ToString(imageIndex++, sz); - UString s = sz; - s += WCHAR_PATH_SEPARATOR; - RINOK(ParseDir(metadata, metadata.GetCapacity(), s, db.Items)); + NumImages++; + RINOK(ParseImageDirs(metadata, -(int)(++imageIndex))); if (needBootMetadata) if (h.MetadataResource.Offset == si.Resource.Offset) needBootMetadata = false; @@ -512,58 +613,114 @@ HRESULT OpenArchive(IInStream *inStream, const CHeader &h, CByteBuffer &xml, CDa { CByteBuffer metadata; RINOK(UnpackData(inStream, h.MetadataResource, h.IsLzxMode(), metadata, NULL)); - RINOK(ParseDir(metadata, metadata.GetCapacity(), L"0" WSTRING_PATH_SEPARATOR, db.Items)); + RINOK(ParseImageDirs(metadata, -1)); + NumImages++; } return S_OK; } -HRESULT SortDatabase(CDatabase &db) +static int CompareStreamsByPos(const CStreamInfo *p1, const CStreamInfo *p2, void * /* param */) { - db.Streams.Sort(CompareStreamsByPos, NULL); + int res = MyCompare(p1->PartNumber, p2->PartNumber); + if (res != 0) + return res; + return MyCompare(p1->Resource.Offset, p2->Resource.Offset); +} + +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 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); { - CRecordVector<int> sortedByHash; + CIntVector sortedByHash; { - for (int j = 0; j < db.Streams.Size(); j++) - sortedByHash.Add(j); - sortedByHash.Sort(CompareHashRefs, &db.Streams); + for (int i = 0; i < Streams.Size(); i++) + sortedByHash.Add(i); + sortedByHash.Sort(CompareHashRefs, &Streams); } - for (int i = 0; i < db.Items.Size(); i++) + for (int i = 0; i < Items.Size(); i++) { - CItem &item = db.Items[i]; + CItem &item = Items[i]; item.StreamIndex = -1; if (item.HasStream()) - item.StreamIndex = FindHash(db.Streams, sortedByHash, item.Hash); + item.StreamIndex = FindHash(Streams, sortedByHash, item.Hash); } } { CRecordVector<bool> used; - int j; - for (j = 0; j < db.Streams.Size(); j++) + int i; + for (i = 0; i < Streams.Size(); i++) { - const CStreamInfo &s = db.Streams[j]; + const CStreamInfo &s = Streams[i]; used.Add(s.Resource.IsMetadata() && s.PartNumber == 1); + // used.Add(false); } - for (int i = 0; i < db.Items.Size(); i++) + for (i = 0; i < Items.Size(); i++) { - CItem &item = db.Items[i]; + CItem &item = Items[i]; if (item.StreamIndex >= 0) used[item.StreamIndex] = true; } - for (j = 0; j < db.Streams.Size(); j++) - if (!used[j]) + for (i = 0; i < Streams.Size(); i++) + if (!used[i]) { CItem item; - item.StreamIndex = j; + item.StreamIndex = i; item.HasMetadata = false; - db.Items.Add(item); - } + Items.Add(item); + } } - - db.Items.Sort(CompareItems, NULL); + + SortedItems.Reserve(Items.Size()); + for (int i = (skipRootDir ? 1 : 0); i < Items.Size(); i++) + SortedItems.Add(i); + SortedItems.Sort(CompareItems, this); return S_OK; } |