summaryrefslogtreecommitdiff
path: root/utils/xbox/vxconsole/exclude_paths.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 /utils/xbox/vxconsole/exclude_paths.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'utils/xbox/vxconsole/exclude_paths.cpp')
-rw-r--r--utils/xbox/vxconsole/exclude_paths.cpp1134
1 files changed, 1134 insertions, 0 deletions
diff --git a/utils/xbox/vxconsole/exclude_paths.cpp b/utils/xbox/vxconsole/exclude_paths.cpp
new file mode 100644
index 0000000..668a835
--- /dev/null
+++ b/utils/xbox/vxconsole/exclude_paths.cpp
@@ -0,0 +1,1134 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// EXCLUDE_PATHS.CPP
+//
+// DVD Exclude paths.
+//=====================================================================================//
+#include "vxconsole.h"
+
+#define UM_CHECKSTATECHANGE (WM_USER + 100)
+#define UM_FIRSTTIMEPOPULATE (WM_USER + 101)
+
+#define MAX_TREE_DEPTH 32
+#define ROOT_NAME "Xbox:\\"
+#define EXCLUDEPATHS_FILE "xbox_exclude_paths.txt"
+
+// known shipping 360 gamedirs
+struct GameName_t
+{
+ const char *pName;
+ bool bIsModPath;
+};
+
+struct GamePath_t
+{
+ CUtlString pathName;
+ bool bIsModPath;
+};
+
+GameName_t g_GameNames[] =
+{
+ { "bin", false },
+ { "platform", false },
+ { "tf", true },
+ { "portal", true },
+ { "hl2", true },
+ { "episodic", true },
+ { "ep2", true }
+};
+
+CUtlVector< CUtlString > g_ExcludePaths;
+BOOL g_bLinkGameDirs;
+
+inline bool PathLessThan( GamePath_t const &lhs, GamePath_t const &rhs )
+{
+ if ( lhs.bIsModPath != rhs.bIsModPath )
+ {
+ // sort mod paths to the bottom
+ return ( lhs.bIsModPath == false );
+ }
+
+ return ( stricmp( lhs.pathName.String(), rhs.pathName.String() ) < 0 );
+}
+CUtlRBTree< GamePath_t > g_PathTable( 0, 0, PathLessThan );
+
+//-----------------------------------------------------------------------------
+// Add string to TreeView at specified level. Assumes an inorder insert.
+// A node (at specified depth) must be inserted before it's children.
+//-----------------------------------------------------------------------------
+static HTREEITEM AddItemToTree( HWND hWndTree, LPSTR lpszItem, int nLevel, bool bIsModPath )
+{
+ TVITEM tvi = { 0 };
+ TVINSERTSTRUCT tvins = { 0 };
+ static HTREEITEM s_hPrevItems[MAX_TREE_DEPTH];
+
+ if ( nLevel < 0 || nLevel >= MAX_TREE_DEPTH )
+ {
+ Assert( 0 );
+ return NULL;
+ }
+
+ HTREEITEM hParent;
+ HTREEITEM hPrev;
+ if ( !nLevel )
+ {
+ // one root item only, reset
+ for ( int i = 0; i < ARRAYSIZE( s_hPrevItems ); i++ )
+ {
+ s_hPrevItems[i] = NULL;
+ }
+ hParent = TVI_ROOT;
+ hPrev = (HTREEITEM)TVI_FIRST;
+ }
+ else
+ {
+ hParent = s_hPrevItems[nLevel-1];
+ hPrev = s_hPrevItems[nLevel];
+ if ( !hParent )
+ {
+ // parent should have been setup
+ Assert( 0 );
+ return NULL;
+ }
+ if ( !hPrev )
+ {
+ hPrev = TVI_FIRST;
+ }
+ }
+
+ tvi.mask = TVIF_TEXT | TVIF_PARAM;
+ tvi.pszText = lpszItem;
+ tvi.cchTextMax = sizeof(tvi.pszText)/sizeof(tvi.pszText[0]);
+ tvi.lParam = (LPARAM)MAKELONG( nLevel, bIsModPath );
+
+ tvins.item = tvi;
+ tvins.hParent = hParent;
+ tvins.hInsertAfter = hPrev;
+
+ // Add the item to the tree-view control.
+ hPrev = TreeView_InsertItem( hWndTree, &tvins );
+ s_hPrevItems[nLevel] = hPrev;
+
+ if ( nLevel > 0 )
+ {
+ // set a back link to its parent
+ tvi.mask = TVIF_HANDLE;
+ tvi.hItem = TreeView_GetParent( hWndTree, hPrev );
+ TreeView_SetItem( hWndTree, &tvi );
+ }
+
+ return hPrev;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get list of files from current path that match pattern
+//-----------------------------------------------------------------------------
+static int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< CUtlString > &fileList )
+{
+ char sourcePath[MAX_PATH];
+ char fullPath[MAX_PATH];
+ bool bFindDirs;
+
+ fileList.Purge();
+
+ strcpy( sourcePath, pDirPath );
+ int len = (int)strlen( sourcePath );
+ if ( !len )
+ {
+ strcpy( sourcePath, ".\\" );
+ }
+ else if ( sourcePath[len-1] != '\\' )
+ {
+ sourcePath[len] = '\\';
+ sourcePath[len+1] = '\0';
+ }
+
+ strcpy( fullPath, sourcePath );
+ if ( pPattern[0] == '\\' && pPattern[1] == '\0' )
+ {
+ // find directories only
+ bFindDirs = true;
+ strcat( fullPath, "*" );
+ }
+ else
+ {
+ // find files, use provided pattern
+ bFindDirs = false;
+ strcat( fullPath, pPattern );
+ }
+
+ struct _finddata_t findData;
+ intptr_t h = _findfirst( fullPath, &findData );
+ if ( h == -1 )
+ {
+ return 0;
+ }
+
+ do
+ {
+ // dos attribute complexities i.e. _A_NORMAL is 0
+ if ( bFindDirs )
+ {
+ // skip non dirs
+ if ( !( findData.attrib & _A_SUBDIR ) )
+ continue;
+ }
+ else
+ {
+ // skip dirs
+ if ( findData.attrib & _A_SUBDIR )
+ continue;
+ }
+
+ if ( !stricmp( findData.name, "." ) )
+ continue;
+
+ if ( !stricmp( findData.name, ".." ) )
+ continue;
+
+ char fileName[MAX_PATH];
+ strcpy( fileName, sourcePath );
+ strcat( fileName, findData.name );
+
+ int j = fileList.AddToTail();
+ fileList[j].Set( fileName );
+ }
+ while ( !_findnext( h, &findData ) );
+
+ _findclose( h );
+
+ return fileList.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively determine directory tree
+//-----------------------------------------------------------------------------
+static void RecurseFileTree_r( const char *pBasePath, const char *pDirPath, int depth, CUtlVector< CUtlString > &dirList, bool bIsModPath )
+{
+ if ( depth >= 2 )
+ {
+ // too much unecessary detail
+ return;
+ }
+
+ // ignore path roots, only interested in subdirs
+ const char *pSubName = pDirPath + strlen( pBasePath );
+ if ( pSubName[0] )
+ {
+ GamePath_t gamePath;
+ gamePath.pathName = pSubName;
+ gamePath.bIsModPath = bIsModPath;
+
+ int iIndex = g_PathTable.Find( gamePath );
+ if ( iIndex == g_PathTable.InvalidIndex() )
+ {
+ g_PathTable.Insert( gamePath );
+ }
+ }
+
+ // recurse from source directory, get directories only
+ CUtlVector< CUtlString > fileList;
+ int dirCount = GetFileList( pDirPath, "\\", fileList );
+ if ( !dirCount )
+ {
+ // add directory name to search tree
+ int j = dirList.AddToTail();
+ dirList[j].Set( pDirPath );
+ return;
+ }
+
+ for ( int i=0; i<dirCount; i++ )
+ {
+ // form new path name, recurse into
+ RecurseFileTree_r( pBasePath, fileList[i].String(), depth+1, dirList, bIsModPath );
+ }
+
+ int j = dirList.AddToTail();
+ dirList[j].Set( pDirPath );
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_SetCheckState_r
+//
+// Propogate the check state to all children
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_SetCheckState_r( HWND hWndTree, HTREEITEM hTree, int depth, int checkState )
+{
+ if ( !hTree )
+ {
+ return;
+ }
+
+ TreeView_SetCheckState( hWndTree, hTree, ( checkState == 1 ) );
+
+ TVITEM tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
+ tvi.hItem = hTree;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ ExcludePathsDlg_SetCheckState_r( hWndTree, hChild, depth+1, checkState );
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return;
+ }
+
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ return;
+ }
+
+ TreeView_SetCheckState( hWndTree, hSibling, ( checkState == 1 ) );
+
+ tvi.hItem = hSibling;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ ExcludePathsDlg_SetCheckState_r( hWndTree, hChild, depth+1, checkState );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_SetCheckStateLinked_r
+//
+// Propogate the check state to all "mod path" children that match the specified name.
+// A NULL name matches all.
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_SetCheckStateLinked_r( HWND hWndTree, HTREEITEM hTree, int depth, int checkState, const char *pName )
+{
+ if ( !hTree )
+ {
+ return;
+ }
+
+ char szNodeName[MAX_PATH];
+ TVITEM tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
+ tvi.hItem = hTree;
+ tvi.pszText = szNodeName;
+ tvi.cchTextMax = sizeof( szNodeName );
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ bool bIsModPath = HIWORD( tvi.lParam ) != 0;
+ if ( bIsModPath && ( !pName || !V_stricmp( szNodeName, pName ) ) )
+ {
+ TreeView_SetCheckState( hWndTree, hTree, ( checkState == 1 ) );
+ }
+
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ ExcludePathsDlg_SetCheckStateLinked_r( hWndTree, hChild, depth+1, checkState, pName );
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return;
+ }
+
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ return;
+ }
+
+ tvi.hItem = hSibling;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ bool bIsModPath = HIWORD( tvi.lParam ) != 0;
+ if ( bIsModPath && ( !pName || !V_stricmp( szNodeName, pName ) ) )
+ {
+ TreeView_SetCheckState( hWndTree, hSibling, ( checkState == 1 ) );
+ }
+
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ ExcludePathsDlg_SetCheckStateLinked_r( hWndTree, hChild, depth+1, checkState, pName );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_GetCheckState_r
+//
+// Caller invokes at node. Returns with state set.
+// State 0: unchecked, 1: checked, -1:unknown.
+// Returns true if All children match, false otherwise.
+//-----------------------------------------------------------------------------
+bool ExcludePathsDlg_GetCheckState_r( HWND hWndTree, HTREEITEM hTree, int depth, int *pCheckState )
+{
+ int checkState;
+
+ checkState = TreeView_GetCheckState( hWndTree, hTree );
+ if ( *pCheckState == -1 )
+ {
+ *pCheckState = checkState;
+ }
+ else if ( *pCheckState != checkState )
+ {
+ // disparate state, no need to recurse
+ return false;
+ }
+
+ TVITEM tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
+ tvi.hItem = hTree;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ if ( !ExcludePathsDlg_GetCheckState_r( hWndTree, hChild, depth+1, pCheckState ) )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return true;
+ }
+
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ break;
+ }
+
+ checkState = TreeView_GetCheckState( hWndTree, hSibling );
+ if ( *pCheckState != checkState )
+ {
+ // disparate state, no need to recurse
+ return false;
+ }
+
+ tvi.hItem = hSibling;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ if ( !ExcludePathsDlg_GetCheckState_r( hWndTree, hChild, depth+1, pCheckState ) )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Expand_r
+//
+// MaxDepth >= 0, Expand/Collapse up to specified depth.
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_Expand_r( HWND hWndTree, HTREEITEM hTree, int depth, int maxDepth, bool bExpand )
+{
+ if ( !hTree )
+ {
+ return;
+ }
+
+ if ( maxDepth >= 0 && depth >= maxDepth )
+ {
+ return;
+ }
+
+ int flags;
+ if ( bExpand )
+ flags = TVE_EXPAND;
+ else
+ flags = TVE_COLLAPSE;
+
+ TVITEM tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
+ tvi.hItem = hTree;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ if ( tvi.cChildren )
+ {
+ TreeView_Expand( hWndTree, hTree, flags );
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ ExcludePathsDlg_Expand_r( hWndTree, hChild, depth+1, maxDepth, bExpand );
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return;
+ }
+
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ return;
+ }
+
+ tvi.hItem = hSibling;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ if ( tvi.cChildren )
+ {
+ TreeView_Expand( hWndTree, hSibling, flags );
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ ExcludePathsDlg_Expand_r( hWndTree, hChild, depth+1, maxDepth, bExpand );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_ExpandToShowState_r
+//
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_ExpandToShowState_r( HWND hWndTree, HTREEITEM hTree, int depth )
+{
+ if ( !hTree )
+ {
+ return;
+ }
+
+ TVITEM tvi = { 0 };
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
+ tvi.hItem = hTree;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ int checkState = -1;
+ bool bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hTree, 0, &checkState );
+
+ if ( tvi.cChildren && !bStateIsSame )
+ {
+ TreeView_Expand( hWndTree, hTree, TVE_EXPAND );
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ ExcludePathsDlg_ExpandToShowState_r( hWndTree, hChild, depth+1 );
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return;
+ }
+
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ return;
+ }
+
+ tvi.hItem = hSibling;
+ if ( TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ int checkState = -1;
+ bool bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hSibling, 0, &checkState );
+
+ if ( tvi.cChildren && !bStateIsSame )
+ {
+ TreeView_Expand( hWndTree, hSibling, TVE_EXPAND );
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ ExcludePathsDlg_ExpandToShowState_r( hWndTree, hChild, depth+1 );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Find_r
+//
+//-----------------------------------------------------------------------------
+HTREEITEM ExcludePathsDlg_Find_r( HWND hWndTree, HTREEITEM hTree, int depth, const char *pBasePath, const char *pFindPath )
+{
+ TVITEM tvi = { 0 };
+ char szName[MAX_PATH];
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_TEXT;
+ tvi.hItem = hTree;
+ tvi.pszText = szName;
+ tvi.cchTextMax = sizeof( szName );
+ if ( !TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ return NULL;
+ }
+
+ char szPath[MAX_PATH];
+ V_ComposeFileName( pBasePath, szName, szPath, sizeof( szPath ) );
+ if ( !stricmp( szPath, pFindPath ) )
+ {
+ return hTree;
+ }
+
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ HTREEITEM hFindTree = ExcludePathsDlg_Find_r( hWndTree, hChild, depth+1, szPath, pFindPath );
+ if ( hFindTree )
+ {
+ return hFindTree;
+ }
+ }
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return NULL;
+ }
+
+ // iterate siblings
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ break;
+ }
+
+ tvi.hItem = hSibling;
+ if ( !TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ break;
+ }
+
+ V_ComposeFileName( pBasePath, szName, szPath, sizeof( szPath ) );
+ if ( !stricmp( szPath, pFindPath ) )
+ {
+ return hSibling;
+ }
+
+ if ( tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ HTREEITEM hFindTree = ExcludePathsDlg_Find_r( hWndTree, hChild, depth+1, szPath, pFindPath );
+ if ( hFindTree )
+ {
+ return hFindTree;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_BuildExcludeList_r
+//
+// An exclude path represents the path head, and thus does not need to iterate its children
+// if they are deemed to the same exclusion state.
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_BuildExcludeList_r( HWND hWndTree, HTREEITEM hTree, int depth, const char *pPath )
+{
+ int checkState = -1;
+ bool bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hTree, 0, &checkState );
+ if ( checkState == -1 )
+ {
+ return;
+ }
+
+ TVITEM tvi = { 0 };
+ char szName[MAX_PATH];
+ tvi.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_TEXT;
+ tvi.hItem = hTree;
+ tvi.pszText = szName;
+ tvi.cchTextMax = sizeof( szName );
+ if ( !TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ return;
+ }
+
+ char szPath[MAX_PATH];
+ V_ComposeFileName( pPath, szName, szPath, sizeof( szPath ) );
+
+ if ( checkState == 1 && ( bStateIsSame || !tvi.cChildren ) )
+ {
+ // add to exclude list
+ g_ExcludePaths.AddToTail( szPath );
+ }
+
+ if ( !bStateIsSame && tvi.cChildren )
+ {
+ // mixed states, must recurse to resolve
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
+ if ( hChild )
+ {
+ ExcludePathsDlg_BuildExcludeList_r( hWndTree, hChild, depth+1, szPath );
+ }
+ }
+
+ if ( !depth )
+ {
+ // only iterate siblings of the parent's child
+ return;
+ }
+
+ // iterate siblings
+ HTREEITEM hSibling = hTree;
+ while ( 1 )
+ {
+ hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
+ if ( !hSibling )
+ {
+ break;
+ }
+
+ checkState = -1;
+ bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hSibling, 0, &checkState );
+ if ( checkState == -1 )
+ {
+ break;
+ }
+
+ tvi.hItem = hSibling;
+ if ( !TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ break;
+ }
+
+ V_ComposeFileName( pPath, szName, szPath, sizeof( szPath ) );
+
+ if ( checkState == 1 && ( bStateIsSame || !tvi.cChildren ) )
+ {
+ // add to exclude list
+ g_ExcludePaths.AddToTail( szPath );
+ }
+
+ if ( !bStateIsSame && tvi.cChildren )
+ {
+ HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
+ if ( hChild )
+ {
+ ExcludePathsDlg_BuildExcludeList_r( hWndTree, hChild, depth+1, szPath );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_SaveChanges
+//
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_SaveChanges( HWND hWnd )
+{
+ g_ExcludePaths.Purge();
+ HWND hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
+ ExcludePathsDlg_BuildExcludeList_r( hWndTree, TreeView_GetRoot( hWndTree ), 0, "" );
+
+ char szPath[MAX_PATH];
+ V_ComposeFileName( g_localPath, EXCLUDEPATHS_FILE, szPath, sizeof( szPath ) );
+
+ if ( !g_ExcludePaths.Count() )
+ {
+ // no exclude paths
+ unlink( szPath );
+ }
+ else
+ {
+ FILE *fp = fopen( szPath, "wt" );
+ if ( !fp )
+ {
+ Sys_MessageBox( "Error", "Could not open '%s' for writing\n", szPath );
+ return;
+ }
+
+ fprintf( fp, "// Auto-Generated by VXConsole - DO NOT MODIFY!\n" );
+ for ( int i = 0; i < g_ExcludePaths.Count(); i++ )
+ {
+ // strip expected root path
+ const char *pPath = g_ExcludePaths[i].String();
+ pPath += strlen( ROOT_NAME );
+ if ( !pPath[0] )
+ {
+ // special code for root
+ fprintf( fp, "*\n" );
+ break;
+ }
+ fprintf( fp, "\"\\%s\"\n", pPath );
+ }
+ fclose( fp );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Populate
+//
+// Generate a path table.
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_Populate( HWND hWnd, bool bRefresh )
+{
+ struct GamePath_t
+ {
+ CUtlString pathName;
+ bool bIsModPath;
+ };
+
+ CUtlVector< GamePath_t > gamePaths;
+
+ // can skip the time consuming directory scan, unless forced
+ if ( bRefresh || !g_PathTable.Count() )
+ {
+ g_PathTable.Purge();
+
+ for ( int i = 0; i < ARRAYSIZE( g_GameNames ); i++ )
+ {
+ char szTargetPath[MAX_PATH];
+ V_strncpy( szTargetPath, g_localPath, sizeof( szTargetPath ) );
+ V_AppendSlash( szTargetPath, sizeof( szTargetPath ) );
+ V_strncat( szTargetPath, g_GameNames[i].pName, sizeof( szTargetPath ) );
+
+ GamePath_t gamePath;
+ gamePath.pathName = szTargetPath;
+ gamePath.bIsModPath = g_GameNames[i].bIsModPath;
+ gamePaths.AddToTail( gamePath );
+ }
+
+ // iterate all game paths
+ for ( int i = 0; i < gamePaths.Count(); i++ )
+ {
+ CUtlVector< CUtlString > dirList;
+ RecurseFileTree_r( g_localPath, gamePaths[i].pathName.String(), 0, dirList, gamePaths[i].bIsModPath );
+ }
+ }
+
+ // reset the tree
+ HWND hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
+ TreeView_DeleteAllItems( hWndTree );
+
+ // reset and add root
+ // only the root is at depth 0
+ AddItemToTree( hWndTree, ROOT_NAME, 0, false );
+
+ // build the tree view
+ for ( int iIndex = g_PathTable.FirstInorder(); iIndex != g_PathTable.InvalidIndex(); iIndex = g_PathTable.NextInorder( iIndex ) )
+ {
+ // due to sorting, number of slashes is depth
+ const char *pString = g_PathTable[iIndex].pathName.String();
+ int depth = 0;
+ for ( int j = 0; j < (int)strlen( pString ); j++ )
+ {
+ if ( pString[j] == '\\' )
+ {
+ depth++;
+ }
+ }
+ if ( !depth )
+ {
+ depth = 1;
+ }
+
+ char szPath[MAX_PATH];
+ V_FileBase( pString, szPath, sizeof( szPath ) );
+ AddItemToTree( hWndTree, szPath, depth, g_PathTable[iIndex].bIsModPath );
+ }
+
+ HTREEITEM hTreeRoot = TreeView_GetRoot( hWndTree );
+ for ( int i = 0; i < g_ExcludePaths.Count(); i++ )
+ {
+ HTREEITEM hTree = ExcludePathsDlg_Find_r( hWndTree, hTreeRoot, 0, "", g_ExcludePaths[i].String() );
+ if ( hTree )
+ {
+ ExcludePathsDlg_SetCheckState_r( hWndTree, hTree, 0, true );
+ }
+ }
+
+ // expand the root node to show state
+ ExcludePathsDlg_ExpandToShowState_r( hWndTree, hTreeRoot, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Setup
+//
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_Setup( HWND hWnd )
+{
+ TreeView_SetBkColor( GetDlgItem( hWnd, IDC_PATHS_TREE ), g_backgroundColor );
+
+ CheckDlgButton( hWnd, IDC_PATHS_LINKGAMEDIRS, g_bLinkGameDirs ? BST_CHECKED : BST_UNCHECKED );
+
+ // read the exisiting exclude paths
+ g_ExcludePaths.Purge();
+ char szFilename[MAX_PATH];
+ V_ComposeFileName( g_localPath, EXCLUDEPATHS_FILE, szFilename, sizeof( szFilename ) );
+ if ( Sys_Exists( szFilename ) )
+ {
+ Sys_LoadScriptFile( szFilename );
+ while ( 1 )
+ {
+ char *pToken = Sys_GetToken( true );
+ if ( !pToken || !pToken[0] )
+ {
+ break;
+ }
+ Sys_StripQuotesFromToken( pToken );
+ if ( !stricmp( pToken, "*" ) )
+ {
+ pToken = "";
+ }
+ else if ( pToken[0] == '\\' )
+ {
+ pToken++;
+ }
+
+ char szPath[MAX_PATH];
+ V_ComposeFileName( ROOT_NAME, pToken, szPath, sizeof( szPath ) );
+ V_FixSlashes( szPath );
+
+ g_ExcludePaths.AddToTail( szPath );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Proc
+//
+//-----------------------------------------------------------------------------
+BOOL CALLBACK ExcludePathsDlg_Proc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+ HWND hWndTree;
+ LPNMHDR lpnmh;
+ HTREEITEM hTree;
+ HTREEITEM hTreeRoot;
+ int checkState;
+ static bool s_bAllowPopulate = 0;
+
+ switch ( message )
+ {
+ case WM_INITDIALOG:
+ ExcludePathsDlg_Setup( hWnd );
+ s_bAllowPopulate = true;
+ return TRUE;
+
+ case WM_NOTIFY:
+ lpnmh = (LPNMHDR)lParam;
+ if ( ( lpnmh->code == NM_CLICK ) && ( lpnmh->idFrom == IDC_PATHS_TREE ) )
+ {
+ TVHITTESTINFO ht = {0};
+ DWORD dwpos = GetMessagePos();
+ ht.pt.x = GET_X_LPARAM( dwpos );
+ ht.pt.y = GET_Y_LPARAM( dwpos );
+ MapWindowPoints( HWND_DESKTOP, lpnmh->hwndFrom, &ht.pt, 1 );
+ TreeView_HitTest( lpnmh->hwndFrom, &ht );
+ if ( ht.flags & TVHT_ONITEMSTATEICON )
+ {
+ PostMessage( hWnd, UM_CHECKSTATECHANGE, 0, (LPARAM)ht.hItem );
+ }
+ }
+ break;
+
+ case UM_CHECKSTATECHANGE:
+ hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
+ hTreeRoot = TreeView_GetRoot( hWndTree );
+ hTree = (HTREEITEM)lParam;
+ checkState = TreeView_GetCheckState( hWndTree, hTree );
+ if ( checkState != -1 )
+ {
+ TVITEM tvi = { 0 };
+ char szName[MAX_PATH];
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
+ tvi.hItem = hTree;
+ tvi.pszText = szName;
+ tvi.cchTextMax = sizeof( szName );
+ if ( !TreeView_GetItem( hWndTree, &tvi ) )
+ {
+ break;
+ }
+ int nDepth = LOWORD( tvi.lParam );
+ bool bIsModPath = HIWORD( tvi.lParam ) != 0;
+
+ if ( g_bLinkGameDirs && bIsModPath )
+ {
+ // a mod path root is at depth 1
+ // a child of a mod path (depth > 1), will match all other children in mod paths
+ // a mod path (depth = 1) will match all other modpaths
+ ExcludePathsDlg_SetCheckStateLinked_r( hWndTree, hTreeRoot, 0, checkState, nDepth == 1 ? NULL : szName );
+ }
+ else
+ {
+ ExcludePathsDlg_SetCheckState_r( hWndTree, hTree, 0, checkState );
+ }
+ }
+ break;
+
+ case WM_PAINT:
+ if ( s_bAllowPopulate )
+ {
+ // unfortunate, but tree view needs to paint first before its state can be set
+ // related to checkbox style which doesn't manifest until after WM_INITDIALOG
+ // stall the initial population until after the first paint
+ s_bAllowPopulate = false;
+ PostMessage( hWnd, UM_FIRSTTIMEPOPULATE, 0, 0 );
+ }
+ break;
+
+ case UM_FIRSTTIMEPOPULATE:
+ ExcludePathsDlg_Populate( hWnd, false );
+ break;
+
+ case WM_COMMAND:
+ switch ( LOWORD( wParam ) )
+ {
+ case IDC_OK:
+ ExcludePathsDlg_SaveChanges( hWnd );
+ EndDialog( hWnd, wParam );
+ return TRUE;
+
+ case IDC_PATHS_RESCAN:
+ ExcludePathsDlg_Populate( hWnd, true );
+ return TRUE;
+
+ case IDC_PATHS_EXPAND:
+ // expand from root
+ hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
+ ExcludePathsDlg_Expand_r( hWndTree, TreeView_GetRoot( hWndTree ), 0, -1, true );
+ return TRUE;
+
+ case IDC_PATHS_COLLAPSE:
+ // collapse from root
+ hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
+ ExcludePathsDlg_Expand_r( hWndTree, TreeView_GetRoot( hWndTree ), 0, -1, false );
+ return TRUE;
+
+ case IDC_PATHS_LINKGAMEDIRS:
+ g_bLinkGameDirs = IsDlgButtonChecked( hWnd, IDC_PATHS_LINKGAMEDIRS );
+ return TRUE;
+
+ case IDCANCEL:
+ case IDC_CANCEL:
+ EndDialog( hWnd, wParam );
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_LoadConfig
+//
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_LoadConfig()
+{
+ // get our config
+ Sys_GetRegistryInteger( "ExcludePaths_LinkGameDirs", true, g_bLinkGameDirs );
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_SaveConfig
+//
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_SaveConfig()
+{
+ Sys_SetRegistryInteger( "ExcludePaths_LinkGameDirs", g_bLinkGameDirs );
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Init
+//
+//-----------------------------------------------------------------------------
+bool ExcludePathsDlg_Init()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// ExcludePathsDlg_Open
+//
+//-----------------------------------------------------------------------------
+void ExcludePathsDlg_Open()
+{
+ ExcludePathsDlg_LoadConfig();
+
+ int result = DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_EXCLUDEPATHS ), g_hDlgMain, ( DLGPROC )ExcludePathsDlg_Proc );
+ if ( LOWORD( result ) != IDC_OK )
+ return;
+
+ ExcludePathsDlg_SaveConfig();
+}