CommandoUpdateDlg.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. // CommandoUpdateDlg.cpp : implementation file
  19. //
  20. #include "stdafx.h"
  21. #include "CommandoUpdate.h"
  22. #include "CommandoUpdateDlg.h"
  23. #include "FileCopyDialog.H"
  24. #ifdef _DEBUG
  25. #define new DEBUG_NEW
  26. #undef THIS_FILE
  27. static char THIS_FILE[] = __FILE__;
  28. #endif
  29. /////////////////////////////////////////////////////////////////////////////
  30. //
  31. // Data Tables
  32. //
  33. /////////////////////////////////////////////////////////////////////////////
  34. typedef struct
  35. {
  36. FileCopyDialogClass *dialog;
  37. CString src_dir;
  38. CString dest_dir;
  39. bool is_recursive;
  40. bool result;
  41. } UPDATE_INFO;
  42. typedef struct
  43. {
  44. LPCTSTR title;
  45. LPCTSTR src;
  46. LPCTSTR default_dir;
  47. LPCTSTR reg_key;
  48. UINT ctrl_id;
  49. UINT clean_ctrl_id;
  50. UINT dir_ctrl_id;
  51. bool clean_default;
  52. bool is_recursive;
  53. } APP_INFO;
  54. typedef enum
  55. {
  56. APP_EDITOR = 0,
  57. APP_GAME_ENGINE,
  58. APP_GAME_LEVELS,
  59. APP_GAME_MOVIES,
  60. APP_LIGHTMAP,
  61. APP_VISFARM,
  62. APP_MIXVIEW,
  63. APP_MULTIPLAY,
  64. APP_MAX,
  65. } APPLICATION_IDS;
  66. const APP_INFO APPLICATIONS[APP_MAX] =
  67. {
  68. { "Level Editor", "\\\\mobius\\project7\\projects\\renegade\\programming\\tools\\level edit", "c:\\commando\\leveledit", "LevelEdit\\Install", IDC_EDITOR_CHECK, IDC_EDITOR_CLEAN_CHECK, IDC_EDITOR_DIR_BUTTON, true, false },
  69. { "Game Engine", "\\\\havoc\\rock\\projects\\renegade\\asset management\\current build", "c:\\commando\\run", "Commando\\Install", IDC_GAME_CHECK, IDC_GAME_CLEAN_CHECK, IDC_GAME_DIR_BUTTON, false, true },
  70. { "Game Levels", "\\\\havoc\\rock\\projects\\renegade\\asset management\\current levels", "c:\\commando\\run", "Commando\\Install", IDC_GAME_LEVELS_CHECK, IDC_GAME_CLEAN_CHECK, IDC_GAME_DIR_BUTTON, false, true },
  71. { "Game Movies", "\\\\havoc\\rock\\projects\\renegade\\asset management\\cinematics", "c:\\commando\\run", "Commando\\Install", IDC_GAME_MOVIES_CHECK, IDC_GAME_CLEAN_CHECK, IDC_GAME_DIR_BUTTON, false, true },
  72. { "Lightmap", "\\\\mobius\\project7\\projects\\renegade\\programming\\tools\\lightmap", "c:\\commando\\lightmap", "LightMap\\Install", IDC_LIGHTMAP_CHECK, IDC_LIGHTMAP_CLEAN_CHECK, IDC_LIGHTMAP_DIR_BUTTON, true, false },
  73. { "Vis Farm", "\\\\mobius\\project7\\projects\\renegade\\programming\\tools\\vis farm", "c:\\commando\\vis farm", "VisFarm\\Install", IDC_VISFARM_CHECK, IDC_VISFARM_CLEAN_CHECK, IDC_VISFARM_DIR_BUTTON, false, false },
  74. { "Mix Viewer", "\\\\mobius\\project7\\projects\\renegade\\programming\\tools\\mixview", "c:\\commando\\mixview", "MixView\\Install", IDC_MIXVIEW_CHECK, IDC_MIXVIEW_CLEAN_CHECK, IDC_MIXVIEW_DIR_BUTTON, false, false },
  75. { "Multiplay", "\\\\tanya\\game\\projects\\renegade\\run", "c:\\renegade\\mplay", "MPlay\\Install", IDC_MULTIPLAY_CHECK, IDC_MULTIPLAY_CLEAN_CHECK, IDC_MULTIPLAY_DIR_BUTTON, false, true }
  76. };
  77. const char * const INSTALL_REG_VALUE = "Path";
  78. const char * const DATA_SUB_DIR = "\\Data";
  79. const char * const MOVIE_SUB_DIR = "\\Data\\Movies";
  80. const char * const SRDLL_SUB_DIR = "\\SrDLL";
  81. /////////////////////////////////////////////////////////////////////////////
  82. //
  83. // Local prototypes
  84. //
  85. /////////////////////////////////////////////////////////////////////////////
  86. bool Update_App (CWnd *parent_wnd, LPCTSTR title, LPCTSTR src_dir, LPCTSTR dest_dir, bool is_recursive);
  87. bool Clean_Directory (LPCTSTR local_dir, bool is_recursive);
  88. bool Get_Install_Directory (HWND hparent_wnd, LPCTSTR title, CString &folder);
  89. bool Delete_File (LPCTSTR filename);
  90. /////////////////////////////////////////////////////////////////////////////
  91. // CCommandoUpdateDlg dialog
  92. CCommandoUpdateDlg::CCommandoUpdateDlg(CWnd* pParent /*=NULL*/)
  93. : CDialog(CCommandoUpdateDlg::IDD, pParent)
  94. {
  95. //{{AFX_DATA_INIT(CCommandoUpdateDlg)
  96. // NOTE: the ClassWizard will add member initialization here
  97. //}}AFX_DATA_INIT
  98. // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
  99. m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  100. }
  101. void CCommandoUpdateDlg::DoDataExchange(CDataExchange* pDX)
  102. {
  103. CDialog::DoDataExchange(pDX);
  104. //{{AFX_DATA_MAP(CCommandoUpdateDlg)
  105. // NOTE: the ClassWizard will add DDX and DDV calls here
  106. //}}AFX_DATA_MAP
  107. }
  108. BEGIN_MESSAGE_MAP(CCommandoUpdateDlg, CDialog)
  109. //{{AFX_MSG_MAP(CCommandoUpdateDlg)
  110. ON_WM_PAINT()
  111. ON_WM_QUERYDRAGICON()
  112. ON_BN_CLICKED(IDC_DEFAULTS, OnDefaults)
  113. //}}AFX_MSG_MAP
  114. END_MESSAGE_MAP()
  115. /////////////////////////////////////////////////////////////////////////////
  116. // CCommandoUpdateDlg message handlers
  117. BOOL CCommandoUpdateDlg::OnInitDialog()
  118. {
  119. CDialog::OnInitDialog();
  120. // Set the icon for this dialog. The framework does this automatically
  121. // when the application's main window is not a dialog
  122. SetIcon(m_hIcon, TRUE); // Set big icon
  123. SetIcon(m_hIcon, FALSE); // Set small icon
  124. //
  125. // Check/uncheck the applications by default
  126. //
  127. for (int index = 0; index < APP_MAX; index ++) {
  128. const APP_INFO &app_info = APPLICATIONS[index];
  129. HKEY hreg_key = NULL;
  130. CString reg_key_name;
  131. reg_key_name.Format ("Software\\Westwood Studios\\%s", app_info.reg_key);
  132. TCHAR path[MAX_PATH] = { 0 };
  133. //
  134. // Is this application installed?
  135. //
  136. if (::RegOpenKeyEx (HKEY_CURRENT_USER, reg_key_name, 0L, KEY_READ, &hreg_key) == ERROR_SUCCESS) {
  137. SendDlgItemMessage (app_info.ctrl_id, BM_SETCHECK, (WPARAM)TRUE);
  138. //
  139. // Read the installation directory from the registry
  140. //
  141. DWORD size = sizeof (path);
  142. ::RegQueryValueEx (hreg_key, INSTALL_REG_VALUE, 0L, NULL, (BYTE *)path, &size);
  143. ::RegCloseKey (hreg_key);
  144. } else {
  145. ::EnableWindow (::GetDlgItem (m_hWnd, app_info.clean_ctrl_id), false);
  146. ::EnableWindow (::GetDlgItem (m_hWnd, app_info.dir_ctrl_id), false);
  147. }
  148. //
  149. // Set the text of the install directory buttons
  150. //
  151. if (path[0] == 0) {
  152. ::lstrcpy (path, app_info.default_dir);
  153. }
  154. SetDlgItemText (app_info.dir_ctrl_id, path);
  155. //
  156. // Check the clean option (by default) if necessary
  157. //
  158. if (app_info.clean_default) {
  159. SendDlgItemMessage (app_info.clean_ctrl_id, BM_SETCHECK, (WPARAM)TRUE);
  160. }
  161. }
  162. return TRUE;
  163. }
  164. // If you add a minimize button to your dialog, you will need the code below
  165. // to draw the icon. For MFC applications using the document/view model,
  166. // this is automatically done for you by the framework.
  167. void CCommandoUpdateDlg::OnPaint()
  168. {
  169. if (IsIconic())
  170. {
  171. CPaintDC dc(this); // device context for painting
  172. SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
  173. // Center icon in client rectangle
  174. int cxIcon = GetSystemMetrics(SM_CXICON);
  175. int cyIcon = GetSystemMetrics(SM_CYICON);
  176. CRect rect;
  177. GetClientRect(&rect);
  178. int x = (rect.Width() - cxIcon + 1) / 2;
  179. int y = (rect.Height() - cyIcon + 1) / 2;
  180. // Draw the icon
  181. dc.DrawIcon(x, y, m_hIcon);
  182. }
  183. else
  184. {
  185. CDialog::OnPaint();
  186. }
  187. }
  188. // The system calls this to obtain the cursor to display while the user drags
  189. // the minimized window.
  190. HCURSOR CCommandoUpdateDlg::OnQueryDragIcon()
  191. {
  192. return (HCURSOR) m_hIcon;
  193. }
  194. //////////////////////////////////////////////////////////////////////////////////
  195. //
  196. // OnOK
  197. //
  198. //////////////////////////////////////////////////////////////////////////////////
  199. void
  200. CCommandoUpdateDlg::OnOK (void)
  201. {
  202. //
  203. // Install/upgrade each application
  204. //
  205. bool success = true;
  206. for (int index = 0; (index < APP_MAX) && success; index ++) {
  207. const APP_INFO &app_info = APPLICATIONS[index];
  208. //
  209. // Does the user want to install this application?
  210. //
  211. if (SendDlgItemMessage (app_info.ctrl_id, BM_GETCHECK) == 1) {
  212. HKEY hreg_key = NULL;
  213. CString reg_key_name;
  214. reg_key_name.Format ("Software\\Westwood Studios\\%s", app_info.reg_key);
  215. //
  216. // Get the path where this application is installed
  217. //
  218. CString local_path;
  219. GetDlgItemText (app_info.dir_ctrl_id, local_path);
  220. // Ensure the path doesn't contain a terminating delimiter
  221. if (local_path[::lstrlen (local_path) - 1] == '\\') {
  222. local_path.Delete (::lstrlen (local_path) - 1, 1);
  223. }
  224. if (success) {
  225. CWaitCursor wait_cursor;
  226. //
  227. // Clean the old version if necessary
  228. //
  229. if (SendDlgItemMessage (app_info.clean_ctrl_id, BM_GETCHECK) == 1) {
  230. //
  231. // Special case the "game engine only" option. Note: we only want
  232. // to clean the data subdir if we aren't cleaning the game directory
  233. //
  234. if (index == APP_GAME_LEVELS || index == APP_GAME_MOVIES) {
  235. if (SendDlgItemMessage (APPLICATIONS[APP_GAME_ENGINE].ctrl_id, BM_GETCHECK) == 0) {
  236. if (index == APP_GAME_MOVIES) {
  237. CString data_dir = local_path + MOVIE_SUB_DIR;
  238. ::Clean_Directory (data_dir, app_info.is_recursive);
  239. } else {
  240. CString data_dir = local_path + DATA_SUB_DIR;
  241. ::Clean_Directory (data_dir, app_info.is_recursive);
  242. }
  243. }
  244. } else {
  245. ::Clean_Directory (local_path, app_info.is_recursive);
  246. }
  247. }
  248. //
  249. // Special case the "game levels" option
  250. //
  251. if (index == APP_GAME_LEVELS) {
  252. CString data_dir = local_path + DATA_SUB_DIR;
  253. CString src_data_dir = app_info.src;
  254. CString data_title = "Game Levels";
  255. success &= ::Update_App (this, data_title, src_data_dir, data_dir, app_info.is_recursive);
  256. } else if (index == APP_GAME_MOVIES) {
  257. CString data_dir = local_path + MOVIE_SUB_DIR;
  258. CString src_data_dir = app_info.src;
  259. CString data_title = "Game Movies";
  260. success &= ::Update_App (this, data_title, src_data_dir, data_dir, app_info.is_recursive);
  261. } else {
  262. //
  263. // Update the application's files
  264. //
  265. success &= ::Update_App (this, app_info.title, app_info.src, local_path, app_info.is_recursive);
  266. }
  267. //
  268. // Write the app's installation dir to the registry if we
  269. // installed it correctly.
  270. //
  271. if (success) {
  272. success &= (::RegCreateKeyEx ( HKEY_CURRENT_USER,
  273. reg_key_name,
  274. 0L,
  275. NULL,
  276. REG_OPTION_NON_VOLATILE,
  277. KEY_ALL_ACCESS,
  278. NULL,
  279. &hreg_key,
  280. NULL) == ERROR_SUCCESS);
  281. if (success) {
  282. ::RegSetValueEx ( hreg_key,
  283. INSTALL_REG_VALUE,
  284. 0L,
  285. REG_SZ,
  286. (BYTE *)(LPCTSTR)local_path,
  287. local_path.GetLength () + 1);
  288. ::RegCloseKey (hreg_key);
  289. }
  290. }
  291. }
  292. }
  293. }
  294. CDialog::OnOK ();
  295. return ;
  296. }
  297. //////////////////////////////////////////////////////////////////////////////////
  298. //
  299. // Get_Install_Directory
  300. //
  301. //////////////////////////////////////////////////////////////////////////////////
  302. bool
  303. Get_Install_Directory (HWND hparent_wnd, LPCTSTR title, CString &folder)
  304. {
  305. bool retval = false;
  306. // Browse for the folder
  307. BROWSEINFO browse_info = { 0 };
  308. browse_info.hwndOwner = hparent_wnd;
  309. browse_info.lpszTitle = title;
  310. browse_info.ulFlags = BIF_RETURNONLYFSDIRS;
  311. LPITEMIDLIST pidl = ::SHBrowseForFolder (&browse_info);
  312. if (pidl != NULL) {
  313. // Convert the 'PIDL' into a string
  314. char path[MAX_PATH];
  315. retval = (::SHGetPathFromIDList (pidl, path) == TRUE);
  316. if (retval) {
  317. folder = path;
  318. }
  319. // Free the 'PIDL'
  320. LPMALLOC pmalloc = NULL;
  321. if (SUCCEEDED (::SHGetMalloc (&pmalloc))) {
  322. pmalloc->Free (pidl);
  323. pmalloc->Release ();
  324. }
  325. }
  326. //CFileDialog dialog (TRUE, ".pth", "test.pth", OFN_PATHMUSTEXIST | OFN_EXPLORER, "files *.*|*.*||", CWnd::FromHandle(hparent_wnd));
  327. //CFileDialog dialog (TRUE, ".pth", "test.pth", OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_ENABLETEMPLATEHANDLE, "files *.*|*.*||", CWnd::FromHandle(hparent_wnd));
  328. //dialog.m_ofn.lpTemplateName = MAKEINTRESOURCE (IDD_DIR_SELECT_DIALOG);
  329. //dialog.m_ofn.hInstance = ::AfxGetInstanceHandle ();
  330. //HRSRC resource = ::FindResource (::AfxGetInstanceHandle (), MAKEINTRESOURCE (IDD_DIR_SELECT_DIALOG), RT_DIALOG);
  331. //dialog.m_ofn.hInstance = (HINSTANCE)::LoadResource (::AfxGetInstanceHandle (), resource);
  332. //dialog.DoModal ();
  333. /*TCHAR path[MAX_PATH] = { 0 };
  334. OPENFILENAME ofn = { 0 };
  335. ofn.lStructSize = sizeof (ofn);
  336. ofn.Flags = OFN_PATHMUSTEXIST | OFN_ENABLETEMPLATE;
  337. ofn.hInstance = ::AfxGetInstanceHandle ();
  338. ofn.lpTemplateName = MAKEINTRESOURCE (IDD_DIR_SELECT_DIALOG);
  339. ofn.hwndOwner = hparent_wnd;
  340. ofn.lpstrFile = path;
  341. ofn.nMaxFile = sizeof (path);
  342. GetOpenFileName (&ofn);
  343. ::CommDlgExtendedError ();*/
  344. //retval = (dialog.DoModal () == IDOK);
  345. // Return the true/false result code
  346. return retval;
  347. }
  348. ////////////////////////////////////////////////////////////////////////////
  349. //
  350. // Strip_Filename_From_Path
  351. //
  352. ////////////////////////////////////////////////////////////////////////////
  353. CString
  354. Strip_Filename_From_Path (LPCTSTR path)
  355. {
  356. // Copy the path to a buffer we can modify
  357. TCHAR temp_path[MAX_PATH];
  358. ::lstrcpy (temp_path, path);
  359. // Find the last occurance of the directory deliminator
  360. LPTSTR filename = ::strrchr (temp_path, '\\');
  361. if (filename != NULL) {
  362. // Strip off the filename
  363. filename[0] = 0;
  364. }
  365. // Return the path only
  366. return CString (temp_path);
  367. }
  368. ////////////////////////////////////////////////////////////////////////////
  369. //
  370. // Delete_File
  371. //
  372. ////////////////////////////////////////////////////////////////////////////
  373. bool
  374. Delete_File (LPCTSTR filename)
  375. {
  376. // Assume failure
  377. bool retval = false;
  378. ASSERT (filename != NULL);
  379. if (filename != NULL) {
  380. // Strip the readonly bit off if necessary
  381. DWORD attributes = ::GetFileAttributes (filename);
  382. if ((attributes != 0xFFFFFFFF) &&
  383. ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY))
  384. {
  385. ::SetFileAttributes (filename, attributes & (~FILE_ATTRIBUTE_READONLY));
  386. }
  387. // Perform the delete operation!
  388. if ((attributes != 0xFFFFFFFF) &&
  389. ((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
  390. {
  391. retval = ::RemoveDirectory (filename) == TRUE;
  392. } else {
  393. retval = ::DeleteFile (filename) == TRUE;
  394. }
  395. }
  396. // Return the true/false result code
  397. return retval;
  398. }
  399. ////////////////////////////////////////////////////////////////////////////
  400. //
  401. // Copy_File
  402. //
  403. ////////////////////////////////////////////////////////////////////////////
  404. bool
  405. Copy_File
  406. (
  407. LPCTSTR existing_filename,
  408. LPCTSTR new_filename,
  409. bool bforce_copy
  410. )
  411. {
  412. // Assume failure
  413. bool retval = false;
  414. ASSERT (existing_filename != NULL);
  415. ASSERT (new_filename != NULL);
  416. if ((existing_filename != NULL) &&
  417. (new_filename != NULL)) {
  418. // Make sure we aren't copying over ourselves
  419. bool allow_copy = (::lstrcmpi (existing_filename, new_filename) != 0);
  420. // Strip the readonly bit off if necessary
  421. DWORD attributes = ::GetFileAttributes (new_filename);
  422. if (allow_copy &&
  423. (attributes != 0xFFFFFFFF) &&
  424. ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)) {
  425. if (bforce_copy) {
  426. ::SetFileAttributes (new_filename, attributes & (~FILE_ATTRIBUTE_READONLY));
  427. } else {
  428. allow_copy = false;
  429. }
  430. }
  431. // Perform the copy operation!
  432. if (allow_copy) {
  433. retval = (::CopyFile (existing_filename, new_filename, FALSE) == TRUE);
  434. }
  435. }
  436. // Return the true/false result code
  437. return retval;
  438. }
  439. __inline void Delimit_Path (CString &path)
  440. {
  441. if (path[::lstrlen (path) - 1] != '\\') {
  442. path += CString ("\\");
  443. }
  444. return ;
  445. }
  446. //////////////////////////////////////////////////////////////////////////////////
  447. //
  448. // Clean_Directory
  449. //
  450. //////////////////////////////////////////////////////////////////////////////////
  451. bool
  452. Clean_Directory (LPCTSTR local_dir, bool is_recursive)
  453. {
  454. bool retval = true;
  455. // Build a search mask from the directory
  456. CString search_mask = CString (local_dir) + "\\*.*";
  457. // Loop through all the files in this directory and add them
  458. // to our list
  459. CStringList file_list;
  460. BOOL bcontinue = TRUE;
  461. WIN32_FIND_DATA find_info = { 0 };
  462. for (HANDLE hfind = ::FindFirstFile (search_mask, &find_info);
  463. (hfind != INVALID_HANDLE_VALUE) && bcontinue;
  464. bcontinue = ::FindNextFile (hfind, &find_info)) {
  465. // If this file isn't a directory, add it to the list
  466. if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  467. CString filename = find_info.cFileName;
  468. file_list.AddTail (filename);
  469. } else if (is_recursive && find_info.cFileName[0] != '.') {
  470. //
  471. // Recurse into this subdirectory
  472. //
  473. CString full_path = local_dir;
  474. ::Delimit_Path (full_path);
  475. full_path += find_info.cFileName;
  476. Clean_Directory (full_path, is_recursive);
  477. //
  478. // Add this directory to the list so it will get
  479. // deleted with the files...
  480. //
  481. CString filename = find_info.cFileName;
  482. file_list.AddTail (filename);
  483. }
  484. }
  485. // Close the search handle
  486. if (hfind != NULL) {
  487. ::FindClose (hfind);
  488. }
  489. //
  490. // Now loop through all the files and delete them
  491. //
  492. for (POSITION pos = file_list.GetHeadPosition (); pos != NULL; ) {
  493. CString &filename = file_list.GetNext (pos);
  494. CString full_path = local_dir + CString ("\\") + filename;
  495. if (::Delete_File (full_path) == FALSE) {
  496. CString message;
  497. message.Format ("Cannot delete %s. This may result in an incomplete update. Please make sure no applications are running before running the update.", full_path);
  498. ::MessageBox (NULL, message, "Delete Error", MB_SETFOREGROUND | MB_TOPMOST | MB_ICONEXCLAMATION | MB_OK);
  499. retval = false;
  500. }
  501. }
  502. return retval;
  503. }
  504. //////////////////////////////////////////////////////////////////////////////////
  505. //
  506. // Create_Dir_If_Necessary
  507. //
  508. //////////////////////////////////////////////////////////////////////////////////
  509. void
  510. Create_Dir_If_Necessary (LPCTSTR path)
  511. {
  512. if (::GetFileAttributes (path) == 0xFFFFFFFF) {
  513. Create_Dir_If_Necessary (::Strip_Filename_From_Path (path));
  514. ::CreateDirectory (path, NULL);
  515. }
  516. return ;
  517. }
  518. //////////////////////////////////////////////////////////////////////////////////
  519. //
  520. // Build_File_List
  521. //
  522. //////////////////////////////////////////////////////////////////////////////////
  523. void
  524. Build_File_List (LPCTSTR search_path, CStringList &file_list)
  525. {
  526. //
  527. // Loop through all the files in this directory and add them
  528. // to our list
  529. //
  530. BOOL keep_going = TRUE;
  531. WIN32_FIND_DATA find_info = { 0 };
  532. for (HANDLE hfind = ::FindFirstFile (search_path, &find_info);
  533. (hfind != INVALID_HANDLE_VALUE) && keep_going;
  534. keep_going = ::FindNextFile (hfind, &find_info))
  535. {
  536. //
  537. // If this file isn't a directory, add it to the list
  538. //
  539. if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  540. CString filename = find_info.cFileName;
  541. file_list.AddTail (filename);
  542. }
  543. }
  544. // Close the search handle
  545. if (hfind != NULL) {
  546. ::FindClose (hfind);
  547. }
  548. return ;
  549. }
  550. ////////////////////////////////////////////////////////////////////////////
  551. //
  552. // Quick_Compare_Files
  553. //
  554. ////////////////////////////////////////////////////////////////////////////
  555. int
  556. Quick_Compare_Files
  557. (
  558. LPCTSTR file1,
  559. LPCTSTR file2
  560. )
  561. {
  562. // Assume they are the same
  563. int compare = 0;
  564. // Params OK?
  565. ASSERT (file1 != NULL);
  566. ASSERT (file2 != NULL);
  567. if ((file1 != NULL) && (file2 != NULL)) {
  568. // Attempt to open file1
  569. HANDLE hfile1 = ::CreateFile (file1,
  570. 0,
  571. 0,
  572. NULL,
  573. OPEN_EXISTING,
  574. 0L,
  575. NULL);
  576. // Attempt to open file2
  577. HANDLE hfile2 = ::CreateFile (file2,
  578. 0,
  579. 0,
  580. NULL,
  581. OPEN_EXISTING,
  582. 0L,
  583. NULL);
  584. if ((hfile1 != INVALID_HANDLE_VALUE) && (hfile2 != INVALID_HANDLE_VALUE)) {
  585. // Get information about these 2 files.
  586. BY_HANDLE_FILE_INFORMATION file1_info = { 0 };
  587. BY_HANDLE_FILE_INFORMATION file2_info = { 0 };
  588. ::GetFileInformationByHandle (hfile1, &file1_info);
  589. ::GetFileInformationByHandle (hfile2, &file2_info);
  590. // Compare the time these files were last written to
  591. compare = ::CompareFileTime (&file1_info.ftLastWriteTime, &file2_info.ftLastWriteTime);
  592. } else if ((hfile1 == INVALID_HANDLE_VALUE) && (hfile2 != INVALID_HANDLE_VALUE)) {
  593. compare = -1;
  594. } else if ((hfile1 != INVALID_HANDLE_VALUE) && (hfile2 == INVALID_HANDLE_VALUE)) {
  595. compare = 1;
  596. }
  597. if (hfile1 != INVALID_HANDLE_VALUE) {
  598. ::CloseHandle (hfile1);
  599. }
  600. if (hfile2 != INVALID_HANDLE_VALUE) {
  601. ::CloseHandle (hfile2);
  602. }
  603. }
  604. // Same as strcmp, -1 if file1 is older than file2, 0 if equal, 1 if file1 is newer than file2
  605. return compare;
  606. }
  607. //////////////////////////////////////////////////////////////////////////////////
  608. //
  609. // FileNeedsCopying
  610. //
  611. //////////////////////////////////////////////////////////////////////////////////
  612. bool
  613. FileNeedsCopying (LPCTSTR src_path, LPCTSTR dest_path)
  614. {
  615. return (Quick_Compare_Files (src_path, dest_path) != 0);
  616. }
  617. //////////////////////////////////////////////////////////////////////////////////
  618. //
  619. // Update_App_Directory
  620. //
  621. //////////////////////////////////////////////////////////////////////////////////
  622. bool
  623. Update_App_Directory
  624. (
  625. FileCopyDialogClass * dialog,
  626. LPCTSTR src_dir,
  627. LPCTSTR dest_dir,
  628. bool is_recursive
  629. )
  630. {
  631. bool retval = true;
  632. //
  633. // Build search masks from the src and dest directories
  634. //
  635. CString remote_search_mask = src_dir;
  636. remote_search_mask += "\\*.*";
  637. //
  638. // Loop through all the files in this directory and add them
  639. // to our list
  640. //
  641. BOOL keep_going = TRUE;
  642. WIN32_FIND_DATA find_info = { 0 };
  643. CStringList file_list;
  644. for (HANDLE hfind = ::FindFirstFile (remote_search_mask, &find_info);
  645. (hfind != INVALID_HANDLE_VALUE) && keep_going && retval;
  646. keep_going = ::FindNextFile (hfind, &find_info))
  647. {
  648. //
  649. // If this file isn't a directory, add it to the list
  650. //
  651. if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  652. CString filename = find_info.cFileName;
  653. file_list.AddTail (filename);
  654. } else if (is_recursive && find_info.cFileName[0] != '.') {
  655. //
  656. // Recurse into this subdirectory
  657. //
  658. CString full_src_path = src_dir;
  659. CString full_dest_path = dest_dir;
  660. ::Delimit_Path (full_src_path);
  661. ::Delimit_Path (full_dest_path);
  662. full_src_path += find_info.cFileName;
  663. full_dest_path += find_info.cFileName;
  664. retval &= ::Update_App_Directory ( dialog,
  665. full_src_path,
  666. full_dest_path,
  667. is_recursive);
  668. }
  669. }
  670. // Close the search handle
  671. if (hfind != NULL) {
  672. ::FindClose (hfind);
  673. }
  674. //
  675. // Now loop through all the files and copy them to the src
  676. //
  677. for (POSITION pos = file_list.GetHeadPosition (); pos != NULL && retval; ) {
  678. CString &filename = file_list.GetNext (pos);
  679. CString src_file = src_dir + CString ("\\") + filename;
  680. CString dest_file = dest_dir + CString ("\\") + filename;
  681. //
  682. // Make sure the destination directory exists
  683. //
  684. ::Create_Dir_If_Necessary (dest_dir);
  685. //
  686. // Do a file comparison to determine whether or not to copy the file
  687. //
  688. if (::FileNeedsCopying (src_file, dest_file)) {
  689. //
  690. // Update the dialog
  691. //
  692. dialog->Set_Current_File (filename);
  693. //
  694. // Copy the file
  695. //
  696. if (::Copy_File (src_file, dest_file, true) == FALSE) {
  697. CString message;
  698. message.Format ("Cannot copy %s to %s. Please make sure no applications are running before running the update.", src_file, dest_file);
  699. ::MessageBox (NULL, message, "Copy Error", MB_SETFOREGROUND | MB_TOPMOST | MB_ICONEXCLAMATION | MB_OK);
  700. retval = false;
  701. }
  702. }
  703. }
  704. return retval;
  705. }
  706. //////////////////////////////////////////////////////////////////////////////////
  707. //
  708. // fnUpdateAppDirectory
  709. //
  710. //////////////////////////////////////////////////////////////////////////////////
  711. UINT
  712. fnUpdateAppDirectory (LPVOID pParam)
  713. {
  714. UPDATE_INFO *info = (UPDATE_INFO *)pParam;
  715. ASSERT (info != NULL);
  716. if (info != NULL) {
  717. //
  718. // Begin updating this directory...
  719. //
  720. info->result = ::Update_App_Directory ( info->dialog,
  721. info->src_dir,
  722. info->dest_dir,
  723. info->is_recursive);
  724. //
  725. // Kill the updating dialog
  726. //
  727. ::PostMessage (info->dialog->m_hWnd, WM_USER + 101, 0, 0L);
  728. }
  729. return 1;
  730. }
  731. //////////////////////////////////////////////////////////////////////////////////
  732. //
  733. // Update_App
  734. //
  735. //////////////////////////////////////////////////////////////////////////////////
  736. bool
  737. Update_App
  738. (
  739. CWnd * parent_wnd,
  740. LPCTSTR title,
  741. LPCTSTR src_dir,
  742. LPCTSTR dest_dir,
  743. bool is_recursive
  744. )
  745. {
  746. CWaitCursor wait_cursor;
  747. FileCopyDialogClass dialog (parent_wnd);
  748. dialog.Set_Current_Application (title);
  749. //
  750. // Kick off a worker thread to do the actual file copy
  751. //
  752. UPDATE_INFO *info = new UPDATE_INFO;
  753. info->dialog = &dialog;
  754. info->src_dir = src_dir;
  755. info->dest_dir = dest_dir;
  756. info->is_recursive = is_recursive;
  757. ::AfxBeginThread (fnUpdateAppDirectory, (LPVOID)info);
  758. //
  759. // Display a dialog to monitor the progress
  760. //
  761. dialog.DoModal ();
  762. bool retval = info->result;
  763. delete info;
  764. return retval;
  765. }
  766. //////////////////////////////////////////////////////////////////////////////////
  767. //
  768. // OnCommand
  769. //
  770. //////////////////////////////////////////////////////////////////////////////////
  771. BOOL
  772. CCommandoUpdateDlg::OnCommand
  773. (
  774. WPARAM wParam,
  775. LPARAM lParam
  776. )
  777. {
  778. bool found = false;
  779. for (int index = 0; (index < APP_MAX) && (found == false); index ++) {
  780. const APP_INFO &app_info = APPLICATIONS[index];
  781. if (LOWORD (wParam) == app_info.ctrl_id) {
  782. bool enable = (SendDlgItemMessage (LOWORD(wParam), BM_GETCHECK) == 1);
  783. if (index == APP_GAME_ENGINE) {
  784. enable |= (SendDlgItemMessage (APPLICATIONS[APP_GAME_LEVELS].ctrl_id, BM_GETCHECK) == 1);
  785. enable |= (SendDlgItemMessage (APPLICATIONS[APP_GAME_MOVIES].ctrl_id, BM_GETCHECK) == 1);
  786. } else if (index == APP_GAME_LEVELS || index == APP_GAME_MOVIES) {
  787. enable |= (SendDlgItemMessage (APPLICATIONS[APP_GAME_ENGINE].ctrl_id, BM_GETCHECK) == 1);
  788. }
  789. //
  790. // Enable or disable the clean and directory buttons
  791. //
  792. ::EnableWindow (::GetDlgItem (m_hWnd, app_info.clean_ctrl_id), enable);
  793. ::EnableWindow (::GetDlgItem (m_hWnd, app_info.dir_ctrl_id), enable);
  794. } else if (LOWORD (wParam) == app_info.dir_ctrl_id) {
  795. //
  796. // Change the installation directoy
  797. //
  798. CString directory;
  799. CString dlg_title;
  800. dlg_title.Format ("Select a directory to install the %s to.", app_info.title);
  801. if (Get_Install_Directory (m_hWnd, dlg_title, directory)) {
  802. SetDlgItemText (app_info.dir_ctrl_id, directory);
  803. }
  804. found = true;
  805. }
  806. }
  807. return CDialog::OnCommand(wParam, lParam);
  808. }
  809. //////////////////////////////////////////////////////////////////////////////////
  810. //
  811. // OnDefaults
  812. //
  813. //////////////////////////////////////////////////////////////////////////////////
  814. void
  815. CCommandoUpdateDlg::OnDefaults (void)
  816. {
  817. for (int index = 0; index < APP_MAX; index ++) {
  818. const APP_INFO &app_info = APPLICATIONS[index];
  819. if (SendDlgItemMessage (app_info.ctrl_id, BM_GETCHECK) != 0) {
  820. SetDlgItemText (app_info.dir_ctrl_id, app_info.default_dir);
  821. }
  822. }
  823. return ;
  824. }