1 // PanelCrc.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/MyException.h"
6
7 #include "../../../Windows/FileFind.h"
8 #include "../../../Windows/FileIO.h"
9 #include "../../../Windows/FileName.h"
10
11 #include "../Common/LoadCodecs.h"
12
13 #include "../GUI/HashGUI.h"
14
15 #include "App.h"
16 #include "LangUtils.h"
17
18 #include "resource.h"
19
20 using namespace NWindows;
21 using namespace NFile;
22
23 #ifdef Z7_EXTERNAL_CODECS
24 extern CExternalCodecs g_ExternalCodecs;
25 HRESULT LoadGlobalCodecs();
26 #endif
27
28 static const UInt32 kBufSize = (1 << 15);
29
30 struct CDirEnumerator
31 {
32 bool EnterToDirs;
33 FString BasePrefix;
34 FString BasePrefix_for_Open;
35 FStringVector FilePaths;
36
37 CObjectVector<NFind::CEnumerator> Enumerators;
38 FStringVector Prefixes;
39 unsigned Index;
40
CDirEnumeratorCDirEnumerator41 CDirEnumerator(): EnterToDirs(false), Index(0) {}
42
43 void Init();
44 DWORD GetNextFile(NFind::CFileInfo &fi, bool &filled, FString &resPath);
45 };
46
Init()47 void CDirEnumerator::Init()
48 {
49 Enumerators.Clear();
50 Prefixes.Clear();
51 Index = 0;
52 }
53
GetNormalizedError()54 static DWORD GetNormalizedError()
55 {
56 const DWORD error = GetLastError();
57 return (error == 0) ? (DWORD)E_FAIL : error;
58 }
59
GetNextFile(NFind::CFileInfo & fi,bool & filled,FString & resPath)60 DWORD CDirEnumerator::GetNextFile(NFind::CFileInfo &fi, bool &filled, FString &resPath)
61 {
62 filled = false;
63 resPath.Empty();
64
65 for (;;)
66 {
67 #if defined(_WIN32) && !defined(UNDER_CE)
68 bool isRootPrefix = (BasePrefix.IsEmpty() || (NName::IsSuperPath(BasePrefix) && BasePrefix[NName::kSuperPathPrefixSize] == 0));
69 #endif
70
71 if (Enumerators.IsEmpty())
72 {
73 if (Index >= FilePaths.Size())
74 return S_OK;
75 const FString &path = FilePaths[Index++];
76 const int pos = path.ReverseFind_PathSepar();
77 if (pos >= 0)
78 resPath.SetFrom(path, (unsigned)pos + 1);
79
80 #if defined(_WIN32) && !defined(UNDER_CE)
81 if (isRootPrefix && path.Len() == 2 && NName::IsDrivePath2(path))
82 {
83 // we use "c:" item as directory item
84 fi.ClearBase();
85 fi.Name = path;
86 fi.SetAsDir();
87 fi.Size = 0;
88 }
89 else
90 #endif
91 if (!fi.Find(BasePrefix + path))
92 {
93 const DWORD error = GetNormalizedError();
94 resPath = path;
95 return error;
96 }
97
98 break;
99 }
100
101 bool found;
102
103 if (Enumerators.Back().Next(fi, found))
104 {
105 if (found)
106 {
107 resPath = Prefixes.Back();
108 break;
109 }
110 }
111 else
112 {
113 const DWORD error = GetNormalizedError();
114 resPath = Prefixes.Back();
115 Enumerators.DeleteBack();
116 Prefixes.DeleteBack();
117 return error;
118 }
119
120 Enumerators.DeleteBack();
121 Prefixes.DeleteBack();
122 }
123
124 resPath += fi.Name;
125
126 if (EnterToDirs && fi.IsDir())
127 {
128 FString s = resPath;
129 s.Add_PathSepar();
130 Prefixes.Add(s);
131 Enumerators.AddNew().SetDirPrefix(BasePrefix + s);
132 }
133
134 filled = true;
135 return S_OK;
136 }
137
138
139
140 class CThreadCrc: public CProgressThreadVirt
141 {
142 bool ResultsWereShown;
143 bool WasFinished;
144
145 HRESULT ProcessVirt() Z7_override;
146 virtual void ProcessWasFinished_GuiVirt() Z7_override;
147 public:
148 CDirEnumerator Enumerator;
149 CHashBundle Hash;
150 // FString FirstFilePath;
151
152 void SetStatus(const UString &s);
153 void AddErrorMessage(DWORD systemError, const FChar *name);
154 void ShowFinalResults(HWND hwnd);
155
CThreadCrc()156 CThreadCrc():
157 ResultsWereShown(false),
158 WasFinished(false)
159 {}
160 };
161
ShowFinalResults(HWND hwnd)162 void CThreadCrc::ShowFinalResults(HWND hwnd)
163 {
164 if (WasFinished)
165 if (!ResultsWereShown)
166 {
167 ResultsWereShown = true;
168 ShowHashResults(Hash, hwnd);
169 }
170 }
171
ProcessWasFinished_GuiVirt()172 void CThreadCrc::ProcessWasFinished_GuiVirt()
173 {
174 ShowFinalResults(*this);
175 }
176
AddErrorMessage(DWORD systemError,const FChar * name)177 void CThreadCrc::AddErrorMessage(DWORD systemError, const FChar *name)
178 {
179 Sync.AddError_Code_Name(HRESULT_FROM_WIN32(systemError), fs2us(Enumerator.BasePrefix + name));
180 Hash.NumErrors++;
181 }
182
SetStatus(const UString & s2)183 void CThreadCrc::SetStatus(const UString &s2)
184 {
185 UString s = s2;
186 if (!Enumerator.BasePrefix.IsEmpty())
187 {
188 s.Add_Space_if_NotEmpty();
189 s += fs2us(Enumerator.BasePrefix);
190 }
191 Sync.Set_Status(s);
192 }
193
ProcessVirt()194 HRESULT CThreadCrc::ProcessVirt()
195 {
196 // Hash.Init();
197
198 CMyBuffer buf;
199 if (!buf.Allocate(kBufSize))
200 return E_OUTOFMEMORY;
201
202 CProgressSync &sync = Sync;
203
204 SetStatus(LangString(IDS_SCANNING));
205
206 Enumerator.Init();
207
208 FString path;
209 NFind::CFileInfo fi;
210 UInt64 numFiles = 0;
211 UInt64 numItems = 0, numItems_Prev = 0;
212 UInt64 totalSize = 0;
213
214 for (;;)
215 {
216 bool filled;
217 const DWORD error = Enumerator.GetNextFile(fi, filled, path);
218 if (error != 0)
219 {
220 AddErrorMessage(error, path);
221 continue;
222 }
223 if (!filled)
224 break;
225 if (!fi.IsDir())
226 {
227 totalSize += fi.Size;
228 numFiles++;
229 }
230 numItems++;
231 bool needPrint = false;
232 // if (fi.IsDir())
233 {
234 if (numItems - numItems_Prev >= 100)
235 {
236 needPrint = true;
237 numItems_Prev = numItems;
238 }
239 }
240 /*
241 else if (numFiles - numFiles_Prev >= 200)
242 {
243 needPrint = true;
244 numFiles_Prev = numFiles;
245 }
246 */
247 if (needPrint)
248 {
249 RINOK(sync.ScanProgress(numFiles, totalSize, path, fi.IsDir()))
250 }
251 }
252 RINOK(sync.ScanProgress(numFiles, totalSize, FString(), false))
253 // sync.SetNumFilesTotal(numFiles);
254 // sync.SetProgress(totalSize, 0);
255 // SetStatus(LangString(IDS_CHECKSUM_CALCULATING));
256 // sync.SetCurFilePath(L"");
257 SetStatus(L"");
258
259 Enumerator.Init();
260
261 FString tempPath;
262 bool isFirstFile = true;
263 UInt64 errorsFilesSize = 0;
264
265 for (;;)
266 {
267 bool filled;
268 DWORD error = Enumerator.GetNextFile(fi, filled, path);
269 if (error != 0)
270 {
271 AddErrorMessage(error, path);
272 continue;
273 }
274 if (!filled)
275 break;
276
277 error = 0;
278 Hash.InitForNewFile();
279 if (!fi.IsDir())
280 {
281 NIO::CInFile inFile;
282 tempPath = Enumerator.BasePrefix_for_Open;
283 tempPath += path;
284 if (!inFile.Open(tempPath))
285 {
286 error = GetNormalizedError();
287 AddErrorMessage(error, path);
288 continue;
289 }
290 if (isFirstFile)
291 {
292 Hash.FirstFileName = fs2us(path);
293 isFirstFile = false;
294 }
295 sync.Set_FilePath(fs2us(path));
296 sync.Set_NumFilesCur(Hash.NumFiles);
297 UInt64 progress_Prev = 0;
298 for (;;)
299 {
300 UInt32 size;
301 if (!inFile.Read(buf, kBufSize, size))
302 {
303 error = GetNormalizedError();
304 AddErrorMessage(error, path);
305 UInt64 errorSize = 0;
306 if (inFile.GetLength(errorSize))
307 errorsFilesSize += errorSize;
308 break;
309 }
310 if (size == 0)
311 break;
312 Hash.Update(buf, size);
313 if (Hash.CurSize - progress_Prev >= ((UInt32)1 << 21))
314 {
315 RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize + Hash.CurSize))
316 progress_Prev = Hash.CurSize;
317 }
318 }
319 }
320 if (error == 0)
321 Hash.Final(fi.IsDir(), false, fs2us(path));
322 RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize))
323 }
324 RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize))
325 sync.Set_NumFilesCur(Hash.NumFiles);
326 if (Hash.NumFiles != 1)
327 sync.Set_FilePath(L"");
328 SetStatus(L"");
329
330 CProgressMessageBoxPair &pair = GetMessagePair(Hash.NumErrors != 0);
331 WasFinished = true;
332 LangString(IDS_CHECKSUM_INFORMATION, pair.Title);
333 return S_OK;
334 }
335
336
337
CalculateCrc2(const UString & methodName)338 HRESULT CApp::CalculateCrc2(const UString &methodName)
339 {
340 unsigned srcPanelIndex = GetFocusedPanelIndex();
341 CPanel &srcPanel = Panels[srcPanelIndex];
342
343 CRecordVector<UInt32> indices;
344 srcPanel.Get_ItemIndices_OperSmart(indices);
345 if (indices.IsEmpty())
346 return S_OK;
347
348 if (!srcPanel.Is_IO_FS_Folder())
349 {
350 CCopyToOptions options;
351 options.streamMode = true;
352 options.showErrorMessages = true;
353 options.hashMethods.Add(methodName);
354 options.NeedRegistryZone = false;
355
356 UStringVector messages;
357 return srcPanel.CopyTo(options, indices, &messages);
358 }
359
360 #ifdef Z7_EXTERNAL_CODECS
361
362 LoadGlobalCodecs();
363
364 #endif
365
366 {
367 CThreadCrc t;
368
369 {
370 UStringVector methods;
371 methods.Add(methodName);
372 RINOK(t.Hash.SetMethods(EXTERNAL_CODECS_VARS_G methods))
373 }
374
375 FOR_VECTOR (i, indices)
376 t.Enumerator.FilePaths.Add(us2fs(srcPanel.GetItemRelPath(indices[i])));
377
378 if (t.Enumerator.FilePaths.Size() == 1)
379 t.Hash.MainName = fs2us(t.Enumerator.FilePaths[0]);
380
381 UString basePrefix = srcPanel.GetFsPath();
382 UString basePrefix2 = basePrefix;
383 if (basePrefix2.Back() == ':')
384 {
385 const int pos = basePrefix2.ReverseFind_PathSepar();
386 if (pos >= 0)
387 basePrefix2.DeleteFrom((unsigned)(pos + 1));
388 }
389
390 t.Enumerator.BasePrefix = us2fs(basePrefix);
391 t.Enumerator.BasePrefix_for_Open = us2fs(basePrefix2);
392
393 t.Enumerator.EnterToDirs = !GetFlatMode();
394
395 t.ShowCompressionInfo = false;
396
397 const UString title = LangString(IDS_CHECKSUM_CALCULATING);
398
399 t.MainWindow = _window;
400 t.MainTitle = "7-Zip"; // LangString(IDS_APP_TITLE);
401 t.MainAddTitle = title;
402 t.MainAddTitle.Add_Space();
403
404 RINOK(t.Create(title, _window))
405
406 t.ShowFinalResults(_window);
407 }
408
409 RefreshTitleAlways();
410 return S_OK;
411 }
412
CalculateCrc(const char * methodName)413 void CApp::CalculateCrc(const char *methodName)
414 {
415 HRESULT res = CalculateCrc2(UString(methodName));
416 if (res != S_OK && res != E_ABORT)
417 {
418 unsigned srcPanelIndex = GetFocusedPanelIndex();
419 CPanel &srcPanel = Panels[srcPanelIndex];
420 srcPanel.MessageBox_Error_HRESULT(res);
421 }
422 }
423