mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-13 18:11:37 -06:00
9.14
This commit is contained in:
committed by
Kornel Lesiński
parent
3dacb5eb8a
commit
708873490e
647
CPP/7zip/Archive/Wim/WimHandlerOut.cpp
Executable file
647
CPP/7zip/Archive/Wim/WimHandlerOut.cpp
Executable file
@@ -0,0 +1,647 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
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 void ConvertUInt32ToHex(UInt32 value, char *s)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int t = value & 0xF;
|
||||
value >>= 4;
|
||||
s[7 - i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
|
||||
}
|
||||
s[8] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static AString TimeToXml(FILETIME &ft)
|
||||
{
|
||||
AString res;
|
||||
char temp[16] = { '0', 'x' };
|
||||
ConvertUInt32ToHex(ft.dwHighDateTime, temp + 2);
|
||||
AddTag(res, "HIGHPART", temp);
|
||||
ConvertUInt32ToHex(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);
|
||||
// memset(meta, 0, kSecuritySize);
|
||||
Set32((Byte *)meta, 0); // only if there is no security data, we can use 0 here.
|
||||
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
|
||||
}
|
||||
|
||||
}}
|
||||
Reference in New Issue
Block a user