You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1134 lines
27 KiB
1134 lines
27 KiB
//========= 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(); |
|
}
|
|
|