summaryrefslogtreecommitdiff
path: root/hammer/FileChangeWatcher.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/FileChangeWatcher.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/FileChangeWatcher.cpp')
-rw-r--r--hammer/FileChangeWatcher.cpp160
1 files changed, 160 insertions, 0 deletions
diff --git a/hammer/FileChangeWatcher.cpp b/hammer/FileChangeWatcher.cpp
new file mode 100644
index 0000000..faff243
--- /dev/null
+++ b/hammer/FileChangeWatcher.cpp
@@ -0,0 +1,160 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "stdafx.h"
+#include "FileChangeWatcher.h"
+#include "tier1/utldict.h"
+#include "filesystem_tools.h"
+
+
+CFileChangeWatcher::CFileChangeWatcher()
+{
+ m_pCallbacks = NULL;
+}
+
+CFileChangeWatcher::~CFileChangeWatcher()
+{
+ Term();
+}
+
+void CFileChangeWatcher::Init( ICallbacks *pCallbacks )
+{
+ Term();
+ m_pCallbacks = pCallbacks;
+}
+
+bool CFileChangeWatcher::AddDirectory( const char *pSearchPathBase, const char *pDirName, bool bRecursive )
+{
+ char fullDirName[MAX_PATH];
+ V_ComposeFileName( pSearchPathBase, pDirName, fullDirName, sizeof( fullDirName ) );
+
+ HANDLE hDir = CreateFile( fullDirName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL );
+ if ( hDir == INVALID_HANDLE_VALUE )
+ {
+ Warning( "CFileChangeWatcher::AddDirectory - can't get a handle to directory %s.\n", pDirName );
+ return false;
+ }
+
+ // Call this once to start the ball rolling.. Next time we call it, it'll tell us the changes that
+ // have happened since this call.
+ CDirWatch *pDirWatch = new CDirWatch;
+ V_strncpy( pDirWatch->m_SearchPathBase, pSearchPathBase, sizeof( pDirWatch->m_SearchPathBase ) );
+ V_strncpy( pDirWatch->m_DirName, pDirName, sizeof( pDirWatch->m_DirName ) );
+ V_strncpy( pDirWatch->m_FullDirName, fullDirName, sizeof( pDirWatch->m_FullDirName ) );
+ pDirWatch->m_hDir = hDir;
+ pDirWatch->m_hEvent = CreateEvent( NULL, false, false, NULL );
+ memset( &pDirWatch->m_Overlapped, 0, sizeof( pDirWatch->m_Overlapped ) );
+ pDirWatch->m_Overlapped.hEvent = pDirWatch->m_hEvent;
+ if ( !CallReadDirectoryChanges( pDirWatch ) )
+ {
+ CloseHandle( pDirWatch->m_hEvent );
+ CloseHandle( pDirWatch->m_hDir );
+ delete pDirWatch;
+ return false;
+ }
+
+ m_DirWatches.AddToTail( pDirWatch );
+ return true;
+}
+
+void CFileChangeWatcher::Term()
+{
+ for ( int i=0; i < m_DirWatches.Count(); i++ )
+ {
+ CloseHandle( m_DirWatches[i]->m_hDir );
+ CloseHandle( m_DirWatches[i]->m_hEvent );
+ }
+ m_DirWatches.PurgeAndDeleteElements();
+ m_pCallbacks = NULL;
+}
+
+int CFileChangeWatcher::Update()
+{
+ CUtlDict< int, int > queuedChanges;
+ int nTotalChanges = 0;
+
+ // Check each CDirWatch.
+ int i = 0;
+ while ( i < m_DirWatches.Count() )
+ {
+ CDirWatch *pDirWatch = m_DirWatches[i];
+
+ DWORD dwBytes = 0;
+ if ( GetOverlappedResult( pDirWatch->m_hDir, &pDirWatch->m_Overlapped, &dwBytes, FALSE ) )
+ {
+ // Read through the notifications.
+ int nBytesLeft = (int)dwBytes;
+ char *pCurPos = pDirWatch->m_Buffer;
+ while ( nBytesLeft >= sizeof( FILE_NOTIFY_INFORMATION ) )
+ {
+ FILE_NOTIFY_INFORMATION *pNotify = (FILE_NOTIFY_INFORMATION*)pCurPos;
+
+ if ( m_pCallbacks )
+ {
+ // Figure out what happened to this file.
+ WCHAR nullTerminated[2048];
+ int nBytesToCopy = min( (int)pNotify->FileNameLength, 2047 );
+ memcpy( nullTerminated, pNotify->FileName, nBytesToCopy );
+ nullTerminated[nBytesToCopy/2] = 0;
+ char ansiFilename[1024];
+ V_UnicodeToUTF8( nullTerminated, ansiFilename, sizeof( ansiFilename ) );
+
+ // Now add it to the queue. We use this queue because sometimes Windows will give us multiple
+ // of the same modified notification back to back, and we want to reduce redundant calls.
+ int iExisting = queuedChanges.Find( ansiFilename );
+ if ( iExisting == queuedChanges.InvalidIndex() )
+ {
+ iExisting = queuedChanges.Insert( ansiFilename, 0 );
+ ++nTotalChanges;
+ }
+ }
+
+ if ( pNotify->NextEntryOffset == 0 )
+ break;
+
+ pCurPos += pNotify->NextEntryOffset;
+ nBytesLeft -= (int)pNotify->NextEntryOffset;
+ }
+
+ CallReadDirectoryChanges( pDirWatch );
+ continue; // Check again because sometimes it queues up duplicate notifications.
+ }
+
+ // Process all the entries in the queue.
+ for ( int iQueuedChange=queuedChanges.First(); iQueuedChange != queuedChanges.InvalidIndex(); iQueuedChange=queuedChanges.Next( iQueuedChange ) )
+ {
+ SendNotification( pDirWatch, queuedChanges.GetElementName( iQueuedChange ) );
+ }
+ queuedChanges.Purge();
+ ++i;
+ }
+
+ return nTotalChanges;
+}
+
+void CFileChangeWatcher::SendNotification( CFileChangeWatcher::CDirWatch *pDirWatch, const char *pRelativeFilename )
+{
+ // Use this for full filenames although you don't strictly need it..
+ char fullFilename[MAX_PATH];
+ V_ComposeFileName( pDirWatch->m_FullDirName, pRelativeFilename, fullFilename, sizeof( fullFilename ) );
+
+ m_pCallbacks->OnFileChange( pRelativeFilename, fullFilename );
+}
+
+BOOL CFileChangeWatcher::CallReadDirectoryChanges( CFileChangeWatcher::CDirWatch *pDirWatch )
+{
+ return ReadDirectoryChangesW( pDirWatch->m_hDir,
+ pDirWatch->m_Buffer,
+ sizeof( pDirWatch->m_Buffer ),
+ true,
+ FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
+ NULL,
+ &pDirWatch->m_Overlapped,
+ NULL );
+}
+
+
+