// PanelCrc.cpp #include "StdAfx.h" #include "../../../Common/MyException.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/FileName.h" #include "../Common/LoadCodecs.h" #include "../GUI/HashGUI.h" #include "App.h" #include "LangUtils.h" #include "resource.h" using namespace NWindows; using namespace NFile; #ifdef EXTERNAL_CODECS extern CExternalCodecs g_ExternalCodecs; HRESULT LoadGlobalCodecs(); #endif static const UInt32 kBufSize = (1 << 15); struct CDirEnumerator { bool EnterToDirs; FString BasePrefix; FString BasePrefix_for_Open; FStringVector FilePaths; CObjectVector Enumerators; FStringVector Prefixes; unsigned Index; CDirEnumerator(): EnterToDirs(false), Index(0) {}; void Init(); DWORD GetNextFile(NFind::CFileInfo &fi, bool &filled, FString &resPath); }; void CDirEnumerator::Init() { Enumerators.Clear(); Prefixes.Clear(); Index = 0; } static DWORD GetNormalizedError() { DWORD error = GetLastError(); return (error == 0) ? E_FAIL : error; } DWORD CDirEnumerator::GetNextFile(NFind::CFileInfo &fi, bool &filled, FString &resPath) { filled = false; resPath.Empty(); for (;;) { #if defined(_WIN32) && !defined(UNDER_CE) bool isRootPrefix = (BasePrefix.IsEmpty() || (NName::IsSuperPath(BasePrefix) && BasePrefix[NName::kSuperPathPrefixSize] == 0)); #endif if (Enumerators.IsEmpty()) { if (Index >= FilePaths.Size()) return S_OK; const FString &path = FilePaths[Index++]; int pos = path.ReverseFind_PathSepar(); if (pos >= 0) resPath.SetFrom(path, pos + 1); #if defined(_WIN32) && !defined(UNDER_CE) if (isRootPrefix && path.Len() == 2 && NName::IsDrivePath2(path)) { // we use "c:" item as directory item fi.ClearBase(); fi.Name = path; fi.SetAsDir(); fi.Size = 0; } else #endif if (!fi.Find(BasePrefix + path)) { DWORD error = GetNormalizedError(); resPath = path; return error; } break; } bool found; if (Enumerators.Back().Next(fi, found)) { if (found) { resPath = Prefixes.Back(); break; } } else { DWORD error = GetNormalizedError(); resPath = Prefixes.Back(); Enumerators.DeleteBack(); Prefixes.DeleteBack(); return error; } Enumerators.DeleteBack(); Prefixes.DeleteBack(); } resPath += fi.Name; if (EnterToDirs && fi.IsDir()) { FString s = resPath; s.Add_PathSepar(); Prefixes.Add(s); Enumerators.AddNew().SetDirPrefix(BasePrefix + s); } filled = true; return S_OK; } class CThreadCrc: public CProgressThreadVirt { HRESULT ProcessVirt(); public: CDirEnumerator Enumerator; CHashBundle Hash; void SetStatus(const UString &s); void AddErrorMessage(DWORD systemError, const FChar *name); }; void CThreadCrc::AddErrorMessage(DWORD systemError, const FChar *name) { ProgressDialog.Sync.AddError_Code_Name(systemError, fs2us(Enumerator.BasePrefix + name)); Hash.NumErrors++; } void CThreadCrc::SetStatus(const UString &s2) { UString s = s2; if (!Enumerator.BasePrefix.IsEmpty()) { s.Add_Space_if_NotEmpty(); s += fs2us(Enumerator.BasePrefix); } ProgressDialog.Sync.Set_Status(s); } HRESULT CThreadCrc::ProcessVirt() { Hash.Init(); CMyBuffer buf; if (!buf.Allocate(kBufSize)) return E_OUTOFMEMORY; CProgressSync &sync = ProgressDialog.Sync; SetStatus(LangString(IDS_SCANNING)); Enumerator.Init(); FString path; NFind::CFileInfo fi; UInt64 numFiles = 0; UInt64 numItems = 0, numItems_Prev = 0; UInt64 totalSize = 0; for (;;) { bool filled; DWORD error = Enumerator.GetNextFile(fi, filled, path); if (error != 0) { AddErrorMessage(error, path); continue; } if (!filled) break; if (!fi.IsDir()) { totalSize += fi.Size; numFiles++; } numItems++; bool needPrint = false; // if (fi.IsDir()) { if (numItems - numItems_Prev >= 100) { needPrint = true; numItems_Prev = numItems; } } /* else if (numFiles - numFiles_Prev >= 200) { needPrint = true; numFiles_Prev = numFiles; } */ if (needPrint) { RINOK(sync.ScanProgress(numFiles, totalSize, path, fi.IsDir())); } } RINOK(sync.ScanProgress(numFiles, totalSize, FString(), false)); // sync.SetNumFilesTotal(numFiles); // sync.SetProgress(totalSize, 0); // SetStatus(LangString(IDS_CHECKSUM_CALCULATING)); // sync.SetCurFilePath(L""); SetStatus(L""); Enumerator.Init(); FString tempPath; FString firstFilePath; bool isFirstFile = true; UInt64 errorsFilesSize = 0; for (;;) { bool filled; DWORD error = Enumerator.GetNextFile(fi, filled, path); if (error != 0) { AddErrorMessage(error, path); continue; } if (!filled) break; error = 0; Hash.InitForNewFile(); if (!fi.IsDir()) { NIO::CInFile inFile; tempPath = Enumerator.BasePrefix_for_Open; tempPath += path; if (!inFile.Open(tempPath)) { error = GetNormalizedError(); AddErrorMessage(error, path); continue; } if (isFirstFile) { firstFilePath = path; isFirstFile = false; } sync.Set_FilePath(fs2us(path)); sync.Set_NumFilesCur(Hash.NumFiles); UInt64 progress_Prev = 0; for (;;) { UInt32 size; if (!inFile.Read(buf, kBufSize, size)) { error = GetNormalizedError(); AddErrorMessage(error, path); UInt64 errorSize = 0; if (inFile.GetLength(errorSize)) errorsFilesSize += errorSize; break; } if (size == 0) break; Hash.Update(buf, size); if (Hash.CurSize - progress_Prev >= ((UInt32)1 << 21)) { RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize + Hash.CurSize)); progress_Prev = Hash.CurSize; } } } if (error == 0) Hash.Final(fi.IsDir(), false, fs2us(path)); RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize)); } RINOK(sync.Set_NumBytesCur(errorsFilesSize + Hash.FilesSize)); sync.Set_NumFilesCur(Hash.NumFiles); if (Hash.NumFiles != 1) sync.Set_FilePath(L""); SetStatus(L""); CProgressMessageBoxPair &pair = GetMessagePair(Hash.NumErrors != 0); AddHashBundleRes(pair.Message, Hash, fs2us(firstFilePath)); LangString(IDS_CHECKSUM_INFORMATION, pair.Title); return S_OK; } HRESULT CApp::CalculateCrc2(const UString &methodName) { unsigned srcPanelIndex = GetFocusedPanelIndex(); CPanel &srcPanel = Panels[srcPanelIndex]; CRecordVector indices; srcPanel.GetOperatedIndicesSmart(indices); if (indices.IsEmpty()) return S_OK; if (!srcPanel.Is_IO_FS_Folder()) { CCopyToOptions options; options.streamMode = true; options.showErrorMessages = true; options.hashMethods.Add(methodName); UStringVector messages; return srcPanel.CopyTo(options, indices, &messages); } #ifdef EXTERNAL_CODECS LoadGlobalCodecs(); #endif { CThreadCrc t; { UStringVector methods; methods.Add(methodName); RINOK(t.Hash.SetMethods(EXTERNAL_CODECS_VARS_G methods)); } FOR_VECTOR (i, indices) t.Enumerator.FilePaths.Add(us2fs(srcPanel.GetItemRelPath(indices[i]))); UString basePrefix = srcPanel.GetFsPath(); UString basePrefix2 = basePrefix; if (basePrefix2.Back() == ':') { int pos = basePrefix2.ReverseFind_PathSepar(); if (pos >= 0) basePrefix2.DeleteFrom(pos + 1); } t.Enumerator.BasePrefix = us2fs(basePrefix); t.Enumerator.BasePrefix_for_Open = us2fs(basePrefix2); t.Enumerator.EnterToDirs = !GetFlatMode(); t.ProgressDialog.ShowCompressionInfo = false; UString title = LangString(IDS_CHECKSUM_CALCULATING); t.ProgressDialog.MainWindow = _window; t.ProgressDialog.MainTitle = "7-Zip"; // LangString(IDS_APP_TITLE); t.ProgressDialog.MainAddTitle = title; t.ProgressDialog.MainAddTitle.Add_Space(); RINOK(t.Create(title, _window)); } RefreshTitleAlways(); return S_OK; } void CApp::CalculateCrc(const char *methodName) { HRESULT res = CalculateCrc2(UString(methodName)); if (res != S_OK && res != E_ABORT) { unsigned srcPanelIndex = GetFocusedPanelIndex(); CPanel &srcPanel = Panels[srcPanelIndex]; srcPanel.MessageBoxError(res); } }