Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/kornelski/7z.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Archive/Wim/WimIn.cpp')
-rw-r--r--[-rwxr-xr-x]CPP/7zip/Archive/Wim/WimIn.cpp1099
1 files changed, 780 insertions, 319 deletions
diff --git a/CPP/7zip/Archive/Wim/WimIn.cpp b/CPP/7zip/Archive/Wim/WimIn.cpp
index c210804d..cec037cc 100755..100644
--- a/CPP/7zip/Archive/Wim/WimIn.cpp
+++ b/CPP/7zip/Archive/Wim/WimIn.cpp
@@ -2,13 +2,17 @@
#include "StdAfx.h"
+// #include <stdio.h>
+
#include "../../../../C/CpuArch.h"
-#include "Common/IntToString.h"
+#include "../../../Common/IntToString.h"
+#include "../../../Common/StringToInt.h"
+#include "../../../Common/UTFConvert.h"
-#include "../../Common/StreamUtils.h"
-#include "../../Common/StreamObjects.h"
#include "../../Common/LimitedStreams.h"
+#include "../../Common/StreamObjects.h"
+#include "../../Common/StreamUtils.h"
#include "../Common/OutStreamWithSha1.h"
@@ -33,7 +37,6 @@ public:
{
if (NeedFlush)
m_Decoder->Flush();
- m_Decoder->ReleaseStreams();
}
};
@@ -44,8 +47,8 @@ HRESULT CDecoder::CodeSpec(UInt32 outSize)
for (unsigned i = 0; i < kMainTableSize; i += 2)
{
Byte b = m_InBitStream.DirectReadByte();
- levels[i] = b & 0xF;
- levels[i + 1] = b >> 4;
+ levels[i] = (Byte)(b & 0xF);
+ levels[i + 1] = (Byte)(b >> 4);
}
if (!m_MainDecoder.SetCodeLengths(levels))
return S_FALSE;
@@ -154,11 +157,7 @@ HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool l
size_t sizesBufSize = (size_t)sizesBufSize64;
if (sizesBufSize != sizesBufSize64)
return E_OUTOFMEMORY;
- if (sizesBufSize > sizesBuf.GetCapacity())
- {
- sizesBuf.Free();
- sizesBuf.SetCapacity(sizesBufSize);
- }
+ sizesBuf.AllocAtLeast(sizesBufSize);
RINOK(ReadStream_FALSE(inStream, (Byte *)sizesBuf, sizesBufSize));
const Byte *p = (const Byte *)sizesBuf;
@@ -237,8 +236,7 @@ static HRESULT UnpackData(IInStream *inStream, const CResource &resource, bool l
size_t size = (size_t)resource.UnpackSize;
if (size != resource.UnpackSize)
return E_OUTOFMEMORY;
- buf.Free();
- buf.SetCapacity(size);
+ buf.Alloc(size);
CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream();
CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
@@ -256,7 +254,7 @@ void CResource::Parse(const Byte *p)
UnpackSize = Get64(p + 16);
}
-#define GetResource(p, res) res.Parse(p)
+#define GET_RESOURCE(_p_, res) res.ParseAndUpdatePhySize(_p_, phySize)
static void GetStream(bool oldVersion, const Byte *p, CStreamInfo &s)
{
@@ -265,111 +263,151 @@ static void GetStream(bool oldVersion, const Byte *p, CStreamInfo &s)
{
s.PartNumber = 1;
s.Id = Get32(p + 24);
- s.RefCount = Get32(p + 28);
- memcpy(s.Hash, p + 32, kHashSize);
+ // printf("\n%d", s.Id);
+ p += 28;
}
else
{
s.PartNumber = Get16(p + 24);
- s.RefCount = Get32(p + 26);
- memcpy(s.Hash, p + 30, kHashSize);
+ p += 26;
}
+ s.RefCount = Get32(p);
+ memcpy(s.Hash, p + 4, kHashSize);
}
-static const wchar_t *kLongPath = L"[LongPath]";
-UString CDatabase::GetItemPath(const int index1) const
+static const char *kLongPath = "[LongPath]";
+
+void CDatabase::GetShortName(unsigned index, NWindows::NCOM::CPropVariant &name) const
{
- int size = 0;
+ const CItem &item = Items[index];
+ const CImage &image = Images[item.ImageIndex];
+ if (item.Parent < 0 && image.NumEmptyRootItems != 0)
+ {
+ name.Clear();
+ return;
+ }
+ const Byte *meta = image.Meta + item.Offset +
+ (IsOldVersion ? kDirRecordSizeOld : kDirRecordSize);
+ UInt32 fileNameLen = Get16(meta - 2);
+ UInt32 shortLen = Get16(meta - 4) / 2;
+ wchar_t *s = name.AllocBstr(shortLen);
+ if (fileNameLen != 0)
+ meta += fileNameLen + 2;
+ for (UInt32 i = 0; i < shortLen; i++)
+ s[i] = Get16(meta + i * 2);
+ s[shortLen] = 0;
+ // empty shortName has no ZERO at the end ?
+}
+
+void CDatabase::GetItemName(unsigned index, NWindows::NCOM::CPropVariant &name) const
+{
+ const CItem &item = Items[index];
+ const CImage &image = Images[item.ImageIndex];
+ if (item.Parent < 0 && image.NumEmptyRootItems != 0)
+ {
+ name = image.RootName;
+ return;
+ }
+ const Byte *meta = image.Meta + item.Offset +
+ (item.IsAltStream ?
+ (IsOldVersion ? 0x10 : 0x24) :
+ (IsOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2));
+ UInt32 len = Get16(meta) / 2;
+ wchar_t *s = name.AllocBstr(len);
+ meta += 2;
+ len++;
+ for (UInt32 i = 0; i < len; i++)
+ s[i] = Get16(meta + i * 2);
+}
+
+void CDatabase::GetItemPath(unsigned index1, bool showImageNumber, NWindows::NCOM::CPropVariant &path) const
+{
+ unsigned size = 0;
int index = index1;
- int newLevel;
- for (newLevel = 0;; newLevel = 1)
+ unsigned newLevel;
+ int imageIndex = Items[index].ImageIndex;
+ const CImage &image = Images[imageIndex];
+ for (newLevel = 0;;)
{
const CItem &item = Items[index];
index = item.Parent;
- if (index >= 0 || !SkipRoot)
- size += item.Name.Length() + newLevel;
+ if (index >= 0 || image.NumEmptyRootItems == 0)
+ {
+ const Byte *meta = image.Meta + item.Offset;
+ meta += item.IsAltStream ?
+ (IsOldVersion ? 0x10 : 0x24) :
+ (IsOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2);
+ size += Get16(meta) / 2;
+ size += newLevel;
+ newLevel = 1;
+ if ((UInt32)size >= ((UInt32)1 << 15))
+ {
+ path = kLongPath;
+ return;
+ }
+ }
if (index < 0)
break;
- if ((UInt32)size >= ((UInt32)1 << 16))
- return kLongPath;
}
- wchar_t temp[16];
- int imageLen = 0;
- if (ShowImageNumber)
+ if (showImageNumber)
{
- ConvertUInt32ToString(-1 - index, temp);
- imageLen = MyStringLen(temp);
- size += imageLen + 1;
+ size += image.RootName.Len();
+ size += newLevel;
}
- if ((UInt32)size >= ((UInt32)1 << 16))
- return kLongPath;
- UString path;
- wchar_t *s = path.GetBuffer(size);
+ wchar_t *s = path.AllocBstr(size);
s[size] = 0;
- if (ShowImageNumber)
+
+ if (showImageNumber)
{
- memcpy(s, temp, imageLen * sizeof(wchar_t));
- s[imageLen] = WCHAR_PATH_SEPARATOR;
+ MyStringCopy(s, (const wchar_t *)image.RootName);
+ if (newLevel)
+ s[image.RootName.Len()] = WCHAR_PATH_SEPARATOR;
}
index = index1;
-
- for (newLevel = 0;; newLevel = 1)
+ wchar_t separator = 0;
+ for (;;)
{
const CItem &item = Items[index];
index = item.Parent;
- if (index >= 0 || !SkipRoot)
+ if (index >= 0 || image.NumEmptyRootItems == 0)
{
- if (newLevel)
- s[--size] = WCHAR_PATH_SEPARATOR;
- size -= item.Name.Length();
- memcpy(s + size, item.Name, sizeof(wchar_t) * item.Name.Length());
+ if (separator)
+ s[--size] = separator;
+ const Byte *meta = image.Meta + item.Offset;
+ meta += (item.IsAltStream) ?
+ (IsOldVersion ? 0x10: 0x24) :
+ (IsOldVersion ? kDirRecordSizeOld - 2 : kDirRecordSize - 2);
+ UInt32 len = Get16(meta) / 2;
+ size -= len;
+ wchar_t *dest = s + size;
+ meta += 2;
+ for (UInt32 i = 0; i < len; i++)
+ dest[i] = Get16(meta + i * 2);
}
if (index < 0)
- {
- path.ReleaseBuffer();
- return path;
- }
+ return;
+ separator = item.IsAltStream ? L':' : WCHAR_PATH_SEPARATOR;
}
}
-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;
-}
+// Root folders in OLD archives (ver = 1.10) conatin real items.
+// Root folders in NEW archives (ver > 1.11) contain only one folder with empty name.
HRESULT CDatabase::ParseDirItem(size_t pos, int parent)
{
if ((pos & 7) != 0)
return S_FALSE;
-
- int prevIndex = -1;
- for (int numItems = 0;; numItems++)
+
+ for (unsigned numItems = 0;; numItems++)
{
- if (OpenCallback)
+ if (OpenCallback && (Items.Size() & 0xFFFF) == 0)
{
UInt64 numFiles = Items.Size();
- if ((numFiles & 0x3FF) == 0)
- {
- RINOK(OpenCallback->SetCompleted(&numFiles, NULL));
- }
+ RINOK(OpenCallback->SetCompleted(&numFiles, NULL));
}
size_t rem = DirSize - pos;
if (pos < DirStartOffset || pos > DirSize || rem < 8)
@@ -379,32 +417,93 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent)
if (len == 0)
{
if (parent < 0 && numItems != 1)
- SkipRoot = false;
+ Images.Back().NumEmptyRootItems = 0;
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)
+
+ UInt32 dirRecordSize = IsOldVersion ? kDirRecordSizeOld : kDirRecordSize;
+ if (len < dirRecordSize)
+ return S_FALSE;
+
+ CItem item;
+ UInt32 attrib = Get32(p + 8);
+ item.IsDir = ((attrib & 0x10) != 0);
+ UInt64 subdirOffset = Get64(p + 0x10);
+ UInt32 numAltStreams = 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;
+
{
- if (len < 0x40 || (/* Get32(p + 12) == 0 && */ Get32(p + 0x14) != 0))
- {
- extraOffset = 0x10;
- }
+ if (*(const UInt16 *)(p + fileNameLen) != 0)
+ return S_FALSE;
+ for (UInt32 j = 0; j < fileNameLen; j += 2)
+ if (*(const UInt16 *)(p + j) == 0)
+ return S_FALSE;
+ }
+ if (shortNameLen != 0)
+ {
+ // empty shortName has no ZERO at the end ?
+ const Byte *p2 = p + fileNameLen2;
+ if (*(const UInt16 *)(p2 + shortNameLen) != 0)
+ return S_FALSE;
+ for (UInt32 j = 0; j < shortNameLen; j += 2)
+ if (*(const UInt16 *)(p2 + j) == 0)
+ return S_FALSE;
}
- else if (Get64(p + 8) == 0)
- extraOffset = 0x24;
- if (extraOffset)
+
+ item.Offset = pos;
+ item.Parent = parent;
+ item.ImageIndex = Images.Size() - 1;
+ unsigned prevIndex = Items.Add(item);
+
+ pos += (size_t)len;
+
+ for (UInt32 i = 0; i < numAltStreams; i++)
{
- if (prevIndex == -1)
+ 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)
+ return S_FALSE;
+ if ((len & 7) != 0 || rem < len)
+ return S_FALSE;
+ if (IsOldVersion)
+ {
+ if (len < 0x18)
+ return S_FALSE;
+ }
+ else
+ if (len < 0x28)
+ return S_FALSE;
+ DirProcessed += (size_t)len;
+ if (DirProcessed > DirSize)
+ return S_FALSE;
+
+ unsigned extraOffset = 0;
+ if (IsOldVersion)
+ extraOffset = 0x10;
+ else
+ {
+ if (Get64(p + 8) != 0)
+ return S_FALSE;
+ extraOffset = 0x24;
+ }
UInt32 fileNameLen = Get16(p + extraOffset);
if ((fileNameLen & 1) != 0)
return S_FALSE;
@@ -414,188 +513,137 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent)
if (((extraOffset + 2 + fileNameLen2 + 6) & ~7) > len)
return S_FALSE;
- UString name;
- RINOK(ReadName(p + extraOffset + 2, fileNameLen, name));
+ {
+ const Byte *p2 = p + extraOffset + 2;
+ if (*(const UInt16 *)(p2 + fileNameLen) != 0)
+ return S_FALSE;
+ for (UInt32 j = 0; j < fileNameLen; j += 2)
+ if (*(const UInt16 *)(p2 + j) == 0)
+ return S_FALSE;
+ }
- CItem &prevItem = Items[prevIndex];
- if (name.IsEmpty() && !prevItem.HasStream())
+ if (fileNameLen == 0)
{
+ Byte *prevMeta = DirData + item.Offset;
if (IsOldVersion)
- prevItem.Id = Get32(p + 8);
+ memcpy(prevMeta + 0x10, p + 8, 4); // It's 32-bit Id
else
- memcpy(prevItem.Hash, p + 0x10, kHashSize);
+ memcpy(prevMeta + 0x40, 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);
+ ThereAreAltStreams = true;
+ CItem item2;
+ item2.Offset = pos;
+ item2.IsAltStream = true;
+ item2.Parent = prevIndex;
+ item2.ImageIndex = Images.Size() - 1;
+ Items.Add(item2);
}
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)
+ if (parent < 0 && numItems == 0 && shortNameLen == 0 && fileNameLen == 0 && item.IsDir)
{
- UString s = GetItemPath(parent) + L"\\" + item.Name;
- printf("\n%s %8x %S", item.IsDir() ? "D" : " ", (int)subdirOffset, (const wchar_t *)s);
+ CImage &image = Images.Back();
+ image.NumEmptyRootItems = Items.Size() - image.StartItem;
}
- */
-
- 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 (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)
+HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent)
{
DirData = buf;
- DirSize = buf.GetCapacity();
-
- size_t pos = 0;
+ DirSize = buf.Size();
if (DirSize < 8)
return S_FALSE;
const Byte *p = DirData;
- UInt32 totalLength = Get32(p);
+ size_t pos = 0;
+ CImage &image = Images.Back();
+
if (IsOldVersion)
{
- for (pos = 4;; pos += 8)
+ // there is no specification about that code
+ UInt32 sum = 0;
+ image.SecurOffsets.Add(0);
+ for (;;)
{
- 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)
+ UInt32 len = Get32(p + pos);
+ if (len > DirSize - sum)
return S_FALSE;
+ sum += len;
+ image.SecurOffsets.Add(sum);
+ UInt32 n = Get32(p + pos + 4); // what does this field mean?
+ pos += 8;
+ if (n == 0)
+ break;
}
- pos += totalLength + 4;
- pos = (pos + 7) & ~(size_t)7;
- if (pos > DirSize)
+ if (sum > DirSize - pos)
return S_FALSE;
+ FOR_VECTOR (i, image.SecurOffsets)
+ image.SecurOffsets[i] += (UInt32)pos;
+ pos += sum;
+ pos = (pos + 7) & ~(size_t)7;
}
else
{
-
- // UInt32 numEntries = Get32(p + 4);
- pos += 8;
- {
- /*
- CRecordVector<UInt64> entryLens;
- UInt64 sum = 0;
- for (UInt32 i = 0; i < numEntries; i++)
+ UInt32 totalLen = Get32(p);
+ if (totalLen == 0)
+ pos = 8;
+ else
{
- if (pos + 8 > DirSize)
+ if (totalLen < 8)
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)
+ UInt32 numEntries = Get32(p + 4);
pos = 8;
- else if (totalLength < 8)
- return S_FALSE;
- else
- pos = totalLength;
- }
+ if (totalLen > DirSize || numEntries > ((totalLen - 8) >> 3))
+ return S_FALSE;
+ UInt32 sum = (UInt32)pos + numEntries * 8;
+ image.SecurOffsets.ClearAndReserve(numEntries + 1);
+ image.SecurOffsets.AddInReserved(sum);
+ for (UInt32 i = 0; i < numEntries; i++, pos += 8)
+ {
+ UInt64 len = Get64(p + pos);
+ if (len > totalLen - sum)
+ return S_FALSE;
+ sum += (UInt32)len;
+ image.SecurOffsets.AddInReserved(sum);
+ }
+ pos = sum;
+ pos = (pos + 7) & ~(size_t)7;
+ if (pos != (((size_t)totalLen + 7) & ~(size_t)7))
+ return S_FALSE;
+ }
}
+
+ if (pos > DirSize)
+ return S_FALSE;
DirStartOffset = DirProcessed = pos;
+ image.StartItem = Items.Size();
RINOK(ParseDirItem(pos, parent));
+ image.NumItems = Items.Size() - image.StartItem;
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)
+ /* Original program writes additional 8 bytes (END_OF_ROOT_FOLDER),
+ but the reference to that folder is empty */
+
+ // we can't use DirProcessed - DirStartOffset == 112 check if there is alt stream in root
+ if (DirProcessed == DirSize - 8 && Get64(p + DirSize - 8) == 0)
return S_OK;
return S_FALSE;
}
-HRESULT CHeader::Parse(const Byte *p)
+HRESULT CHeader::Parse(const Byte *p, UInt64 &phySize)
{
UInt32 headerSize = Get32(p + 8);
+ phySize = headerSize;
Version = Get32(p + 0x0C);
Flags = Get32(p + 0x10);
if (!IsSupported())
@@ -603,7 +651,7 @@ HRESULT CHeader::Parse(const Byte *p)
ChunkSize = Get32(p + 0x14);
if (ChunkSize != kChunkSize && ChunkSize != 0)
return S_FALSE;
- int offset;
+ unsigned offset;
if (IsOldVersion())
{
if (headerSize != 0x60)
@@ -627,124 +675,190 @@ HRESULT CHeader::Parse(const Byte *p)
offset += 4;
}
}
- GetResource(p + offset, OffsetResource);
- GetResource(p + offset + 0x18, XmlResource);
- GetResource(p + offset + 0x30, MetadataResource);
+ GET_RESOURCE(p + offset , OffsetResource);
+ GET_RESOURCE(p + offset + 0x18, XmlResource);
+ GET_RESOURCE(p + offset + 0x30, MetadataResource);
+ BootIndex = 0;
if (IsNewVersion())
{
if (headerSize < 0xD0)
return S_FALSE;
- BootIndex = Get32(p + 0x48);
- IntegrityResource.Parse(p + offset + 0x4C);
+ BootIndex = Get32(p + offset + 0x48);
+ GET_RESOURCE(p + offset + 0x4C, IntegrityResource);
}
+
return S_OK;
}
const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 };
-HRESULT ReadHeader(IInStream *inStream, CHeader &h)
+HRESULT ReadHeader(IInStream *inStream, CHeader &h, UInt64 &phySize)
{
Byte p[kHeaderSizeMax];
RINOK(ReadStream_FALSE(inStream, p, kHeaderSizeMax));
if (memcmp(p, kSignature, kSignatureSize) != 0)
return S_FALSE;
- return h.Parse(p);
+ return h.Parse(p, phySize);
}
-static HRESULT ReadStreams(bool oldVersion, 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));
size_t i;
- size_t streamInfoSize = oldVersion ? kStreamInfoSize + 2 : kStreamInfoSize;
- for (i = 0; offsetBuf.GetCapacity() - i >= streamInfoSize; i += streamInfoSize)
+ size_t streamInfoSize = h.IsOldVersion() ? kStreamInfoSize + 2 : kStreamInfoSize;
+ for (i = 0; offsetBuf.Size() - i >= streamInfoSize; i += streamInfoSize)
{
CStreamInfo s;
- GetStream(oldVersion, (const Byte *)offsetBuf + i, s);
+ GetStream(h.IsOldVersion(), (const Byte *)offsetBuf + i, s);
if (s.PartNumber == h.PartNumber)
- db.Streams.Add(s);
+ {
+ if (s.Resource.IsMetadata())
+ {
+ if (s.RefCount == 0)
+ return S_FALSE;
+ if (s.RefCount > 1)
+ {
+ s.RefCount--;
+ db.DataStreams.Add(s);
+ }
+ s.RefCount = 1;
+ db.MetaStreams.Add(s);
+ }
+ else
+ db.DataStreams.Add(s);
+ }
}
- return (i == offsetBuf.GetCapacity()) ? S_OK : S_FALSE;
+ return (i == offsetBuf.Size()) ? S_OK : S_FALSE;
}
static bool IsEmptySha(const Byte *data)
{
- for (int i = 0; i < kHashSize; i++)
+ for (unsigned 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)
+HRESULT CDatabase::OpenXml(IInStream *inStream, const CHeader &h, CByteBuffer &xml)
+{
+ return UnpackData(inStream, h.XmlResource, h.IsLzxMode(), xml, NULL);
+}
+
+static void SetRootNames(CImage &image, unsigned value)
+{
+ wchar_t temp[16];
+ ConvertUInt32ToString(value, temp);
+ image.RootName = temp;
+ image.RootNameBuf.Alloc(image.RootName.Len() * 2 + 2);
+ Byte *p = image.RootNameBuf;
+ unsigned len = image.RootName.Len() + 1;
+ for (unsigned k = 0; k < len; k++)
+ {
+ p[k * 2] = (Byte)temp[k];
+ p[k * 2 + 1] = 0;
+ }
+}
+
+HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, unsigned numItemsReserve, IArchiveOpenCallback *openCallback)
{
OpenCallback = openCallback;
IsOldVersion = h.IsOldVersion();
- RINOK(UnpackData(inStream, h.XmlResource, h.IsLzxMode(), xml, NULL));
- RINOK(ReadStreams(h.IsOldVersion(), inStream, h, *this));
+ RINOK(ReadStreams(inStream, h, *this));
+
+ // printf("\nh.PartNumber = %02d", (unsigned)h.PartNumber);
bool needBootMetadata = !h.MetadataResource.IsEmpty();
- Order = 0;
- if (h.PartNumber == 1)
+
+ FOR_VECTOR (i, MetaStreams)
{
- int imageIndex = 1;
- for (int i = 0; i < Streams.Size(); i++)
+ const CStreamInfo &si = MetaStreams[i];
+ /*
+ printf("\ni = %5d"
+ " Refs = %3d"
+ " Part = %1d"
+ " Offs = %7X"
+ " PackSize = %7X"
+ " Size = %7X"
+ " Flags = %d "
+ ,
+ i,
+ si.RefCount,
+ (unsigned)si.PartNumber,
+ (unsigned)si.Resource.Offset,
+ (unsigned)si.Resource.PackSize,
+ (unsigned)si.Resource.UnpackSize,
+ (unsigned)si.Resource.Flags
+ );
+ for (unsigned y = 0; y < 2; y++)
+ printf("%02X", (unsigned)si.Hash[y]);
+ */
+
+ if (h.PartNumber != 1 || si.PartNumber != h.PartNumber)
+ continue;
+
+ Byte hash[kHashSize];
+ CImage &image = Images.AddNew();
+ SetRootNames(image, Images.Size());
+ CByteBuffer &metadata = image.Meta;
+ RINOK(UnpackData(inStream, si.Resource, h.IsLzxMode(), metadata, hash));
+ if (memcmp(hash, si.Hash, kHashSize) != 0 &&
+ !(h.IsOldVersion() && IsEmptySha(si.Hash)))
+ return S_FALSE;
+ image.NumEmptyRootItems = 0;
+ if (Items.IsEmpty())
+ Items.ClearAndReserve(numItemsReserve);
+ RINOK(ParseImageDirs(metadata, -1));
+ if (needBootMetadata)
{
- // 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;
+ bool sameRes = (h.MetadataResource.Offset == si.Resource.Offset);
+ if (sameRes)
+ needBootMetadata = false;
+ bool isBootIndex = (h.BootIndex == (UInt32)Images.Size());
+ if (h.IsNewVersion())
+ {
+ if (sameRes && !isBootIndex)
+ return S_FALSE;
+ if (isBootIndex && !sameRes)
+ return S_FALSE;
+ }
}
}
if (needBootMetadata)
- {
- CByteBuffer metadata;
- RINOK(UnpackData(inStream, h.MetadataResource, h.IsLzxMode(), metadata, NULL));
- RINOK(ParseImageDirs(metadata, -1));
- NumImages++;
- }
+ return S_FALSE;
return S_OK;
}
+#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
+
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);
+ RINOZ(MyCompare(p1->PartNumber, p2->PartNumber));
+ RINOZ(MyCompare(p1->Resource.Offset, p2->Resource.Offset));
+ return MyCompare(p1->Resource.PackSize, p2->Resource.PackSize);
}
-static int CompareIDs(const int *p1, const int *p2, void *param)
+static int CompareIDs(const unsigned *p1, const unsigned *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)
+static int CompareHashRefs(const unsigned *p1, const unsigned *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)
+ const CUIntVector &sortedByHash, UInt32 id)
{
- int left = 0, right = streams.Size();
+ unsigned left = 0, right = streams.Size();
while (left != right)
{
- int mid = (left + right) / 2;
- int streamIndex = sortedByHash[mid];
+ unsigned mid = (left + right) / 2;
+ unsigned streamIndex = sortedByHash[mid];
UInt32 id2 = streams[streamIndex].Id;
if (id == id2)
return streamIndex;
@@ -757,15 +871,15 @@ static int FindId(const CRecordVector<CStreamInfo> &streams,
}
static int FindHash(const CRecordVector<CStreamInfo> &streams,
- const CIntVector &sortedByHash, const Byte *hash)
+ const CUIntVector &sortedByHash, const Byte *hash)
{
- int left = 0, right = streams.Size();
+ unsigned left = 0, right = streams.Size();
while (left != right)
{
- int mid = (left + right) / 2;
- int streamIndex = sortedByHash[mid];
- UInt32 i;
+ unsigned mid = (left + right) / 2;
+ unsigned streamIndex = sortedByHash[mid];
const Byte *hash2 = streams[streamIndex].Hash;
+ unsigned i;
for (i = 0; i < kHashSize; i++)
if (hash[i] != hash2[i])
break;
@@ -779,77 +893,424 @@ static int FindHash(const CRecordVector<CStreamInfo> &streams,
return -1;
}
-static int CompareItems(const int *a1, const int *a2, void *param)
+bool CDatabase::ItemHasStream(const CItem &item) const
{
- const CObjectVector<CItem> &items = ((CDatabase *)param)->Items;
+ if (item.ImageIndex < 0)
+ return true;
+ const Byte *meta = Images[item.ImageIndex].Meta + item.Offset;
+ if (IsOldVersion)
+ {
+ // old wim use same field for file_id and dir_offset;
+ if (item.IsDir)
+ return false;
+ meta += (item.IsAltStream ? 0x8 : 0x10);
+ UInt32 id = GetUi32(meta);
+ return id != 0;
+ }
+ meta += (item.IsAltStream ? 0x10 : 0x40);
+ for (unsigned i = 0; i < kHashSize; i++)
+ if (meta[i] != 0)
+ return true;
+ return false;
+}
+
+static int CompareItems(const unsigned *a1, const unsigned *a2, void *param)
+{
+ const CRecordVector<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);
+ if (i1.IsDir != i2.IsDir)
+ return i1.IsDir ? -1 : 1;
+ if (i1.IsAltStream != i2.IsAltStream)
+ return i1.IsAltStream ? 1 : -1;
+ RINOZ(MyCompare(i1.StreamIndex, i2.StreamIndex));
+ RINOZ(MyCompare(i1.ImageIndex, i2.ImageIndex));
+ return MyCompare(i1.Offset, i2.Offset);
}
-HRESULT CDatabase::Sort(bool skipRootDir)
+HRESULT CDatabase::FillAndCheck()
{
- Streams.Sort(CompareStreamsByPos, NULL);
-
{
- CIntVector sortedByHash;
+ DataStreams.Sort(CompareStreamsByPos, NULL);
+ for (unsigned i = 1; i < DataStreams.Size(); i++)
+ {
+ const CStreamInfo &s0 = DataStreams[i - 1];
+ const CStreamInfo &s1 = DataStreams[i];
+ if (s0.PartNumber == s1.PartNumber)
+ if (s0.Resource.GetEndLimit() > s1.Resource.Offset)
+ return S_FALSE;
+ }
+ }
+
+ {
+ CUIntVector sortedByHash;
{
- for (int i = 0; i < Streams.Size(); i++)
- sortedByHash.Add(i);
+ unsigned num = DataStreams.Size();
+ sortedByHash.ClearAndSetSize(num);
+ unsigned *vals = &sortedByHash[0];
+ for (unsigned i = 0; i < num; i++)
+ vals[i] = i;
if (IsOldVersion)
- sortedByHash.Sort(CompareIDs, &Streams);
+ sortedByHash.Sort(CompareIDs, &DataStreams);
else
- sortedByHash.Sort(CompareHashRefs, &Streams);
+ sortedByHash.Sort(CompareHashRefs, &DataStreams);
}
- for (int i = 0; i < Items.Size(); i++)
+ FOR_VECTOR (i, Items)
{
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);
+ const Byte *hash = Images[item.ImageIndex].Meta + item.Offset;
+ if (IsOldVersion)
+ {
+ if (!item.IsDir)
+ {
+ hash += (item.IsAltStream ? 0x8 : 0x10);
+ UInt32 id = GetUi32(hash);
+ if (id != 0)
+ item.StreamIndex = FindId(DataStreams, sortedByHash, id);
+ }
+ }
+ /*
+ else if (item.IsDir)
+ {
+ // reparse points can have dirs some dir
+ }
+ */
+ else
+ {
+ hash += (item.IsAltStream ? 0x10 : 0x40);
+ unsigned hi;
+ for (hi = 0; hi < kHashSize; hi++)
+ if (hash[hi] != 0)
+ break;
+ if (hi != kHashSize)
+ {
+ item.StreamIndex = FindHash(DataStreams, sortedByHash, hash);
+ }
+ }
}
}
-
{
- CRecordVector<bool> used;
- int i;
- for (i = 0; i < Streams.Size(); i++)
+ CUIntVector refCounts;
+ refCounts.ClearAndSetSize(DataStreams.Size());
+ unsigned i;
+
+ for (i = 0; i < DataStreams.Size(); i++)
{
- const CStreamInfo &s = Streams[i];
- used.Add(s.Resource.IsMetadata() && s.PartNumber == 1);
- // used.Add(false);
+ UInt32 startVal = 0;
+ // const CStreamInfo &s = DataStreams[i];
+ /*
+ if (s.Resource.IsMetadata() && s.PartNumber == 1)
+ startVal = 1;
+ */
+ refCounts[i] = startVal;
}
+
for (i = 0; i < Items.Size(); i++)
{
- CItem &item = Items[i];
- if (item.StreamIndex >= 0)
- used[item.StreamIndex] = true;
+ int streamIndex = Items[i].StreamIndex;
+ if (streamIndex >= 0)
+ refCounts[streamIndex]++;
}
- for (i = 0; i < Streams.Size(); i++)
- if (!used[i])
+
+ for (i = 0; i < DataStreams.Size(); i++)
+ {
+ const CStreamInfo &s = DataStreams[i];
+ if (s.RefCount != refCounts[i])
+ {
+ /*
+ printf("\ni = %5d si.Refcount = %d realRefs = %d size = %6d offset = %6x id = %4d ",
+ i, s.RefCount, refCounts[i], (unsigned)s.Resource.UnpackSize, (unsigned)s.Resource.Offset, s.Id);
+ */
+ // return S_FALSE;
+ RefCountError = true;
+ }
+ if (refCounts[i] == 0)
{
CItem item;
+ item.Offset = 0;
item.StreamIndex = i;
- item.HasMetadata = false;
+ item.ImageIndex = -1;
+ ThereAreDeletedStreams = true;
Items.Add(item);
}
+ }
}
+ return S_OK;
+}
+
+HRESULT CDatabase::GenerateSortedItems(int imageIndex, bool showImageNumber)
+{
+ SortedItems.Clear();
+ VirtualRoots.Clear();
+ IndexOfUserImage = imageIndex;
+ NumExludededItems = 0;
+ ExludedItem = -1;
+
+ if (Images.Size() != 1 && imageIndex < 0)
+ showImageNumber = true;
+
+ unsigned startItem = 0;
+ unsigned endItem = 0;
+ if (imageIndex < 0)
+ {
+ endItem = Items.Size();
+ if (Images.Size() == 1)
+ {
+ IndexOfUserImage = 0;
+ const CImage &image = Images[0];
+ if (!showImageNumber)
+ NumExludededItems = image.NumEmptyRootItems;
+ }
+ }
+ else if ((unsigned)imageIndex < Images.Size())
+ {
+ const CImage &image = Images[imageIndex];
+ startItem = image.StartItem;
+ endItem = startItem + image.NumItems;
+ if (!showImageNumber)
+ NumExludededItems = image.NumEmptyRootItems;
+ }
+ if (NumExludededItems != 0)
+ {
+ ExludedItem = startItem;
+ startItem += NumExludededItems;
+ }
+
+ unsigned num = endItem - startItem;
+ SortedItems.ClearAndSetSize(num);
+ unsigned i;
+ for (i = 0; i < num; i++)
+ SortedItems[i] = startItem + i;
- SortedItems.Reserve(Items.Size());
- for (int i = (skipRootDir ? 1 : 0); i < Items.Size(); i++)
- SortedItems.Add(i);
SortedItems.Sort(CompareItems, this);
+ for (i = 0; i < SortedItems.Size(); i++)
+ Items[SortedItems[i]].IndexInSorted = i;
+
+ if (showImageNumber)
+ for (i = 0; i < Images.Size(); i++)
+ {
+ CImage &image = Images[i];
+ if (image.NumEmptyRootItems != 0)
+ continue;
+ image.VirtualRootIndex = VirtualRoots.Size();
+ VirtualRoots.Add(i);
+ }
+ return S_OK;
+}
+
+static void IntVector_SetMinusOne_IfNeed(CIntVector &v, unsigned size)
+{
+ if (v.Size() == size)
+ return;
+ v.ClearAndSetSize(size);
+ int *vals = &v[0];
+ for (unsigned i = 0; i < size; i++)
+ vals[i] = -1;
+}
+
+HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, IArchiveOpenCallback *openCallback)
+{
+ ItemToReparse.Clear();
+ ReparseItems.Clear();
+
+ // we don't know about Reparse field for OLD WIM format
+ if (IsOldVersion)
+ return S_OK;
+
+ CIntVector streamToReparse;
+
+ FOR_VECTOR(i, Items)
+ {
+ // maybe it's better to use sorted items for faster access?
+ const CItem &item = Items[i];
+
+ if (!item.HasMetadata() || item.IsAltStream)
+ continue;
+
+ if (item.ImageIndex < 0)
+ continue;
+
+ const Byte *metadata = Images[item.ImageIndex].Meta + item.Offset;
+
+ const UInt32 attrib = Get32(metadata + 8);
+ if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+ continue;
+
+ if (item.StreamIndex < 0)
+ continue; // it's ERROR
+
+ const CStreamInfo &si = DataStreams[item.StreamIndex];
+ if (si.Resource.UnpackSize >= (1 << 16))
+ continue; // reparse data can not be larger than 64 KB
+
+ IntVector_SetMinusOne_IfNeed(streamToReparse, DataStreams.Size());
+ IntVector_SetMinusOne_IfNeed(ItemToReparse, Items.Size());
+
+ const unsigned offset = 0x58; // we don't know about Reparse field for OLD WIM format
+ UInt32 tag = Get32(metadata + offset);
+ int reparseIndex = streamToReparse[item.StreamIndex];
+ CByteBuffer buf;
+
+ if (openCallback && (i & 0xFFFF) == 0)
+ {
+ UInt64 numFiles = Items.Size();
+ RINOK(openCallback->SetCompleted(&numFiles, NULL));
+ }
+
+ if (reparseIndex >= 0)
+ {
+ const CByteBuffer &reparse = ReparseItems[reparseIndex];
+ if (tag == Get32(reparse))
+ {
+ ItemToReparse[i] = reparseIndex;
+ continue;
+ }
+ buf = reparse;
+ // we support that strange and unusual situation with different tags and same reparse data.
+ }
+ else
+ {
+ /*
+ if (si.PartNumber >= volumes.Size())
+ continue;
+ */
+ const CVolume &vol = volumes[si.PartNumber];
+ /*
+ if (!vol.Stream)
+ continue;
+ */
+
+ Byte digest[kHashSize];
+ HRESULT res = UnpackData(vol.Stream, si.Resource, vol.Header.IsLzxMode(), buf, digest);
+
+ if (res == S_FALSE)
+ continue;
+
+ RINOK(res);
+
+ if (memcmp(digest, si.Hash, kHashSize) != 0
+ // && !(h.IsOldVersion() && IsEmptySha(si.Hash))
+ )
+ {
+ // setErrorStatus;
+ continue;
+ }
+ }
+
+ CByteBuffer &reparse = ReparseItems.AddNew();
+ reparse.Alloc(8 + buf.Size());
+ Byte *dest = (Byte *)reparse;
+ SetUi32(dest, tag);
+ SetUi32(dest + 4, (UInt32)buf.Size());
+ memcpy(dest + 8, buf, buf.Size());
+ ItemToReparse[i] = ReparseItems.Size() - 1;
+ }
+
return S_OK;
}
+
+
+static bool ParseNumber64(const AString &s, UInt64 &res)
+{
+ const char *end;
+ if (s.IsPrefixedBy("0x"))
+ {
+ if (s.Len() == 2)
+ return false;
+ res = ConvertHexStringToUInt64(s.Ptr(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;
+}
+
+static 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);
+
+ ParseNumber64(item.GetSubStringForTag("DIRCOUNT"), DirCount);
+ ParseNumber64(item.GetSubStringForTag("FILECOUNT"), FileCount);
+ IndexDefined = ParseNumber32(item.GetPropVal("INDEX"), Index);
+}
+
+void CWimXml::ToUnicode(UString &s)
+{
+ size_t size = Data.Size();
+ if (size < 2 || (size & 1) != 0 || size > (1 << 24))
+ return;
+ const Byte *p = Data;
+ if (Get16(p) != 0xFEFF)
+ return;
+ wchar_t *chars = s.GetBuffer((unsigned)size / 2);
+ for (size_t i = 2; i < size; i += 2)
+ *chars++ = (wchar_t)Get16(p + i);
+ *chars = 0;
+ s.ReleaseBuffer();
+}
+
+bool CWimXml::Parse()
+{
+ UString s;
+ ToUnicode(s);
+ AString utf;
+ if (!ConvertUnicodeToUTF8(s, utf))
+ return false;
+ if (!Xml.Parse(utf))
+ return false;
+ if (Xml.Root.Name != "WIM")
+ return false;
+
+ FOR_VECTOR (i, Xml.Root.SubItems)
+ {
+ const CXmlItem &item = Xml.Root.SubItems[i];
+ if (item.IsTagged("IMAGE"))
+ {
+ CImageInfo imageInfo;
+ imageInfo.Parse(item);
+ if (!imageInfo.IndexDefined || imageInfo.Index != (UInt32)Images.Size() + 1)
+ return false;
+ imageInfo.ItemIndexInXml = i;
+ Images.Add(imageInfo);
+ }
+ }
+ return true;
+}
+
}}