974 lines
27 KiB
C++
974 lines
27 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// CommandoUpdateDlg.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "CommandoUpdate.h"
|
|
#include "CommandoUpdateDlg.h"
|
|
#include "FileCopyDialog.H"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Data Tables
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
typedef struct
|
|
{
|
|
FileCopyDialogClass *dialog;
|
|
CString src_dir;
|
|
CString dest_dir;
|
|
bool is_recursive;
|
|
bool result;
|
|
} UPDATE_INFO;
|
|
|
|
typedef struct
|
|
{
|
|
LPCTSTR title;
|
|
LPCTSTR src;
|
|
LPCTSTR default_dir;
|
|
LPCTSTR reg_key;
|
|
UINT ctrl_id;
|
|
UINT clean_ctrl_id;
|
|
UINT dir_ctrl_id;
|
|
bool clean_default;
|
|
bool is_recursive;
|
|
} APP_INFO;
|
|
|
|
typedef enum
|
|
{
|
|
APP_EDITOR = 0,
|
|
APP_GAME_ENGINE,
|
|
APP_GAME_LEVELS,
|
|
APP_GAME_MOVIES,
|
|
APP_LIGHTMAP,
|
|
APP_VISFARM,
|
|
APP_MIXVIEW,
|
|
APP_MULTIPLAY,
|
|
APP_MAX,
|
|
} APPLICATION_IDS;
|
|
|
|
const APP_INFO APPLICATIONS[APP_MAX] =
|
|
{
|
|
{ "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 },
|
|
{ "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 },
|
|
{ "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 },
|
|
{ "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 },
|
|
{ "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 },
|
|
{ "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 },
|
|
{ "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 },
|
|
{ "Multiplay", "\\\\tanya\\game\\projects\\renegade\\run", "c:\\renegade\\mplay", "MPlay\\Install", IDC_MULTIPLAY_CHECK, IDC_MULTIPLAY_CLEAN_CHECK, IDC_MULTIPLAY_DIR_BUTTON, false, true }
|
|
};
|
|
|
|
|
|
const char * const INSTALL_REG_VALUE = "Path";
|
|
const char * const DATA_SUB_DIR = "\\Data";
|
|
const char * const MOVIE_SUB_DIR = "\\Data\\Movies";
|
|
const char * const SRDLL_SUB_DIR = "\\SrDLL";
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Local prototypes
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
bool Update_App (CWnd *parent_wnd, LPCTSTR title, LPCTSTR src_dir, LPCTSTR dest_dir, bool is_recursive);
|
|
bool Clean_Directory (LPCTSTR local_dir, bool is_recursive);
|
|
bool Get_Install_Directory (HWND hparent_wnd, LPCTSTR title, CString &folder);
|
|
bool Delete_File (LPCTSTR filename);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CCommandoUpdateDlg dialog
|
|
|
|
CCommandoUpdateDlg::CCommandoUpdateDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CCommandoUpdateDlg::IDD, pParent)
|
|
{
|
|
//{{AFX_DATA_INIT(CCommandoUpdateDlg)
|
|
// NOTE: the ClassWizard will add member initialization here
|
|
//}}AFX_DATA_INIT
|
|
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
}
|
|
|
|
void CCommandoUpdateDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(CCommandoUpdateDlg)
|
|
// NOTE: the ClassWizard will add DDX and DDV calls here
|
|
//}}AFX_DATA_MAP
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CCommandoUpdateDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CCommandoUpdateDlg)
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_BN_CLICKED(IDC_DEFAULTS, OnDefaults)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CCommandoUpdateDlg message handlers
|
|
|
|
BOOL CCommandoUpdateDlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
|
|
//
|
|
// Check/uncheck the applications by default
|
|
//
|
|
for (int index = 0; index < APP_MAX; index ++) {
|
|
const APP_INFO &app_info = APPLICATIONS[index];
|
|
HKEY hreg_key = NULL;
|
|
CString reg_key_name;
|
|
reg_key_name.Format ("Software\\Westwood Studios\\%s", app_info.reg_key);
|
|
|
|
TCHAR path[MAX_PATH] = { 0 };
|
|
|
|
|
|
//
|
|
// Is this application installed?
|
|
//
|
|
if (::RegOpenKeyEx (HKEY_CURRENT_USER, reg_key_name, 0L, KEY_READ, &hreg_key) == ERROR_SUCCESS) {
|
|
SendDlgItemMessage (app_info.ctrl_id, BM_SETCHECK, (WPARAM)TRUE);
|
|
|
|
//
|
|
// Read the installation directory from the registry
|
|
//
|
|
DWORD size = sizeof (path);
|
|
::RegQueryValueEx (hreg_key, INSTALL_REG_VALUE, 0L, NULL, (BYTE *)path, &size);
|
|
::RegCloseKey (hreg_key);
|
|
} else {
|
|
::EnableWindow (::GetDlgItem (m_hWnd, app_info.clean_ctrl_id), false);
|
|
::EnableWindow (::GetDlgItem (m_hWnd, app_info.dir_ctrl_id), false);
|
|
}
|
|
|
|
//
|
|
// Set the text of the install directory buttons
|
|
//
|
|
if (path[0] == 0) {
|
|
::lstrcpy (path, app_info.default_dir);
|
|
}
|
|
SetDlgItemText (app_info.dir_ctrl_id, path);
|
|
|
|
//
|
|
// Check the clean option (by default) if necessary
|
|
//
|
|
if (app_info.clean_default) {
|
|
SendDlgItemMessage (app_info.clean_ctrl_id, BM_SETCHECK, (WPARAM)TRUE);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CCommandoUpdateDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this to obtain the cursor to display while the user drags
|
|
// the minimized window.
|
|
HCURSOR CCommandoUpdateDlg::OnQueryDragIcon()
|
|
{
|
|
return (HCURSOR) m_hIcon;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnOK
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CCommandoUpdateDlg::OnOK (void)
|
|
{
|
|
//
|
|
// Install/upgrade each application
|
|
//
|
|
bool success = true;
|
|
for (int index = 0; (index < APP_MAX) && success; index ++) {
|
|
const APP_INFO &app_info = APPLICATIONS[index];
|
|
|
|
//
|
|
// Does the user want to install this application?
|
|
//
|
|
if (SendDlgItemMessage (app_info.ctrl_id, BM_GETCHECK) == 1) {
|
|
HKEY hreg_key = NULL;
|
|
CString reg_key_name;
|
|
reg_key_name.Format ("Software\\Westwood Studios\\%s", app_info.reg_key);
|
|
|
|
//
|
|
// Get the path where this application is installed
|
|
//
|
|
CString local_path;
|
|
GetDlgItemText (app_info.dir_ctrl_id, local_path);
|
|
|
|
// Ensure the path doesn't contain a terminating delimiter
|
|
if (local_path[::lstrlen (local_path) - 1] == '\\') {
|
|
local_path.Delete (::lstrlen (local_path) - 1, 1);
|
|
}
|
|
|
|
if (success) {
|
|
CWaitCursor wait_cursor;
|
|
|
|
//
|
|
// Clean the old version if necessary
|
|
//
|
|
if (SendDlgItemMessage (app_info.clean_ctrl_id, BM_GETCHECK) == 1) {
|
|
|
|
//
|
|
// Special case the "game engine only" option. Note: we only want
|
|
// to clean the data subdir if we aren't cleaning the game directory
|
|
//
|
|
if (index == APP_GAME_LEVELS || index == APP_GAME_MOVIES) {
|
|
if (SendDlgItemMessage (APPLICATIONS[APP_GAME_ENGINE].ctrl_id, BM_GETCHECK) == 0) {
|
|
|
|
if (index == APP_GAME_MOVIES) {
|
|
CString data_dir = local_path + MOVIE_SUB_DIR;
|
|
::Clean_Directory (data_dir, app_info.is_recursive);
|
|
} else {
|
|
CString data_dir = local_path + DATA_SUB_DIR;
|
|
::Clean_Directory (data_dir, app_info.is_recursive);
|
|
}
|
|
}
|
|
} else {
|
|
::Clean_Directory (local_path, app_info.is_recursive);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Special case the "game levels" option
|
|
//
|
|
if (index == APP_GAME_LEVELS) {
|
|
CString data_dir = local_path + DATA_SUB_DIR;
|
|
CString src_data_dir = app_info.src;
|
|
CString data_title = "Game Levels";
|
|
success &= ::Update_App (this, data_title, src_data_dir, data_dir, app_info.is_recursive);
|
|
} else if (index == APP_GAME_MOVIES) {
|
|
CString data_dir = local_path + MOVIE_SUB_DIR;
|
|
CString src_data_dir = app_info.src;
|
|
CString data_title = "Game Movies";
|
|
success &= ::Update_App (this, data_title, src_data_dir, data_dir, app_info.is_recursive);
|
|
} else {
|
|
|
|
//
|
|
// Update the application's files
|
|
//
|
|
success &= ::Update_App (this, app_info.title, app_info.src, local_path, app_info.is_recursive);
|
|
}
|
|
|
|
//
|
|
// Write the app's installation dir to the registry if we
|
|
// installed it correctly.
|
|
//
|
|
if (success) {
|
|
success &= (::RegCreateKeyEx ( HKEY_CURRENT_USER,
|
|
reg_key_name,
|
|
0L,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hreg_key,
|
|
NULL) == ERROR_SUCCESS);
|
|
if (success) {
|
|
::RegSetValueEx ( hreg_key,
|
|
INSTALL_REG_VALUE,
|
|
0L,
|
|
REG_SZ,
|
|
(BYTE *)(LPCTSTR)local_path,
|
|
local_path.GetLength () + 1);
|
|
::RegCloseKey (hreg_key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CDialog::OnOK ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get_Install_Directory
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Get_Install_Directory (HWND hparent_wnd, LPCTSTR title, CString &folder)
|
|
{
|
|
bool retval = false;
|
|
|
|
// Browse for the folder
|
|
BROWSEINFO browse_info = { 0 };
|
|
browse_info.hwndOwner = hparent_wnd;
|
|
browse_info.lpszTitle = title;
|
|
browse_info.ulFlags = BIF_RETURNONLYFSDIRS;
|
|
LPITEMIDLIST pidl = ::SHBrowseForFolder (&browse_info);
|
|
if (pidl != NULL) {
|
|
|
|
// Convert the 'PIDL' into a string
|
|
char path[MAX_PATH];
|
|
retval = (::SHGetPathFromIDList (pidl, path) == TRUE);
|
|
if (retval) {
|
|
folder = path;
|
|
}
|
|
|
|
// Free the 'PIDL'
|
|
LPMALLOC pmalloc = NULL;
|
|
if (SUCCEEDED (::SHGetMalloc (&pmalloc))) {
|
|
pmalloc->Free (pidl);
|
|
pmalloc->Release ();
|
|
}
|
|
}
|
|
|
|
|
|
//CFileDialog dialog (TRUE, ".pth", "test.pth", OFN_PATHMUSTEXIST | OFN_EXPLORER, "files *.*|*.*||", CWnd::FromHandle(hparent_wnd));
|
|
//CFileDialog dialog (TRUE, ".pth", "test.pth", OFN_PATHMUSTEXIST | OFN_EXPLORER | OFN_ENABLETEMPLATEHANDLE, "files *.*|*.*||", CWnd::FromHandle(hparent_wnd));
|
|
//dialog.m_ofn.lpTemplateName = MAKEINTRESOURCE (IDD_DIR_SELECT_DIALOG);
|
|
//dialog.m_ofn.hInstance = ::AfxGetInstanceHandle ();
|
|
//HRSRC resource = ::FindResource (::AfxGetInstanceHandle (), MAKEINTRESOURCE (IDD_DIR_SELECT_DIALOG), RT_DIALOG);
|
|
//dialog.m_ofn.hInstance = (HINSTANCE)::LoadResource (::AfxGetInstanceHandle (), resource);
|
|
|
|
//dialog.DoModal ();
|
|
/*TCHAR path[MAX_PATH] = { 0 };
|
|
|
|
OPENFILENAME ofn = { 0 };
|
|
ofn.lStructSize = sizeof (ofn);
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_ENABLETEMPLATE;
|
|
ofn.hInstance = ::AfxGetInstanceHandle ();
|
|
ofn.lpTemplateName = MAKEINTRESOURCE (IDD_DIR_SELECT_DIALOG);
|
|
ofn.hwndOwner = hparent_wnd;
|
|
ofn.lpstrFile = path;
|
|
ofn.nMaxFile = sizeof (path);
|
|
GetOpenFileName (&ofn);
|
|
::CommDlgExtendedError ();*/
|
|
//retval = (dialog.DoModal () == IDOK);
|
|
|
|
// Return the true/false result code
|
|
return retval;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Strip_Filename_From_Path
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
CString
|
|
Strip_Filename_From_Path (LPCTSTR path)
|
|
{
|
|
// Copy the path to a buffer we can modify
|
|
TCHAR temp_path[MAX_PATH];
|
|
::lstrcpy (temp_path, path);
|
|
|
|
// Find the last occurance of the directory deliminator
|
|
LPTSTR filename = ::strrchr (temp_path, '\\');
|
|
if (filename != NULL) {
|
|
// Strip off the filename
|
|
filename[0] = 0;
|
|
}
|
|
|
|
// Return the path only
|
|
return CString (temp_path);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Delete_File
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Delete_File (LPCTSTR filename)
|
|
{
|
|
// Assume failure
|
|
bool retval = false;
|
|
|
|
ASSERT (filename != NULL);
|
|
if (filename != NULL) {
|
|
|
|
// Strip the readonly bit off if necessary
|
|
DWORD attributes = ::GetFileAttributes (filename);
|
|
if ((attributes != 0xFFFFFFFF) &&
|
|
((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
::SetFileAttributes (filename, attributes & (~FILE_ATTRIBUTE_READONLY));
|
|
}
|
|
|
|
// Perform the delete operation!
|
|
if ((attributes != 0xFFFFFFFF) &&
|
|
((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
retval = ::RemoveDirectory (filename) == TRUE;
|
|
} else {
|
|
retval = ::DeleteFile (filename) == TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
// Return the true/false result code
|
|
return retval;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copy_File
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Copy_File
|
|
(
|
|
LPCTSTR existing_filename,
|
|
LPCTSTR new_filename,
|
|
bool bforce_copy
|
|
)
|
|
{
|
|
// Assume failure
|
|
bool retval = false;
|
|
|
|
ASSERT (existing_filename != NULL);
|
|
ASSERT (new_filename != NULL);
|
|
if ((existing_filename != NULL) &&
|
|
(new_filename != NULL)) {
|
|
|
|
// Make sure we aren't copying over ourselves
|
|
bool allow_copy = (::lstrcmpi (existing_filename, new_filename) != 0);
|
|
|
|
// Strip the readonly bit off if necessary
|
|
DWORD attributes = ::GetFileAttributes (new_filename);
|
|
if (allow_copy &&
|
|
(attributes != 0xFFFFFFFF) &&
|
|
((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)) {
|
|
if (bforce_copy) {
|
|
::SetFileAttributes (new_filename, attributes & (~FILE_ATTRIBUTE_READONLY));
|
|
} else {
|
|
allow_copy = false;
|
|
}
|
|
}
|
|
|
|
// Perform the copy operation!
|
|
if (allow_copy) {
|
|
retval = (::CopyFile (existing_filename, new_filename, FALSE) == TRUE);
|
|
}
|
|
}
|
|
|
|
// Return the true/false result code
|
|
return retval;
|
|
}
|
|
|
|
|
|
__inline void Delimit_Path (CString &path)
|
|
{
|
|
if (path[::lstrlen (path) - 1] != '\\') {
|
|
path += CString ("\\");
|
|
}
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Clean_Directory
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Clean_Directory (LPCTSTR local_dir, bool is_recursive)
|
|
{
|
|
bool retval = true;
|
|
|
|
// Build a search mask from the directory
|
|
CString search_mask = CString (local_dir) + "\\*.*";
|
|
|
|
// Loop through all the files in this directory and add them
|
|
// to our list
|
|
CStringList file_list;
|
|
BOOL bcontinue = TRUE;
|
|
WIN32_FIND_DATA find_info = { 0 };
|
|
for (HANDLE hfind = ::FindFirstFile (search_mask, &find_info);
|
|
(hfind != INVALID_HANDLE_VALUE) && bcontinue;
|
|
bcontinue = ::FindNextFile (hfind, &find_info)) {
|
|
|
|
// If this file isn't a directory, add it to the list
|
|
if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
CString filename = find_info.cFileName;
|
|
file_list.AddTail (filename);
|
|
} else if (is_recursive && find_info.cFileName[0] != '.') {
|
|
|
|
//
|
|
// Recurse into this subdirectory
|
|
//
|
|
CString full_path = local_dir;
|
|
::Delimit_Path (full_path);
|
|
full_path += find_info.cFileName;
|
|
Clean_Directory (full_path, is_recursive);
|
|
|
|
//
|
|
// Add this directory to the list so it will get
|
|
// deleted with the files...
|
|
//
|
|
CString filename = find_info.cFileName;
|
|
file_list.AddTail (filename);
|
|
}
|
|
}
|
|
|
|
// Close the search handle
|
|
if (hfind != NULL) {
|
|
::FindClose (hfind);
|
|
}
|
|
|
|
//
|
|
// Now loop through all the files and delete them
|
|
//
|
|
for (POSITION pos = file_list.GetHeadPosition (); pos != NULL; ) {
|
|
CString &filename = file_list.GetNext (pos);
|
|
CString full_path = local_dir + CString ("\\") + filename;
|
|
if (::Delete_File (full_path) == FALSE) {
|
|
CString message;
|
|
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);
|
|
::MessageBox (NULL, message, "Delete Error", MB_SETFOREGROUND | MB_TOPMOST | MB_ICONEXCLAMATION | MB_OK);
|
|
retval = false;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create_Dir_If_Necessary
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
Create_Dir_If_Necessary (LPCTSTR path)
|
|
{
|
|
if (::GetFileAttributes (path) == 0xFFFFFFFF) {
|
|
Create_Dir_If_Necessary (::Strip_Filename_From_Path (path));
|
|
::CreateDirectory (path, NULL);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Build_File_List
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
Build_File_List (LPCTSTR search_path, CStringList &file_list)
|
|
{
|
|
//
|
|
// Loop through all the files in this directory and add them
|
|
// to our list
|
|
//
|
|
BOOL keep_going = TRUE;
|
|
WIN32_FIND_DATA find_info = { 0 };
|
|
|
|
for (HANDLE hfind = ::FindFirstFile (search_path, &find_info);
|
|
(hfind != INVALID_HANDLE_VALUE) && keep_going;
|
|
keep_going = ::FindNextFile (hfind, &find_info))
|
|
{
|
|
//
|
|
// If this file isn't a directory, add it to the list
|
|
//
|
|
if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
CString filename = find_info.cFileName;
|
|
file_list.AddTail (filename);
|
|
}
|
|
}
|
|
|
|
// Close the search handle
|
|
if (hfind != NULL) {
|
|
::FindClose (hfind);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Quick_Compare_Files
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
Quick_Compare_Files
|
|
(
|
|
LPCTSTR file1,
|
|
LPCTSTR file2
|
|
)
|
|
{
|
|
// Assume they are the same
|
|
int compare = 0;
|
|
|
|
// Params OK?
|
|
ASSERT (file1 != NULL);
|
|
ASSERT (file2 != NULL);
|
|
if ((file1 != NULL) && (file2 != NULL)) {
|
|
|
|
// Attempt to open file1
|
|
HANDLE hfile1 = ::CreateFile (file1,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0L,
|
|
NULL);
|
|
|
|
// Attempt to open file2
|
|
HANDLE hfile2 = ::CreateFile (file2,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0L,
|
|
NULL);
|
|
|
|
if ((hfile1 != INVALID_HANDLE_VALUE) && (hfile2 != INVALID_HANDLE_VALUE)) {
|
|
|
|
// Get information about these 2 files.
|
|
BY_HANDLE_FILE_INFORMATION file1_info = { 0 };
|
|
BY_HANDLE_FILE_INFORMATION file2_info = { 0 };
|
|
::GetFileInformationByHandle (hfile1, &file1_info);
|
|
::GetFileInformationByHandle (hfile2, &file2_info);
|
|
|
|
// Compare the time these files were last written to
|
|
compare = ::CompareFileTime (&file1_info.ftLastWriteTime, &file2_info.ftLastWriteTime);
|
|
} else if ((hfile1 == INVALID_HANDLE_VALUE) && (hfile2 != INVALID_HANDLE_VALUE)) {
|
|
compare = -1;
|
|
} else if ((hfile1 != INVALID_HANDLE_VALUE) && (hfile2 == INVALID_HANDLE_VALUE)) {
|
|
compare = 1;
|
|
}
|
|
|
|
if (hfile1 != INVALID_HANDLE_VALUE) {
|
|
::CloseHandle (hfile1);
|
|
}
|
|
|
|
if (hfile2 != INVALID_HANDLE_VALUE) {
|
|
::CloseHandle (hfile2);
|
|
}
|
|
}
|
|
|
|
// Same as strcmp, -1 if file1 is older than file2, 0 if equal, 1 if file1 is newer than file2
|
|
return compare;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FileNeedsCopying
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
FileNeedsCopying (LPCTSTR src_path, LPCTSTR dest_path)
|
|
{
|
|
return (Quick_Compare_Files (src_path, dest_path) != 0);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_App_Directory
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Update_App_Directory
|
|
(
|
|
FileCopyDialogClass * dialog,
|
|
LPCTSTR src_dir,
|
|
LPCTSTR dest_dir,
|
|
bool is_recursive
|
|
)
|
|
{
|
|
bool retval = true;
|
|
|
|
//
|
|
// Build search masks from the src and dest directories
|
|
//
|
|
CString remote_search_mask = src_dir;
|
|
remote_search_mask += "\\*.*";
|
|
|
|
//
|
|
// Loop through all the files in this directory and add them
|
|
// to our list
|
|
//
|
|
BOOL keep_going = TRUE;
|
|
WIN32_FIND_DATA find_info = { 0 };
|
|
CStringList file_list;
|
|
|
|
for (HANDLE hfind = ::FindFirstFile (remote_search_mask, &find_info);
|
|
(hfind != INVALID_HANDLE_VALUE) && keep_going && retval;
|
|
keep_going = ::FindNextFile (hfind, &find_info))
|
|
{
|
|
//
|
|
// If this file isn't a directory, add it to the list
|
|
//
|
|
if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
CString filename = find_info.cFileName;
|
|
file_list.AddTail (filename);
|
|
} else if (is_recursive && find_info.cFileName[0] != '.') {
|
|
|
|
//
|
|
// Recurse into this subdirectory
|
|
//
|
|
CString full_src_path = src_dir;
|
|
CString full_dest_path = dest_dir;
|
|
::Delimit_Path (full_src_path);
|
|
::Delimit_Path (full_dest_path);
|
|
full_src_path += find_info.cFileName;
|
|
full_dest_path += find_info.cFileName;
|
|
|
|
retval &= ::Update_App_Directory ( dialog,
|
|
full_src_path,
|
|
full_dest_path,
|
|
is_recursive);
|
|
}
|
|
}
|
|
|
|
// Close the search handle
|
|
if (hfind != NULL) {
|
|
::FindClose (hfind);
|
|
}
|
|
|
|
//
|
|
// Now loop through all the files and copy them to the src
|
|
//
|
|
for (POSITION pos = file_list.GetHeadPosition (); pos != NULL && retval; ) {
|
|
|
|
CString &filename = file_list.GetNext (pos);
|
|
|
|
CString src_file = src_dir + CString ("\\") + filename;
|
|
CString dest_file = dest_dir + CString ("\\") + filename;
|
|
|
|
//
|
|
// Make sure the destination directory exists
|
|
//
|
|
::Create_Dir_If_Necessary (dest_dir);
|
|
|
|
//
|
|
// Do a file comparison to determine whether or not to copy the file
|
|
//
|
|
if (::FileNeedsCopying (src_file, dest_file)) {
|
|
|
|
//
|
|
// Update the dialog
|
|
//
|
|
dialog->Set_Current_File (filename);
|
|
|
|
//
|
|
// Copy the file
|
|
//
|
|
if (::Copy_File (src_file, dest_file, true) == FALSE) {
|
|
CString message;
|
|
message.Format ("Cannot copy %s to %s. Please make sure no applications are running before running the update.", src_file, dest_file);
|
|
::MessageBox (NULL, message, "Copy Error", MB_SETFOREGROUND | MB_TOPMOST | MB_ICONEXCLAMATION | MB_OK);
|
|
retval = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// fnUpdateAppDirectory
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
UINT
|
|
fnUpdateAppDirectory (LPVOID pParam)
|
|
{
|
|
UPDATE_INFO *info = (UPDATE_INFO *)pParam;
|
|
ASSERT (info != NULL);
|
|
if (info != NULL) {
|
|
|
|
//
|
|
// Begin updating this directory...
|
|
//
|
|
info->result = ::Update_App_Directory ( info->dialog,
|
|
info->src_dir,
|
|
info->dest_dir,
|
|
info->is_recursive);
|
|
|
|
//
|
|
// Kill the updating dialog
|
|
//
|
|
::PostMessage (info->dialog->m_hWnd, WM_USER + 101, 0, 0L);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Update_App
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
bool
|
|
Update_App
|
|
(
|
|
CWnd * parent_wnd,
|
|
LPCTSTR title,
|
|
LPCTSTR src_dir,
|
|
LPCTSTR dest_dir,
|
|
bool is_recursive
|
|
)
|
|
{
|
|
CWaitCursor wait_cursor;
|
|
|
|
FileCopyDialogClass dialog (parent_wnd);
|
|
dialog.Set_Current_Application (title);
|
|
|
|
//
|
|
// Kick off a worker thread to do the actual file copy
|
|
//
|
|
UPDATE_INFO *info = new UPDATE_INFO;
|
|
info->dialog = &dialog;
|
|
info->src_dir = src_dir;
|
|
info->dest_dir = dest_dir;
|
|
info->is_recursive = is_recursive;
|
|
::AfxBeginThread (fnUpdateAppDirectory, (LPVOID)info);
|
|
|
|
//
|
|
// Display a dialog to monitor the progress
|
|
//
|
|
dialog.DoModal ();
|
|
bool retval = info->result;
|
|
delete info;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnCommand
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
CCommandoUpdateDlg::OnCommand
|
|
(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
bool found = false;
|
|
for (int index = 0; (index < APP_MAX) && (found == false); index ++) {
|
|
const APP_INFO &app_info = APPLICATIONS[index];
|
|
|
|
if (LOWORD (wParam) == app_info.ctrl_id) {
|
|
|
|
bool enable = (SendDlgItemMessage (LOWORD(wParam), BM_GETCHECK) == 1);
|
|
|
|
if (index == APP_GAME_ENGINE) {
|
|
enable |= (SendDlgItemMessage (APPLICATIONS[APP_GAME_LEVELS].ctrl_id, BM_GETCHECK) == 1);
|
|
enable |= (SendDlgItemMessage (APPLICATIONS[APP_GAME_MOVIES].ctrl_id, BM_GETCHECK) == 1);
|
|
} else if (index == APP_GAME_LEVELS || index == APP_GAME_MOVIES) {
|
|
enable |= (SendDlgItemMessage (APPLICATIONS[APP_GAME_ENGINE].ctrl_id, BM_GETCHECK) == 1);
|
|
}
|
|
|
|
//
|
|
// Enable or disable the clean and directory buttons
|
|
//
|
|
::EnableWindow (::GetDlgItem (m_hWnd, app_info.clean_ctrl_id), enable);
|
|
::EnableWindow (::GetDlgItem (m_hWnd, app_info.dir_ctrl_id), enable);
|
|
|
|
} else if (LOWORD (wParam) == app_info.dir_ctrl_id) {
|
|
|
|
//
|
|
// Change the installation directoy
|
|
//
|
|
CString directory;
|
|
CString dlg_title;
|
|
dlg_title.Format ("Select a directory to install the %s to.", app_info.title);
|
|
if (Get_Install_Directory (m_hWnd, dlg_title, directory)) {
|
|
SetDlgItemText (app_info.dir_ctrl_id, directory);
|
|
}
|
|
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
return CDialog::OnCommand(wParam, lParam);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OnDefaults
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
CCommandoUpdateDlg::OnDefaults (void)
|
|
{
|
|
for (int index = 0; index < APP_MAX; index ++) {
|
|
const APP_INFO &app_info = APPLICATIONS[index];
|
|
|
|
if (SendDlgItemMessage (app_info.ctrl_id, BM_GETCHECK) != 0) {
|
|
SetDlgItemText (app_info.dir_ctrl_id, app_info.default_dir);
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|