387 lines
8.5 KiB
C++
387 lines
8.5 KiB
C++
|
// 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<NFind::CEnumerator> 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);
|
||
|
s += FCHAR_ANY_MASK;
|
||
|
Enumerators.Add(NFind::CEnumerator(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<UInt32> 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 = L"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 UString &methodName)
|
||
|
{
|
||
|
HRESULT res = CalculateCrc2(methodName);
|
||
|
if (res != S_OK && res != E_ABORT)
|
||
|
{
|
||
|
unsigned srcPanelIndex = GetFocusedPanelIndex();
|
||
|
CPanel &srcPanel = Panels[srcPanelIndex];
|
||
|
srcPanel.MessageBoxError(res);
|
||
|
}
|
||
|
}
|