1cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky// HashCalc.cpp
2cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
3cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "StdAfx.h"
4cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
5cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "../../../../C/Alloc.h"
6cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
7cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "../../../Common/StringToInt.h"
8cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
9cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "../../Common/FileStreams.h"
10cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "../../Common/StreamUtils.h"
11cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
12cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "EnumDirItems.h"
13cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky#include "HashCalc.h"
14cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
15cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyusing namespace NWindows;
16cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
17cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyclass CHashMidBuf
18cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
19cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  void *_data;
20cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckypublic:
21cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CHashMidBuf(): _data(0) {}
22cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  operator void *() { return _data; }
23cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  bool Alloc(size_t size)
24cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
25cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (_data != 0)
26cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      return false;
27cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    _data = ::MidAlloc(size);
28cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    return _data != 0;
29cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
30cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  ~CHashMidBuf() { ::MidFree(_data); }
31cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky};
32cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
33cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckystruct CEnumDirItemCallback_Hash: public IEnumDirItemCallback
34cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
35cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  IHashCallbackUI *Callback;
36cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
37cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir)
38cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
39cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir);
40cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
41cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky};
42cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
43cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckystatic const wchar_t *k_DefaultHashMethod = L"CRC32";
44cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
45cd66d540cead3f8200b0c73bad9c276d67896c3dDavid SrbeckyHRESULT CHashBundle::SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &hashMethods)
46cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
47cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  UStringVector names = hashMethods;
48cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (names.IsEmpty())
49cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    names.Add(k_DefaultHashMethod);
50cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
51cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CRecordVector<CMethodId> ids;
52cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CObjectVector<COneMethodInfo> methods;
53cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
54cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  unsigned i;
55cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  for (i = 0; i < names.Size(); i++)
56cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
57cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    COneMethodInfo m;
58cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(m.ParseMethodFromString(names[i]));
59cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
60cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (m.MethodName.IsEmpty())
61cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      m.MethodName = k_DefaultHashMethod;
62cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
63cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (m.MethodName == L"*")
64cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
65cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      CRecordVector<CMethodId> tempMethods;
66cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods);
67cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      methods.Clear();
68cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      ids.Clear();
69cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      FOR_VECTOR (t, tempMethods)
70cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      {
71cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        int index = ids.AddToUniqueSorted(tempMethods[t]);
72cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        if (ids.Size() != methods.Size())
73cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          methods.Insert(index, m);
74cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      }
75cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      break;
76cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
77cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    else
78cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
79cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      // m.MethodName.RemoveChar(L'-');
80cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      CMethodId id;
81cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      if (!FindHashMethod(EXTERNAL_CODECS_LOC_VARS m.MethodName, id))
82cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        return E_NOTIMPL;
83cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      int index = ids.AddToUniqueSorted(id);
84cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      if (ids.Size() != methods.Size())
85cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        methods.Insert(index, m);
86cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
87cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
88cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
89cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  for (i = 0; i < ids.Size(); i++)
90cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
91cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CMyComPtr<IHasher> hasher;
92cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    UString name;
93cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS ids[i], name, hasher));
94cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (!hasher)
95cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      throw "Can't create hasher";
96cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    const COneMethodInfo &m = methods[i];
97cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
98cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      CMyComPtr<ICompressSetCoderProperties> scp;
99cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp);
100cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      if (scp)
101cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      {
102cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        RINOK(m.SetCoderProps(scp, NULL));
103cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      }
104cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
105cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    UInt32 digestSize = hasher->GetDigestSize();
106cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (digestSize > k_HashCalc_DigestSize_Max)
107cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      return E_NOTIMPL;
108cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CHasherState &h = Hashers.AddNew();
109cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Hasher = hasher;
110cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Name = name;
111cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.DigestSize = digestSize;
112cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    for (int i = 0; i < k_HashCalc_NumGroups; i++)
113cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      memset(h.Digests[i], 0, digestSize);
114cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
115cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  return S_OK;
116cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
117cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
118cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyvoid CHashBundle::InitForNewFile()
119cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
120cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CurSize = 0;
121cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  FOR_VECTOR (i, Hashers)
122cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
123cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CHasherState &h = Hashers[i];
124cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Hasher->Init();
125cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    memset(h.Digests[k_HashCalc_Index_Current], 0, h.DigestSize);
126cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
127cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
128cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
129cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyvoid CHashBundle::Update(const void *data, UInt32 size)
130cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
131cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CurSize += size;
132cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  FOR_VECTOR (i, Hashers)
133cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    Hashers[i].Hasher->Update(data, size);
134cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
135cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
136cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyvoid CHashBundle::SetSize(UInt64 size)
137cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
138cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CurSize = size;
139cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
140cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
141cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckystatic void AddDigests(Byte *dest, const Byte *src, UInt32 size)
142cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
143cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  unsigned next = 0;
144cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  for (UInt32 i = 0; i < size; i++)
145cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
146cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    next += (unsigned)dest[i] + (unsigned)src[i];
147cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dest[i] = (Byte)next;
148cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    next >>= 8;
149cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
150cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
151cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
152cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyvoid CHashBundle::Final(bool isDir, bool isAltStream, const UString &path)
153cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
154cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (isDir)
155cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    NumDirs++;
156cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  else if (isAltStream)
157cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
158cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    NumAltStreams++;
159cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    AltStreamsSize += CurSize;
160cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
161cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  else
162cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
163cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    NumFiles++;
164cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    FilesSize += CurSize;
165cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
166cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
167cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  Byte pre[16];
168cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  memset(pre, 0, sizeof(pre));
169cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (isDir)
170cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    pre[0] = 1;
171cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
172cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  FOR_VECTOR (i, Hashers)
173cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
174cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CHasherState &h = Hashers[i];
175cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (!isDir)
176cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
177cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      h.Hasher->Final(h.Digests[0]);
178cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      if (!isAltStream)
179cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        AddDigests(h.Digests[k_HashCalc_Index_DataSum], h.Digests[0], h.DigestSize);
180cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
181cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
182cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Hasher->Init();
183cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Hasher->Update(pre, sizeof(pre));
184cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Hasher->Update(h.Digests[0], h.DigestSize);
185cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
186cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    for (unsigned k = 0; k < path.Len(); k++)
187cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
188cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      wchar_t c = path[k];
189cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      Byte temp[2] = { (Byte)(c & 0xFF), (Byte)((c >> 8) & 0xFF) };
190cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      h.Hasher->Update(temp, 2);
191cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
192cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
193cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    Byte tempDigest[k_HashCalc_DigestSize_Max];
194cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    h.Hasher->Final(tempDigest);
195cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (!isAltStream)
196cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      AddDigests(h.Digests[k_HashCalc_Index_NamesSum], tempDigest, h.DigestSize);
197cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    AddDigests(h.Digests[k_HashCalc_Index_StreamsSum], tempDigest, h.DigestSize);
198cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
199cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
200cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
201cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
202cd66d540cead3f8200b0c73bad9c276d67896c3dDavid SrbeckyHRESULT HashCalc(
203cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    DECL_EXTERNAL_CODECS_LOC_VARS
204cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    const NWildcard::CCensor &censor,
205cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    const CHashOptions &options,
206cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    UString &errorInfo,
207cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    IHashCallbackUI *callback)
208cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
209cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CDirItems dirItems;
210cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
211cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  UInt64 numErrors = 0;
212cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  UInt64 totalBytes = 0;
213cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (options.StdInMode)
214cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
215cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CDirItem di;
216cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    di.Size = (UInt64)(Int64)-1;
217cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    di.Attrib = 0;
218cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    di.MTime.dwLowDateTime = 0;
219cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    di.MTime.dwHighDateTime = 0;
220cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    di.CTime = di.ATime = di.MTime;
221cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dirItems.Items.Add(di);
222cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
223cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  else
224cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
225cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CEnumDirItemCallback_Hash enumCallback;
226cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    enumCallback.Callback = callback;
227cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->StartScanning());
228cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dirItems.ScanAltStreams = options.AltStreamsMode;
229cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    HRESULT res = EnumerateItems(censor,
230cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        options.PathMode,
231cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        UString(),
232cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        dirItems, &enumCallback);
233cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    totalBytes = dirItems.TotalSize;
234cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    FOR_VECTOR (i, dirItems.ErrorPaths)
235cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
236cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i]));
237cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
238cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    numErrors = dirItems.ErrorPaths.Size();
239cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (res != S_OK)
240cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
241cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      if (res != E_ABORT)
242cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        errorInfo = L"Scanning error";
243cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      return res;
244cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
245cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->FinishScanning());
246cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
247cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
248cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  unsigned i;
249cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CHashBundle hb;
250cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS options.Methods));
251cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  hb.Init();
252cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  hb.NumErrors = numErrors;
253cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
254cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (options.StdInMode)
255cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
256cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->SetNumFiles(1));
257cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
258cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  else
259cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
260cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->SetTotal(totalBytes));
261cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
262cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
263cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  const UInt32 kBufSize = 1 << 15;
264cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  CHashMidBuf buf;
265cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (!buf.Alloc(kBufSize))
266cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    return E_OUTOFMEMORY;
267cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
268cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  UInt64 completeValue = 0;
269cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
270cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  RINOK(callback->BeforeFirstFile(hb));
271cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
272cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  for (i = 0; i < dirItems.Items.Size(); i++)
273cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
274cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    CMyComPtr<ISequentialInStream> inStream;
275cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    UString path;
276cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    bool isDir = false;
277cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    bool isAltStream = false;
278cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (options.StdInMode)
279cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
280cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      inStream = new CStdInFileStream;
281cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
282cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    else
283cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
284cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      CInFileStream *inStreamSpec = new CInFileStream;
285cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      inStream = inStreamSpec;
286cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      const CDirItem &dirItem = dirItems.Items[i];
287cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      isDir = dirItem.IsDir();
288cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      isAltStream = dirItem.IsAltStream;
289cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      path = dirItems.GetLogPath(i);
290cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      if (!isDir)
291cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      {
292cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        UString phyPath = dirItems.GetPhyPath(i);
293cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        if (!inStreamSpec->OpenShared(us2fs(phyPath), options.OpenShareForWrite))
294cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        {
295cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          HRESULT res = callback->OpenFileError(phyPath, ::GetLastError());
296cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          hb.NumErrors++;
297cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          if (res != S_FALSE)
298cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky            return res;
299cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          continue;
300cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        }
301cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      }
302cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
303cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->GetStream(path, isDir));
304cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    UInt64 fileSize = 0;
305cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
306cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    hb.InitForNewFile();
307cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    if (!isDir)
308cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
309cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      for (UInt32 step = 0;; step++)
310cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      {
311cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        if ((step & 0xFF) == 0)
312cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          RINOK(callback->SetCompleted(&completeValue));
313cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        UInt32 size;
314cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        RINOK(inStream->Read(buf, kBufSize, &size));
315cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        if (size == 0)
316cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky          break;
317cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        hb.Update(buf, size);
318cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        fileSize += size;
319cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky        completeValue += size;
320cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      }
321cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
322cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    hb.Final(isDir, isAltStream, path);
323cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->SetOperationResult(fileSize, hb, !isDir));
324cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    RINOK(callback->SetCompleted(&completeValue));
325cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
326cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  return callback->AfterLastFile(hb);
327cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
328cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
329cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
330cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckystatic inline char GetHex(Byte value)
331cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
332cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
333cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
334cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky
335cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbeckyvoid AddHashHexToString(char *dest, const Byte *data, UInt32 size)
336cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky{
337cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  dest[size * 2] = 0;
338cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (!data)
339cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
340cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    for (UInt32 i = 0; i < size; i++)
341cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    {
342cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      dest[0] = ' ';
343cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      dest[1] = ' ';
344cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky      dest += 2;
345cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    }
346cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    return;
347cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
348cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  int step = 2;
349cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  if (size <= 8)
350cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
351cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    step = -2;
352cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dest += size * 2 - 2;
353cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
354cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  for (UInt32 i = 0; i < size; i++)
355cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  {
356cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    Byte b = data[i];
357cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dest[0] = GetHex((Byte)((b >> 4) & 0xF));
358cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dest[1] = GetHex((Byte)(b & 0xF));
359cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky    dest += step;
360cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky  }
361cd66d540cead3f8200b0c73bad9c276d67896c3dDavid Srbecky}
362