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.
1135 lines
27 KiB
1135 lines
27 KiB
![]()
4 years ago
|
//========= 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();
|
||
|
}
|