Files
FC1/Editor/Util/FileChangeMonitor.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

299 lines
7.7 KiB
C++

////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: filechangemonitor.cpp
// Version: v1.00
// Created: 15/11/2002 by Timur.
// Compilers: Visual Studio.NET
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "FileChangeMonitor.h"
#include "Thread.h"
#include "Util\FileUtil.h"
#include <sys/stat.h>
//////////////////////////////////////////////////////////////////////////
// Directory monitoring thread.
//////////////////////////////////////////////////////////////////////////
struct CFileChangeMonitorThread : public CThread
{
public:
std::vector<HANDLE> m_handles;
std::vector<CString> m_dirs;
MTQueue<CString> m_files;
HANDLE m_killEvent;
DWORD m_mainThreadId;
CFileChangeMonitorThread()
{
m_mainThreadId = GetCurrentThreadId();
m_killEvent = ::CreateEvent( NULL,TRUE,FALSE,NULL );
// First event in list is KillEvent.
m_handles.push_back(m_killEvent);
m_dirs.push_back("");
}
~CFileChangeMonitorThread()
{
::CloseHandle( m_killEvent );
m_killEvent = 0;
}
protected:
void FindChangedFiles( const CString &dir );
void Run()
{
DWORD dwWaitStatus;
int numHandles = m_handles.size();
// If First handle triggers, its quit.
// Waiting thread.
while (TRUE)
{
// Wait for notification.
dwWaitStatus = WaitForMultipleObjects( numHandles,&m_handles[0],FALSE,INFINITE );
if (dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus < WAIT_OBJECT_0+numHandles)
{
if (dwWaitStatus == WAIT_OBJECT_0)
{
// This is Thread Kill event.
break;
}
// One of objects got triggered, find out which.
int id = dwWaitStatus - WAIT_OBJECT_0;
CString dir = m_dirs[id];
FindChangedFiles(dir);
// Now the intesting part.. we need to find which file have been changed.
// The fastest way, is scan directoryto take current time
if (FindNextChangeNotification(m_handles[id]) == FALSE)
{
// Error!!!.
//ExitProcess(GetLastError());
}
// Notify main thread that something have changed.
PostThreadMessage( m_mainThreadId,WM_FILEMONITORCHANGE,0,0 );
}
}
// Close all change notification handles.
for (int i = 1; i < m_handles.size(); i++)
{
FindCloseChangeNotification(m_handles[i]);
}
}
};
//////////////////////////////////////////////////////////////////////////
inline void UnixTimeToFileTime(time_t t, LPFILETIME pft)
{
// Note that LONGLONG is a 64-bit value
LONGLONG ll;
ll = Int32x32To64(t, 10000000) + 116444736000000000;
pft->dwLowDateTime = (DWORD)ll;
pft->dwHighDateTime = ll >> 32;
}
//////////////////////////////////////////////////////////////////////////
inline bool ScanDirectoryFiles( const CString &root,const CString &path,const CString &fileSpec,std::vector<CFileChangeMonitor::SFileEnumInfo> &files )
{
bool anyFound = false;
CString dir = Path::AddBackslash(root + path);
CString findFilter = Path::Make(dir,fileSpec);
ICryPak *pIPak = GetIEditor()->GetSystem()->GetIPak();
// Add all directories.
CFileFind finder;
BOOL bWorking = finder.FindFile( Path::Make(dir,fileSpec) );
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDots())
continue;
if (!finder.IsDirectory())
{
anyFound = true;
CFileChangeMonitor::SFileEnumInfo fd;
fd.filename = dir + finder.GetFileName();
//fd.nFileSize = finder.GetLength();
//finder.GetCreationTime( &fd.ftCreationTime );
//finder.GetLastAccessTime( &fd.ftLastAccessTime );
finder.GetLastWriteTime( &fd.ftLastWriteTime );
files.push_back(fd);
}
}
return anyFound;
}
//////////////////////////////////////////////////////////////////////////
// Get directory contents.
//////////////////////////////////////////////////////////////////////////
inline bool ScanDirectoryRecursive( const CString &root,const CString &path,const CString &fileSpec,std::vector<CFileChangeMonitor::SFileEnumInfo> &files, bool recursive )
{
bool anyFound = false;
CString dir = Path::AddBackslash(root + path);
if (ScanDirectoryFiles( root,path,fileSpec,files ))
anyFound = true;
if (recursive)
{
CFileFind finder;
BOOL bWorking = finder.FindFile( Path::Make(dir,"*.*") );
while (bWorking)
{
bWorking = finder.FindNextFile();
if (finder.IsDots())
continue;
if (finder.IsDirectory())
{
// Scan directory.
if (ScanDirectoryRecursive( root,Path::AddBackslash(path+finder.GetFileName()),fileSpec,files,recursive ))
anyFound = true;
}
}
}
return anyFound;
}
//////////////////////////////////////////////////////////////////////////
void CFileChangeMonitorThread::FindChangedFiles( const CString &dir )
{
//CLogFile::WriteLine( "** Searching file" );
// Get current file time.
SYSTEMTIME curSysTime;
FILETIME curFileTime;
GetSystemTime( &curSysTime );
SystemTimeToFileTime( &curSysTime,&curFileTime );
std::vector<CFileChangeMonitor::SFileEnumInfo> files;
files.reserve( 1000 );
ScanDirectoryRecursive( dir,"","*.*",files,true );
INT64 curftime = *(INT64*)&curFileTime;
SYSTEMTIME s1,s2;
FILETIME ft1,ft2;
ZeroStruct(ft1);
FileTimeToSystemTime(&ft1,&s1);
FileTimeToSystemTime(&ft1,&s2);
s2.wSecond = 5; // 5 second.
SystemTimeToFileTime(&s1,&ft1);
SystemTimeToFileTime(&s2,&ft2);
int deltaT = (*(INT64*)&ft2) - (*(INT64*)&ft1) ; // 1 second.
for (int i = 0; i < files.size(); i++)
{
// now compare time stamps.
INT64 ftime = *(INT64*)&(files[i].ftLastWriteTime);
INT64 dt = curftime-ftime;
if (dt < 0)
{
dt = ftime - curftime;
}
if (dt < deltaT)
{
// This file was written within deltaT time from now, consider it as modified.
CString filename = files[i].filename;
m_files.push( filename );
}
}
}
//////////////////////////////////////////////////////////////////////////
CFileChangeMonitor::CFileChangeMonitor()
{
m_thread = new CFileChangeMonitorThread;
}
//////////////////////////////////////////////////////////////////////////
CFileChangeMonitor::~CFileChangeMonitor()
{
// Send to thread a kill event.
StopMonitor();
}
//////////////////////////////////////////////////////////////////////////
bool CFileChangeMonitor::IsDirectory(const char* sFileName)
{
struct __stat64 my_stat;
if (_stat64(sFileName, &my_stat) != 0) return false;
return ((my_stat.st_mode & S_IFDIR) != 0);
}
//////////////////////////////////////////////////////////////////////////
void CFileChangeMonitor::MonitorDirectories( const std::vector<CString> &dirs )
{
// Watch the C:\WINDOWS directory for file creation and
// deletion.
for (int i = 0; i < dirs.size(); i++)
{
if (!IsDirectory( Path::RemoveBackslash(dirs[i]) ))
continue;
m_thread->m_dirs.push_back(dirs[i]);
// Create Notification Event.
HANDLE dwHandle = FindFirstChangeNotification( dirs[i],TRUE,FILE_NOTIFY_CHANGE_LAST_WRITE); // watch file name changes
m_thread->m_handles.push_back(dwHandle);
}
// Start monitoring thread.
m_thread->Start();
}
//////////////////////////////////////////////////////////////////////////
void CFileChangeMonitor::StopMonitor()
{
if (m_thread)
{
::SetEvent(m_thread->m_killEvent);
}
}
//////////////////////////////////////////////////////////////////////////
bool CFileChangeMonitor::HaveModifiedFiles() const
{
return !m_thread->m_files.empty();
}
//////////////////////////////////////////////////////////////////////////
CString CFileChangeMonitor::GetModifiedFile()
{
CString file;
if (!m_thread->m_files.empty())
{
file = m_thread->m_files.top();
m_thread->m_files.pop();
}
return file;
}