Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // EXCLUDE_PATHS.CPP
  4. //
  5. // DVD Exclude paths.
  6. //=====================================================================================//
  7. #include "vxconsole.h"
  8. #define UM_CHECKSTATECHANGE (WM_USER + 100)
  9. #define UM_FIRSTTIMEPOPULATE (WM_USER + 101)
  10. #define MAX_TREE_DEPTH 32
  11. #define ROOT_NAME "Xbox:\\"
  12. #define EXCLUDEPATHS_FILE "xbox_exclude_paths.txt"
  13. // known shipping 360 gamedirs
  14. struct GameName_t
  15. {
  16. const char *pName;
  17. bool bIsModPath;
  18. };
  19. struct GamePath_t
  20. {
  21. CUtlString pathName;
  22. bool bIsModPath;
  23. };
  24. GameName_t g_GameNames[] =
  25. {
  26. { "bin", false },
  27. { "platform", false },
  28. { "tf", true },
  29. { "portal", true },
  30. { "hl2", true },
  31. { "episodic", true },
  32. { "ep2", true }
  33. };
  34. CUtlVector< CUtlString > g_ExcludePaths;
  35. BOOL g_bLinkGameDirs;
  36. inline bool PathLessThan( GamePath_t const &lhs, GamePath_t const &rhs )
  37. {
  38. if ( lhs.bIsModPath != rhs.bIsModPath )
  39. {
  40. // sort mod paths to the bottom
  41. return ( lhs.bIsModPath == false );
  42. }
  43. return ( stricmp( lhs.pathName.String(), rhs.pathName.String() ) < 0 );
  44. }
  45. CUtlRBTree< GamePath_t > g_PathTable( 0, 0, PathLessThan );
  46. //-----------------------------------------------------------------------------
  47. // Add string to TreeView at specified level. Assumes an inorder insert.
  48. // A node (at specified depth) must be inserted before it's children.
  49. //-----------------------------------------------------------------------------
  50. static HTREEITEM AddItemToTree( HWND hWndTree, LPSTR lpszItem, int nLevel, bool bIsModPath )
  51. {
  52. TVITEM tvi = { 0 };
  53. TVINSERTSTRUCT tvins = { 0 };
  54. static HTREEITEM s_hPrevItems[MAX_TREE_DEPTH];
  55. if ( nLevel < 0 || nLevel >= MAX_TREE_DEPTH )
  56. {
  57. Assert( 0 );
  58. return NULL;
  59. }
  60. HTREEITEM hParent;
  61. HTREEITEM hPrev;
  62. if ( !nLevel )
  63. {
  64. // one root item only, reset
  65. for ( int i = 0; i < ARRAYSIZE( s_hPrevItems ); i++ )
  66. {
  67. s_hPrevItems[i] = NULL;
  68. }
  69. hParent = TVI_ROOT;
  70. hPrev = (HTREEITEM)TVI_FIRST;
  71. }
  72. else
  73. {
  74. hParent = s_hPrevItems[nLevel-1];
  75. hPrev = s_hPrevItems[nLevel];
  76. if ( !hParent )
  77. {
  78. // parent should have been setup
  79. Assert( 0 );
  80. return NULL;
  81. }
  82. if ( !hPrev )
  83. {
  84. hPrev = TVI_FIRST;
  85. }
  86. }
  87. tvi.mask = TVIF_TEXT | TVIF_PARAM;
  88. tvi.pszText = lpszItem;
  89. tvi.cchTextMax = sizeof(tvi.pszText)/sizeof(tvi.pszText[0]);
  90. tvi.lParam = (LPARAM)MAKELONG( nLevel, bIsModPath );
  91. tvins.item = tvi;
  92. tvins.hParent = hParent;
  93. tvins.hInsertAfter = hPrev;
  94. // Add the item to the tree-view control.
  95. hPrev = TreeView_InsertItem( hWndTree, &tvins );
  96. s_hPrevItems[nLevel] = hPrev;
  97. if ( nLevel > 0 )
  98. {
  99. // set a back link to its parent
  100. tvi.mask = TVIF_HANDLE;
  101. tvi.hItem = TreeView_GetParent( hWndTree, hPrev );
  102. TreeView_SetItem( hWndTree, &tvi );
  103. }
  104. return hPrev;
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose: Get list of files from current path that match pattern
  108. //-----------------------------------------------------------------------------
  109. static int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< CUtlString > &fileList )
  110. {
  111. char sourcePath[MAX_PATH];
  112. char fullPath[MAX_PATH];
  113. bool bFindDirs;
  114. fileList.Purge();
  115. strcpy( sourcePath, pDirPath );
  116. int len = (int)strlen( sourcePath );
  117. if ( !len )
  118. {
  119. strcpy( sourcePath, ".\\" );
  120. }
  121. else if ( sourcePath[len-1] != '\\' )
  122. {
  123. sourcePath[len] = '\\';
  124. sourcePath[len+1] = '\0';
  125. }
  126. strcpy( fullPath, sourcePath );
  127. if ( pPattern[0] == '\\' && pPattern[1] == '\0' )
  128. {
  129. // find directories only
  130. bFindDirs = true;
  131. strcat( fullPath, "*" );
  132. }
  133. else
  134. {
  135. // find files, use provided pattern
  136. bFindDirs = false;
  137. strcat( fullPath, pPattern );
  138. }
  139. struct _finddata_t findData;
  140. intptr_t h = _findfirst( fullPath, &findData );
  141. if ( h == -1 )
  142. {
  143. return 0;
  144. }
  145. do
  146. {
  147. // dos attribute complexities i.e. _A_NORMAL is 0
  148. if ( bFindDirs )
  149. {
  150. // skip non dirs
  151. if ( !( findData.attrib & _A_SUBDIR ) )
  152. continue;
  153. }
  154. else
  155. {
  156. // skip dirs
  157. if ( findData.attrib & _A_SUBDIR )
  158. continue;
  159. }
  160. if ( !stricmp( findData.name, "." ) )
  161. continue;
  162. if ( !stricmp( findData.name, ".." ) )
  163. continue;
  164. char fileName[MAX_PATH];
  165. strcpy( fileName, sourcePath );
  166. strcat( fileName, findData.name );
  167. int j = fileList.AddToTail();
  168. fileList[j].Set( fileName );
  169. }
  170. while ( !_findnext( h, &findData ) );
  171. _findclose( h );
  172. return fileList.Count();
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose: Recursively determine directory tree
  176. //-----------------------------------------------------------------------------
  177. static void RecurseFileTree_r( const char *pBasePath, const char *pDirPath, int depth, CUtlVector< CUtlString > &dirList, bool bIsModPath )
  178. {
  179. if ( depth >= 2 )
  180. {
  181. // too much unecessary detail
  182. return;
  183. }
  184. // ignore path roots, only interested in subdirs
  185. const char *pSubName = pDirPath + strlen( pBasePath );
  186. if ( pSubName[0] )
  187. {
  188. GamePath_t gamePath;
  189. gamePath.pathName = pSubName;
  190. gamePath.bIsModPath = bIsModPath;
  191. int iIndex = g_PathTable.Find( gamePath );
  192. if ( iIndex == g_PathTable.InvalidIndex() )
  193. {
  194. g_PathTable.Insert( gamePath );
  195. }
  196. }
  197. // recurse from source directory, get directories only
  198. CUtlVector< CUtlString > fileList;
  199. int dirCount = GetFileList( pDirPath, "\\", fileList );
  200. if ( !dirCount )
  201. {
  202. // add directory name to search tree
  203. int j = dirList.AddToTail();
  204. dirList[j].Set( pDirPath );
  205. return;
  206. }
  207. for ( int i=0; i<dirCount; i++ )
  208. {
  209. // form new path name, recurse into
  210. RecurseFileTree_r( pBasePath, fileList[i].String(), depth+1, dirList, bIsModPath );
  211. }
  212. int j = dirList.AddToTail();
  213. dirList[j].Set( pDirPath );
  214. }
  215. //-----------------------------------------------------------------------------
  216. // ExcludePathsDlg_SetCheckState_r
  217. //
  218. // Propogate the check state to all children
  219. //-----------------------------------------------------------------------------
  220. void ExcludePathsDlg_SetCheckState_r( HWND hWndTree, HTREEITEM hTree, int depth, int checkState )
  221. {
  222. if ( !hTree )
  223. {
  224. return;
  225. }
  226. TreeView_SetCheckState( hWndTree, hTree, ( checkState == 1 ) );
  227. TVITEM tvi = { 0 };
  228. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
  229. tvi.hItem = hTree;
  230. if ( TreeView_GetItem( hWndTree, &tvi ) )
  231. {
  232. if ( tvi.cChildren )
  233. {
  234. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  235. if ( hChild )
  236. {
  237. ExcludePathsDlg_SetCheckState_r( hWndTree, hChild, depth+1, checkState );
  238. }
  239. }
  240. }
  241. else
  242. {
  243. return;
  244. }
  245. if ( !depth )
  246. {
  247. // only iterate siblings of the parent's child
  248. return;
  249. }
  250. HTREEITEM hSibling = hTree;
  251. while ( 1 )
  252. {
  253. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  254. if ( !hSibling )
  255. {
  256. return;
  257. }
  258. TreeView_SetCheckState( hWndTree, hSibling, ( checkState == 1 ) );
  259. tvi.hItem = hSibling;
  260. if ( TreeView_GetItem( hWndTree, &tvi ) )
  261. {
  262. if ( tvi.cChildren )
  263. {
  264. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  265. if ( hChild )
  266. {
  267. ExcludePathsDlg_SetCheckState_r( hWndTree, hChild, depth+1, checkState );
  268. }
  269. }
  270. }
  271. }
  272. }
  273. //-----------------------------------------------------------------------------
  274. // ExcludePathsDlg_SetCheckStateLinked_r
  275. //
  276. // Propogate the check state to all "mod path" children that match the specified name.
  277. // A NULL name matches all.
  278. //-----------------------------------------------------------------------------
  279. void ExcludePathsDlg_SetCheckStateLinked_r( HWND hWndTree, HTREEITEM hTree, int depth, int checkState, const char *pName )
  280. {
  281. if ( !hTree )
  282. {
  283. return;
  284. }
  285. char szNodeName[MAX_PATH];
  286. TVITEM tvi = { 0 };
  287. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
  288. tvi.hItem = hTree;
  289. tvi.pszText = szNodeName;
  290. tvi.cchTextMax = sizeof( szNodeName );
  291. if ( TreeView_GetItem( hWndTree, &tvi ) )
  292. {
  293. bool bIsModPath = HIWORD( tvi.lParam ) != 0;
  294. if ( bIsModPath && ( !pName || !V_stricmp( szNodeName, pName ) ) )
  295. {
  296. TreeView_SetCheckState( hWndTree, hTree, ( checkState == 1 ) );
  297. }
  298. if ( tvi.cChildren )
  299. {
  300. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  301. if ( hChild )
  302. {
  303. ExcludePathsDlg_SetCheckStateLinked_r( hWndTree, hChild, depth+1, checkState, pName );
  304. }
  305. }
  306. }
  307. else
  308. {
  309. return;
  310. }
  311. if ( !depth )
  312. {
  313. // only iterate siblings of the parent's child
  314. return;
  315. }
  316. HTREEITEM hSibling = hTree;
  317. while ( 1 )
  318. {
  319. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  320. if ( !hSibling )
  321. {
  322. return;
  323. }
  324. tvi.hItem = hSibling;
  325. if ( TreeView_GetItem( hWndTree, &tvi ) )
  326. {
  327. bool bIsModPath = HIWORD( tvi.lParam ) != 0;
  328. if ( bIsModPath && ( !pName || !V_stricmp( szNodeName, pName ) ) )
  329. {
  330. TreeView_SetCheckState( hWndTree, hSibling, ( checkState == 1 ) );
  331. }
  332. if ( tvi.cChildren )
  333. {
  334. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  335. if ( hChild )
  336. {
  337. ExcludePathsDlg_SetCheckStateLinked_r( hWndTree, hChild, depth+1, checkState, pName );
  338. }
  339. }
  340. }
  341. }
  342. }
  343. //-----------------------------------------------------------------------------
  344. // ExcludePathsDlg_GetCheckState_r
  345. //
  346. // Caller invokes at node. Returns with state set.
  347. // State 0: unchecked, 1: checked, -1:unknown.
  348. // Returns true if All children match, false otherwise.
  349. //-----------------------------------------------------------------------------
  350. bool ExcludePathsDlg_GetCheckState_r( HWND hWndTree, HTREEITEM hTree, int depth, int *pCheckState )
  351. {
  352. int checkState;
  353. checkState = TreeView_GetCheckState( hWndTree, hTree );
  354. if ( *pCheckState == -1 )
  355. {
  356. *pCheckState = checkState;
  357. }
  358. else if ( *pCheckState != checkState )
  359. {
  360. // disparate state, no need to recurse
  361. return false;
  362. }
  363. TVITEM tvi = { 0 };
  364. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
  365. tvi.hItem = hTree;
  366. if ( TreeView_GetItem( hWndTree, &tvi ) )
  367. {
  368. if ( tvi.cChildren )
  369. {
  370. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  371. if ( hChild )
  372. {
  373. if ( !ExcludePathsDlg_GetCheckState_r( hWndTree, hChild, depth+1, pCheckState ) )
  374. {
  375. return false;
  376. }
  377. }
  378. }
  379. }
  380. else
  381. {
  382. return false;
  383. }
  384. if ( !depth )
  385. {
  386. // only iterate siblings of the parent's child
  387. return true;
  388. }
  389. HTREEITEM hSibling = hTree;
  390. while ( 1 )
  391. {
  392. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  393. if ( !hSibling )
  394. {
  395. break;
  396. }
  397. checkState = TreeView_GetCheckState( hWndTree, hSibling );
  398. if ( *pCheckState != checkState )
  399. {
  400. // disparate state, no need to recurse
  401. return false;
  402. }
  403. tvi.hItem = hSibling;
  404. if ( TreeView_GetItem( hWndTree, &tvi ) )
  405. {
  406. if ( tvi.cChildren )
  407. {
  408. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  409. if ( hChild )
  410. {
  411. if ( !ExcludePathsDlg_GetCheckState_r( hWndTree, hChild, depth+1, pCheckState ) )
  412. {
  413. return false;
  414. }
  415. }
  416. }
  417. }
  418. }
  419. return true;
  420. }
  421. //-----------------------------------------------------------------------------
  422. // ExcludePathsDlg_Expand_r
  423. //
  424. // MaxDepth >= 0, Expand/Collapse up to specified depth.
  425. //-----------------------------------------------------------------------------
  426. void ExcludePathsDlg_Expand_r( HWND hWndTree, HTREEITEM hTree, int depth, int maxDepth, bool bExpand )
  427. {
  428. if ( !hTree )
  429. {
  430. return;
  431. }
  432. if ( maxDepth >= 0 && depth >= maxDepth )
  433. {
  434. return;
  435. }
  436. int flags;
  437. if ( bExpand )
  438. flags = TVE_EXPAND;
  439. else
  440. flags = TVE_COLLAPSE;
  441. TVITEM tvi = { 0 };
  442. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
  443. tvi.hItem = hTree;
  444. if ( TreeView_GetItem( hWndTree, &tvi ) )
  445. {
  446. if ( tvi.cChildren )
  447. {
  448. TreeView_Expand( hWndTree, hTree, flags );
  449. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  450. if ( hChild )
  451. {
  452. ExcludePathsDlg_Expand_r( hWndTree, hChild, depth+1, maxDepth, bExpand );
  453. }
  454. }
  455. }
  456. else
  457. {
  458. return;
  459. }
  460. if ( !depth )
  461. {
  462. // only iterate siblings of the parent's child
  463. return;
  464. }
  465. HTREEITEM hSibling = hTree;
  466. while ( 1 )
  467. {
  468. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  469. if ( !hSibling )
  470. {
  471. return;
  472. }
  473. tvi.hItem = hSibling;
  474. if ( TreeView_GetItem( hWndTree, &tvi ) )
  475. {
  476. if ( tvi.cChildren )
  477. {
  478. TreeView_Expand( hWndTree, hSibling, flags );
  479. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  480. if ( hChild )
  481. {
  482. ExcludePathsDlg_Expand_r( hWndTree, hChild, depth+1, maxDepth, bExpand );
  483. }
  484. }
  485. }
  486. }
  487. }
  488. //-----------------------------------------------------------------------------
  489. // ExcludePathsDlg_ExpandToShowState_r
  490. //
  491. //-----------------------------------------------------------------------------
  492. void ExcludePathsDlg_ExpandToShowState_r( HWND hWndTree, HTREEITEM hTree, int depth )
  493. {
  494. if ( !hTree )
  495. {
  496. return;
  497. }
  498. TVITEM tvi = { 0 };
  499. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN;
  500. tvi.hItem = hTree;
  501. if ( TreeView_GetItem( hWndTree, &tvi ) )
  502. {
  503. int checkState = -1;
  504. bool bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hTree, 0, &checkState );
  505. if ( tvi.cChildren && !bStateIsSame )
  506. {
  507. TreeView_Expand( hWndTree, hTree, TVE_EXPAND );
  508. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  509. if ( hChild )
  510. {
  511. ExcludePathsDlg_ExpandToShowState_r( hWndTree, hChild, depth+1 );
  512. }
  513. }
  514. }
  515. else
  516. {
  517. return;
  518. }
  519. if ( !depth )
  520. {
  521. // only iterate siblings of the parent's child
  522. return;
  523. }
  524. HTREEITEM hSibling = hTree;
  525. while ( 1 )
  526. {
  527. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  528. if ( !hSibling )
  529. {
  530. return;
  531. }
  532. tvi.hItem = hSibling;
  533. if ( TreeView_GetItem( hWndTree, &tvi ) )
  534. {
  535. int checkState = -1;
  536. bool bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hSibling, 0, &checkState );
  537. if ( tvi.cChildren && !bStateIsSame )
  538. {
  539. TreeView_Expand( hWndTree, hSibling, TVE_EXPAND );
  540. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  541. if ( hChild )
  542. {
  543. ExcludePathsDlg_ExpandToShowState_r( hWndTree, hChild, depth+1 );
  544. }
  545. }
  546. }
  547. }
  548. }
  549. //-----------------------------------------------------------------------------
  550. // ExcludePathsDlg_Find_r
  551. //
  552. //-----------------------------------------------------------------------------
  553. HTREEITEM ExcludePathsDlg_Find_r( HWND hWndTree, HTREEITEM hTree, int depth, const char *pBasePath, const char *pFindPath )
  554. {
  555. TVITEM tvi = { 0 };
  556. char szName[MAX_PATH];
  557. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_TEXT;
  558. tvi.hItem = hTree;
  559. tvi.pszText = szName;
  560. tvi.cchTextMax = sizeof( szName );
  561. if ( !TreeView_GetItem( hWndTree, &tvi ) )
  562. {
  563. return NULL;
  564. }
  565. char szPath[MAX_PATH];
  566. V_ComposeFileName( pBasePath, szName, szPath, sizeof( szPath ) );
  567. if ( !stricmp( szPath, pFindPath ) )
  568. {
  569. return hTree;
  570. }
  571. if ( tvi.cChildren )
  572. {
  573. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  574. if ( hChild )
  575. {
  576. HTREEITEM hFindTree = ExcludePathsDlg_Find_r( hWndTree, hChild, depth+1, szPath, pFindPath );
  577. if ( hFindTree )
  578. {
  579. return hFindTree;
  580. }
  581. }
  582. }
  583. if ( !depth )
  584. {
  585. // only iterate siblings of the parent's child
  586. return NULL;
  587. }
  588. // iterate siblings
  589. HTREEITEM hSibling = hTree;
  590. while ( 1 )
  591. {
  592. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  593. if ( !hSibling )
  594. {
  595. break;
  596. }
  597. tvi.hItem = hSibling;
  598. if ( !TreeView_GetItem( hWndTree, &tvi ) )
  599. {
  600. break;
  601. }
  602. V_ComposeFileName( pBasePath, szName, szPath, sizeof( szPath ) );
  603. if ( !stricmp( szPath, pFindPath ) )
  604. {
  605. return hSibling;
  606. }
  607. if ( tvi.cChildren )
  608. {
  609. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  610. if ( hChild )
  611. {
  612. HTREEITEM hFindTree = ExcludePathsDlg_Find_r( hWndTree, hChild, depth+1, szPath, pFindPath );
  613. if ( hFindTree )
  614. {
  615. return hFindTree;
  616. }
  617. }
  618. }
  619. }
  620. return NULL;
  621. }
  622. //-----------------------------------------------------------------------------
  623. // ExcludePathsDlg_BuildExcludeList_r
  624. //
  625. // An exclude path represents the path head, and thus does not need to iterate its children
  626. // if they are deemed to the same exclusion state.
  627. //-----------------------------------------------------------------------------
  628. void ExcludePathsDlg_BuildExcludeList_r( HWND hWndTree, HTREEITEM hTree, int depth, const char *pPath )
  629. {
  630. int checkState = -1;
  631. bool bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hTree, 0, &checkState );
  632. if ( checkState == -1 )
  633. {
  634. return;
  635. }
  636. TVITEM tvi = { 0 };
  637. char szName[MAX_PATH];
  638. tvi.mask = TVIF_HANDLE | TVIF_CHILDREN | TVIF_TEXT;
  639. tvi.hItem = hTree;
  640. tvi.pszText = szName;
  641. tvi.cchTextMax = sizeof( szName );
  642. if ( !TreeView_GetItem( hWndTree, &tvi ) )
  643. {
  644. return;
  645. }
  646. char szPath[MAX_PATH];
  647. V_ComposeFileName( pPath, szName, szPath, sizeof( szPath ) );
  648. if ( checkState == 1 && ( bStateIsSame || !tvi.cChildren ) )
  649. {
  650. // add to exclude list
  651. g_ExcludePaths.AddToTail( szPath );
  652. }
  653. if ( !bStateIsSame && tvi.cChildren )
  654. {
  655. // mixed states, must recurse to resolve
  656. HTREEITEM hChild = TreeView_GetChild( hWndTree, hTree );
  657. if ( hChild )
  658. {
  659. ExcludePathsDlg_BuildExcludeList_r( hWndTree, hChild, depth+1, szPath );
  660. }
  661. }
  662. if ( !depth )
  663. {
  664. // only iterate siblings of the parent's child
  665. return;
  666. }
  667. // iterate siblings
  668. HTREEITEM hSibling = hTree;
  669. while ( 1 )
  670. {
  671. hSibling = TreeView_GetNextSibling( hWndTree, hSibling );
  672. if ( !hSibling )
  673. {
  674. break;
  675. }
  676. checkState = -1;
  677. bStateIsSame = ExcludePathsDlg_GetCheckState_r( hWndTree, hSibling, 0, &checkState );
  678. if ( checkState == -1 )
  679. {
  680. break;
  681. }
  682. tvi.hItem = hSibling;
  683. if ( !TreeView_GetItem( hWndTree, &tvi ) )
  684. {
  685. break;
  686. }
  687. V_ComposeFileName( pPath, szName, szPath, sizeof( szPath ) );
  688. if ( checkState == 1 && ( bStateIsSame || !tvi.cChildren ) )
  689. {
  690. // add to exclude list
  691. g_ExcludePaths.AddToTail( szPath );
  692. }
  693. if ( !bStateIsSame && tvi.cChildren )
  694. {
  695. HTREEITEM hChild = TreeView_GetChild( hWndTree, hSibling );
  696. if ( hChild )
  697. {
  698. ExcludePathsDlg_BuildExcludeList_r( hWndTree, hChild, depth+1, szPath );
  699. }
  700. }
  701. }
  702. }
  703. //-----------------------------------------------------------------------------
  704. // ExcludePathsDlg_SaveChanges
  705. //
  706. //-----------------------------------------------------------------------------
  707. void ExcludePathsDlg_SaveChanges( HWND hWnd )
  708. {
  709. g_ExcludePaths.Purge();
  710. HWND hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
  711. ExcludePathsDlg_BuildExcludeList_r( hWndTree, TreeView_GetRoot( hWndTree ), 0, "" );
  712. char szPath[MAX_PATH];
  713. V_ComposeFileName( g_localPath, EXCLUDEPATHS_FILE, szPath, sizeof( szPath ) );
  714. if ( !g_ExcludePaths.Count() )
  715. {
  716. // no exclude paths
  717. unlink( szPath );
  718. }
  719. else
  720. {
  721. FILE *fp = fopen( szPath, "wt" );
  722. if ( !fp )
  723. {
  724. Sys_MessageBox( "Error", "Could not open '%s' for writing\n", szPath );
  725. return;
  726. }
  727. fprintf( fp, "// Auto-Generated by VXConsole - DO NOT MODIFY!\n" );
  728. for ( int i = 0; i < g_ExcludePaths.Count(); i++ )
  729. {
  730. // strip expected root path
  731. const char *pPath = g_ExcludePaths[i].String();
  732. pPath += strlen( ROOT_NAME );
  733. if ( !pPath[0] )
  734. {
  735. // special code for root
  736. fprintf( fp, "*\n" );
  737. break;
  738. }
  739. fprintf( fp, "\"\\%s\"\n", pPath );
  740. }
  741. fclose( fp );
  742. }
  743. }
  744. //-----------------------------------------------------------------------------
  745. // ExcludePathsDlg_Populate
  746. //
  747. // Generate a path table.
  748. //-----------------------------------------------------------------------------
  749. void ExcludePathsDlg_Populate( HWND hWnd, bool bRefresh )
  750. {
  751. struct GamePath_t
  752. {
  753. CUtlString pathName;
  754. bool bIsModPath;
  755. };
  756. CUtlVector< GamePath_t > gamePaths;
  757. // can skip the time consuming directory scan, unless forced
  758. if ( bRefresh || !g_PathTable.Count() )
  759. {
  760. g_PathTable.Purge();
  761. for ( int i = 0; i < ARRAYSIZE( g_GameNames ); i++ )
  762. {
  763. char szTargetPath[MAX_PATH];
  764. V_strncpy( szTargetPath, g_localPath, sizeof( szTargetPath ) );
  765. V_AppendSlash( szTargetPath, sizeof( szTargetPath ) );
  766. V_strncat( szTargetPath, g_GameNames[i].pName, sizeof( szTargetPath ) );
  767. GamePath_t gamePath;
  768. gamePath.pathName = szTargetPath;
  769. gamePath.bIsModPath = g_GameNames[i].bIsModPath;
  770. gamePaths.AddToTail( gamePath );
  771. }
  772. // iterate all game paths
  773. for ( int i = 0; i < gamePaths.Count(); i++ )
  774. {
  775. CUtlVector< CUtlString > dirList;
  776. RecurseFileTree_r( g_localPath, gamePaths[i].pathName.String(), 0, dirList, gamePaths[i].bIsModPath );
  777. }
  778. }
  779. // reset the tree
  780. HWND hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
  781. TreeView_DeleteAllItems( hWndTree );
  782. // reset and add root
  783. // only the root is at depth 0
  784. AddItemToTree( hWndTree, ROOT_NAME, 0, false );
  785. // build the tree view
  786. for ( int iIndex = g_PathTable.FirstInorder(); iIndex != g_PathTable.InvalidIndex(); iIndex = g_PathTable.NextInorder( iIndex ) )
  787. {
  788. // due to sorting, number of slashes is depth
  789. const char *pString = g_PathTable[iIndex].pathName.String();
  790. int depth = 0;
  791. for ( int j = 0; j < (int)strlen( pString ); j++ )
  792. {
  793. if ( pString[j] == '\\' )
  794. {
  795. depth++;
  796. }
  797. }
  798. if ( !depth )
  799. {
  800. depth = 1;
  801. }
  802. char szPath[MAX_PATH];
  803. V_FileBase( pString, szPath, sizeof( szPath ) );
  804. AddItemToTree( hWndTree, szPath, depth, g_PathTable[iIndex].bIsModPath );
  805. }
  806. HTREEITEM hTreeRoot = TreeView_GetRoot( hWndTree );
  807. for ( int i = 0; i < g_ExcludePaths.Count(); i++ )
  808. {
  809. HTREEITEM hTree = ExcludePathsDlg_Find_r( hWndTree, hTreeRoot, 0, "", g_ExcludePaths[i].String() );
  810. if ( hTree )
  811. {
  812. ExcludePathsDlg_SetCheckState_r( hWndTree, hTree, 0, true );
  813. }
  814. }
  815. // expand the root node to show state
  816. ExcludePathsDlg_ExpandToShowState_r( hWndTree, hTreeRoot, 0 );
  817. }
  818. //-----------------------------------------------------------------------------
  819. // ExcludePathsDlg_Setup
  820. //
  821. //-----------------------------------------------------------------------------
  822. void ExcludePathsDlg_Setup( HWND hWnd )
  823. {
  824. TreeView_SetBkColor( GetDlgItem( hWnd, IDC_PATHS_TREE ), g_backgroundColor );
  825. CheckDlgButton( hWnd, IDC_PATHS_LINKGAMEDIRS, g_bLinkGameDirs ? BST_CHECKED : BST_UNCHECKED );
  826. // read the exisiting exclude paths
  827. g_ExcludePaths.Purge();
  828. char szFilename[MAX_PATH];
  829. V_ComposeFileName( g_localPath, EXCLUDEPATHS_FILE, szFilename, sizeof( szFilename ) );
  830. if ( Sys_Exists( szFilename ) )
  831. {
  832. Sys_LoadScriptFile( szFilename );
  833. while ( 1 )
  834. {
  835. char *pToken = Sys_GetToken( true );
  836. if ( !pToken || !pToken[0] )
  837. {
  838. break;
  839. }
  840. Sys_StripQuotesFromToken( pToken );
  841. if ( !stricmp( pToken, "*" ) )
  842. {
  843. pToken = "";
  844. }
  845. else if ( pToken[0] == '\\' )
  846. {
  847. pToken++;
  848. }
  849. char szPath[MAX_PATH];
  850. V_ComposeFileName( ROOT_NAME, pToken, szPath, sizeof( szPath ) );
  851. V_FixSlashes( szPath );
  852. g_ExcludePaths.AddToTail( szPath );
  853. }
  854. }
  855. }
  856. //-----------------------------------------------------------------------------
  857. // ExcludePathsDlg_Proc
  858. //
  859. //-----------------------------------------------------------------------------
  860. BOOL CALLBACK ExcludePathsDlg_Proc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
  861. {
  862. HWND hWndTree;
  863. LPNMHDR lpnmh;
  864. HTREEITEM hTree;
  865. HTREEITEM hTreeRoot;
  866. int checkState;
  867. static bool s_bAllowPopulate = 0;
  868. switch ( message )
  869. {
  870. case WM_INITDIALOG:
  871. ExcludePathsDlg_Setup( hWnd );
  872. s_bAllowPopulate = true;
  873. return TRUE;
  874. case WM_NOTIFY:
  875. lpnmh = (LPNMHDR)lParam;
  876. if ( ( lpnmh->code == NM_CLICK ) && ( lpnmh->idFrom == IDC_PATHS_TREE ) )
  877. {
  878. TVHITTESTINFO ht = {0};
  879. DWORD dwpos = GetMessagePos();
  880. ht.pt.x = GET_X_LPARAM( dwpos );
  881. ht.pt.y = GET_Y_LPARAM( dwpos );
  882. MapWindowPoints( HWND_DESKTOP, lpnmh->hwndFrom, &ht.pt, 1 );
  883. TreeView_HitTest( lpnmh->hwndFrom, &ht );
  884. if ( ht.flags & TVHT_ONITEMSTATEICON )
  885. {
  886. PostMessage( hWnd, UM_CHECKSTATECHANGE, 0, (LPARAM)ht.hItem );
  887. }
  888. }
  889. break;
  890. case UM_CHECKSTATECHANGE:
  891. hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
  892. hTreeRoot = TreeView_GetRoot( hWndTree );
  893. hTree = (HTREEITEM)lParam;
  894. checkState = TreeView_GetCheckState( hWndTree, hTree );
  895. if ( checkState != -1 )
  896. {
  897. TVITEM tvi = { 0 };
  898. char szName[MAX_PATH];
  899. tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT;
  900. tvi.hItem = hTree;
  901. tvi.pszText = szName;
  902. tvi.cchTextMax = sizeof( szName );
  903. if ( !TreeView_GetItem( hWndTree, &tvi ) )
  904. {
  905. break;
  906. }
  907. int nDepth = LOWORD( tvi.lParam );
  908. bool bIsModPath = HIWORD( tvi.lParam ) != 0;
  909. if ( g_bLinkGameDirs && bIsModPath )
  910. {
  911. // a mod path root is at depth 1
  912. // a child of a mod path (depth > 1), will match all other children in mod paths
  913. // a mod path (depth = 1) will match all other modpaths
  914. ExcludePathsDlg_SetCheckStateLinked_r( hWndTree, hTreeRoot, 0, checkState, nDepth == 1 ? NULL : szName );
  915. }
  916. else
  917. {
  918. ExcludePathsDlg_SetCheckState_r( hWndTree, hTree, 0, checkState );
  919. }
  920. }
  921. break;
  922. case WM_PAINT:
  923. if ( s_bAllowPopulate )
  924. {
  925. // unfortunate, but tree view needs to paint first before its state can be set
  926. // related to checkbox style which doesn't manifest until after WM_INITDIALOG
  927. // stall the initial population until after the first paint
  928. s_bAllowPopulate = false;
  929. PostMessage( hWnd, UM_FIRSTTIMEPOPULATE, 0, 0 );
  930. }
  931. break;
  932. case UM_FIRSTTIMEPOPULATE:
  933. ExcludePathsDlg_Populate( hWnd, false );
  934. break;
  935. case WM_COMMAND:
  936. switch ( LOWORD( wParam ) )
  937. {
  938. case IDC_OK:
  939. ExcludePathsDlg_SaveChanges( hWnd );
  940. EndDialog( hWnd, wParam );
  941. return TRUE;
  942. case IDC_PATHS_RESCAN:
  943. ExcludePathsDlg_Populate( hWnd, true );
  944. return TRUE;
  945. case IDC_PATHS_EXPAND:
  946. // expand from root
  947. hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
  948. ExcludePathsDlg_Expand_r( hWndTree, TreeView_GetRoot( hWndTree ), 0, -1, true );
  949. return TRUE;
  950. case IDC_PATHS_COLLAPSE:
  951. // collapse from root
  952. hWndTree = GetDlgItem( hWnd, IDC_PATHS_TREE );
  953. ExcludePathsDlg_Expand_r( hWndTree, TreeView_GetRoot( hWndTree ), 0, -1, false );
  954. return TRUE;
  955. case IDC_PATHS_LINKGAMEDIRS:
  956. g_bLinkGameDirs = IsDlgButtonChecked( hWnd, IDC_PATHS_LINKGAMEDIRS );
  957. return TRUE;
  958. case IDCANCEL:
  959. case IDC_CANCEL:
  960. EndDialog( hWnd, wParam );
  961. return TRUE;
  962. }
  963. break;
  964. }
  965. return FALSE;
  966. }
  967. //-----------------------------------------------------------------------------
  968. // ExcludePathsDlg_LoadConfig
  969. //
  970. //-----------------------------------------------------------------------------
  971. void ExcludePathsDlg_LoadConfig()
  972. {
  973. // get our config
  974. Sys_GetRegistryInteger( "ExcludePaths_LinkGameDirs", true, g_bLinkGameDirs );
  975. }
  976. //-----------------------------------------------------------------------------
  977. // ExcludePathsDlg_SaveConfig
  978. //
  979. //-----------------------------------------------------------------------------
  980. void ExcludePathsDlg_SaveConfig()
  981. {
  982. Sys_SetRegistryInteger( "ExcludePaths_LinkGameDirs", g_bLinkGameDirs );
  983. }
  984. //-----------------------------------------------------------------------------
  985. // ExcludePathsDlg_Init
  986. //
  987. //-----------------------------------------------------------------------------
  988. bool ExcludePathsDlg_Init()
  989. {
  990. return true;
  991. }
  992. //-----------------------------------------------------------------------------
  993. // ExcludePathsDlg_Open
  994. //
  995. //-----------------------------------------------------------------------------
  996. void ExcludePathsDlg_Open()
  997. {
  998. ExcludePathsDlg_LoadConfig();
  999. int result = DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_EXCLUDEPATHS ), g_hDlgMain, ( DLGPROC )ExcludePathsDlg_Proc );
  1000. if ( LOWORD( result ) != IDC_OK )
  1001. return;
  1002. ExcludePathsDlg_SaveConfig();
  1003. }