//----------------------------------------------------------------------------
//
// Copyright (c) 1997-1999  Microsoft Corporation
// All rights reserved.
//
// File Name:
//      copyfile.c
//
// Description:
//      This file has the dlgproc for the copy files page.
//
//----------------------------------------------------------------------------

#include "pch.h"
#include "resource.h"

#define DAYS_IN_A_WEEK   7
#define MONTHS_IN_A_YEAR 12

//
// This struct and is used to pack the input args passed to the tree
// copy thread.
//

typedef struct {
    TCHAR lpSourceBuffer[MAX_PATH];
    TCHAR lpDestBuffer[MAX_PATH];
    HWND  hwnd;
} COPY_THREAD_PARAMS;

//
// String constants loaded from resource
//

static TCHAR *StrBuildingList;
static TCHAR *StrCopyingFiles;
static TCHAR *StrFileAlreadyExists;
static TCHAR *StrModified;
static TCHAR *StrBytes;

static TCHAR *StrJanuary;
static TCHAR *StrFebruary;
static TCHAR *StrMarch;
static TCHAR *StrApril;
static TCHAR *StrMay;
static TCHAR *StrJune;
static TCHAR *StrJuly;
static TCHAR *StrAugust;
static TCHAR *StrSeptember;
static TCHAR *StrOctober;
static TCHAR *StrNovember;
static TCHAR *StrDecember;

static TCHAR *StrSunday;
static TCHAR *StrMonday;
static TCHAR *StrTuesday;
static TCHAR *StrWednesday;
static TCHAR *StrThursday;
static TCHAR *StrFriday;
static TCHAR *StrSaturday;

static TCHAR *rgMonthsOfYear[MONTHS_IN_A_YEAR];
static TCHAR *rgDaysOfWeek[DAYS_IN_A_WEEK + 1];

//
// Messages for the dialog procedure
//

#define WMX_BEGINCOPYING (WM_USER+1)
#define WMX_FILECOPIED   (WM_USER+2)
#define WMX_ENDCOPYING   (WM_USER+3)

//
// Global counters
//

HDSKSPC ghDiskSpaceList;
int gnFilesCopied = 0;
int gnTotalFiles  = 0;

//
// Misc constants
//

#define ONE_MEG ( 1024 * 1024 )

//
//  Confirm File replace constants
//
#define YES       1
#define YESTOALL  2
#define NO        3
#define NOTOALL   4
#define CANCEL    5

#define MAX_DAY_OF_WEEK_LEN     64
#define MAX_MONTHS_OF_YEAR_LEN  64

static TCHAR g_szFileAlreadyExistsText[MAX_STRING_LEN] = _T("");
static TCHAR g_szSrcFileDate[MAX_STRING_LEN]  = _T("");
static TCHAR g_szDestFileDate[MAX_STRING_LEN] = _T("");
static TCHAR g_szSrcFileSize[MAX_STRING_LEN]  = _T("");
static TCHAR g_szDestFileSize[MAX_STRING_LEN] = _T("");
static BOOL  g_SetFocusYes;

//
//  Dialog proc that runs in the copying thread's context
//
INT_PTR CALLBACK
ConfirmFileReplaceDlgProc( IN HWND     hwnd,
                           IN UINT     uMsg,
                           IN WPARAM   wParam,
                           IN LPARAM   lParam);


//---------------------------------------------------------------------------
//
// This section of code runs in the context of a spawned thread.  We do the
// NT source copy work in a separate thread so that the dialog repaints
// and such.
//
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//
// Function: CountSpaceNeeded
//
// Purpose: Routine that walks a tree and counts how many files there are
//          and how much diskspace is needed at the dest drive.
//
// Returns: VOID
//
//---------------------------------------------------------------------------

VOID CountSpaceNeeded(HWND    hwnd,
                      LPTSTR  SrcRootPath,
                      LPTSTR  DestRootPath)
{
    LPTSTR SrcRootPathEnd  = SrcRootPath  + lstrlen(SrcRootPath);
    LPTSTR DestRootPathEnd = DestRootPath + lstrlen(DestRootPath);

    LONGLONG llFileSize;
    HANDLE   FindHandle;

    WIN32_FIND_DATA FindData;

    //
    // Look for * in this dir
    //
    if ( ! ConcatenatePaths(SrcRootPath, _T("*"), NULL) )
        return;

    FindHandle = FindFirstFile(SrcRootPath, &FindData);

    if ( FindHandle == INVALID_HANDLE_VALUE )
        return;

    do {

        *SrcRootPathEnd  = _T('\0');
        *DestRootPathEnd = _T('\0');

        if (lstrcmp(FindData.cFileName, _T(".") )  == 0 ||
            lstrcmp(FindData.cFileName, _T("..") ) == 0 )
            continue;

        if ( ! ConcatenatePaths(SrcRootPath,  FindData.cFileName, NULL) ||
             ! ConcatenatePaths(DestRootPath, FindData.cFileName, NULL) )
            continue;

        //
        // If a file, increment space and TotalFile counters else
        // recurse down
        //
        if ( ! (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {

            llFileSize = (((LONGLONG) FindData.nFileSizeHigh) << 32) |
                                      FindData.nFileSizeLow;

            SetupAddToDiskSpaceList(ghDiskSpaceList,
                                    DestRootPath,
                                    llFileSize,
                                    FILEOP_COPY,
                                    NULL,
                                    0);

            gnTotalFiles++;

        } else {

            CountSpaceNeeded(hwnd,
                             SrcRootPath,
                             DestRootPath);
        }

    } while ( FindNextFile(FindHandle, &FindData) );

    *SrcRootPathEnd  = _T('\0');
    *DestRootPathEnd = _T('\0');

    FindClose(FindHandle);
}

//---------------------------------------------------------------------------
//
//  Function: BuildTimeString
//
//  Purpose:
//
//  Arguments:
//
//  Returns:
//
//---------------------------------------------------------------------------
VOID
BuildTimeString( IN FILETIME *FileTime, OUT TCHAR *szTimeString, IN DWORD cbSize )
{

    // ISSUE-2002/02/28-stelo- should probably strip all of this low-level Time stuff out of here
    //  and put in supplib

    FILETIME   LocalTime;
    SYSTEMTIME LastWriteSystemTime;
    HRESULT hrPrintf;

    FileTimeToLocalFileTime( FileTime, &LocalTime);

    FileTimeToSystemTime( &LocalTime, &LastWriteSystemTime );

    hrPrintf=StringCchPrintf( szTimeString, cbSize,
              _T("%s: %s, %s %d, %d, %d:%.2d:%.2d"),
              StrModified,
              rgDaysOfWeek[LastWriteSystemTime.wDayOfWeek],
              rgMonthsOfYear[LastWriteSystemTime.wMonth-1],
              LastWriteSystemTime.wDay,
              LastWriteSystemTime.wYear,
              LastWriteSystemTime.wHour,
              LastWriteSystemTime.wMinute,
              LastWriteSystemTime.wSecond );

}

//---------------------------------------------------------------------------
//
//  Function: CheckIfCancel
//
//  Purpose: Ask user "You sure you want to cancel the file copy"?
//           And if user says YES, jump the wizard to the unsucessful
//           completion page.
//
//  Returns:
//      TRUE  - wizard is now canceled, quit copying files
//      FALSE - user wants to keep trying
//
//---------------------------------------------------------------------------

BOOL CheckIfCancel(HWND hwnd)
{
    UINT iRet;

    iRet = ReportErrorId(hwnd, MSGTYPE_YESNO, IDS_WARN_COPY_CANCEL);

    if ( iRet == IDYES ) {
        PostMessage(GetParent(hwnd),
                    PSM_SETCURSELID,
                    (WPARAM) 0,
                    (LPARAM) IDD_FINISH2);
        return TRUE;
    }

    return FALSE;
}

//---------------------------------------------------------------------------
//
//  Function: CopySingleFile
//
//  Purpose: Copies a file, does all error reporting and interacting with
//           the user.
//
//           If there are copy errors and the user cancels, this routine
//           cancels the whole wizard by jumping to the cancel page.  In
//           that case it returns FALSE.
//
//           After a file is successfully copied, gnFilesCopied will be
//           incremented and the gas-guage dlgproc will be notified.
//
//           Note that this code runs in the spawned thread.
//
//  Returns:
//      TRUE  if file was copied
//      FALSE if file was not copied (user canceled)
//
//---------------------------------------------------------------------------

BOOL CopySingleFile(HWND hwnd, LPTSTR Src, LPTSTR Dest)
{
    BOOL bRetry    = TRUE;
    UINT iRet, iRet2;
    static iOverwriteFiles = YES;
    HRESULT hrPrintf;

    // ISSUE-2002/02/28-stelo- I think this is actually going to have to be resolved so
    //  it doesn't mess up copies on an edit, when a distrib folder is
    //  already there, they just want to add files to it.

    //
    // ISSUE-2002/02/28-stelo- POSTPONED
    //
    // When we CopyFile from the CD, the readonly attribute is set on the
    // dest.  So we call SetFileAttributes and reset it.  If the user has
    // to redo the copy, he doesn't get a 1000 "Access Denied" errors.
    //
    // If the user cancels on the main wizard page, that thread jumps to
    // IDD_FINISH2.  This thread keeps running.
    //
    // When the user finally clicks the Finish button, this thread gets
    // terminated the hard way because WinMain() in thread0 exits.
    //
    // Due to this, there will frequently be a file at the dest that still
    // has the readonly bit set when user cancels on the main wizard page.
    //
    // To fix this, we would need to synchronize with the wizard having
    // been canceled and back out gracefully (before the user has time to
    // push the Finish button).
    //
    // Note that when the wizard is canceled because a copy error already
    // ocurred, thread1 (this thread) does the popping up and it is this
    // thread that jumps to IDD_FINISH2.  In this case we do back out
    // gracefully.  This bug only happens when the user presses Cancel
    // on the wizard page while the gas-guage is happily painting.
    //

    // ISSUE-2002/02/28-stelo- this function needs to be cleaned up.  To many if statements
    // scattered.  Don't make if conditional so long.
    if( iOverwriteFiles != YESTOALL )
    {

        if( DoesFileExist( Dest ) )
        {

            INT_PTR iRetVal;
            HANDLE hSrcFile;
            HANDLE hDestFile;
            DWORD dwSrcSize;
            DWORD dwDestSize;
            FILETIME LastWriteTimeSrc;
            FILETIME LastWriteTimeDest;
            SYSTEMTIME LastWriteSystemTime;

            if( iOverwriteFiles == NOTOALL )
            {

                //
                //  Give the illusion the file was copied
                //
                SendMessage( hwnd,
                             WMX_FILECOPIED,
                             (WPARAM) 0,
                             (LPARAM) 0 );

                gnFilesCopied++;

                return( TRUE );

            }

            hrPrintf=StringCchPrintf( g_szFileAlreadyExistsText, AS(g_szFileAlreadyExistsText),
                      StrFileAlreadyExists,
                      MyGetFullPath( Dest ) );

            //
            //  Open the files
            //
            hDestFile = CreateFile( Dest, GENERIC_READ, FILE_SHARE_READ, NULL,
                                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

            hSrcFile = CreateFile( Src, GENERIC_READ, FILE_SHARE_READ, NULL,
                                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

            GetFileTime( hSrcFile, NULL, NULL, &LastWriteTimeSrc );
            GetFileTime( hDestFile, NULL, NULL, &LastWriteTimeDest );

            // ISSUE-2002/02/28-stelo- need to display AM or PM, but what about other countries
            BuildTimeString( &LastWriteTimeSrc, g_szSrcFileDate, AS(g_szSrcFileDate) );
            BuildTimeString( &LastWriteTimeDest, g_szDestFileDate, AS(g_szSrcFileDate) );

            //
            //  Default to NO if file times are equal
            //
            if( CompareFileTime( &LastWriteTimeSrc, &LastWriteTimeDest ) < 0 )
            {
                g_SetFocusYes = FALSE;
            }
            else
            {
                g_SetFocusYes = TRUE;
            }

            // ISSUE-2002/02/28-stelo- doesn't handle file sized > 2^32 bytes, need to catch
            //   2nd parameter value
            dwSrcSize  = GetFileSize( hSrcFile, NULL );
            dwDestSize = GetFileSize( hDestFile, NULL );

            // ISSUE-2002/02/28-stelo- need to insert commas into size so it looks pretty
            hrPrintf=StringCchPrintf( g_szSrcFileSize,AS(g_szSrcFileSize), _T("%d %s"), dwSrcSize, StrBytes );
            hrPrintf=StringCchPrintf( g_szDestFileSize,AS(g_szDestFileSize), _T("%d %s"), dwDestSize, StrBytes );

            CloseHandle( hSrcFile  );
            CloseHandle( hDestFile );

            iRetVal = DialogBox( FixedGlobals.hInstance,
                                 (LPCTSTR) IDD_CONFIRM_FILE_REPLACE,
                                 hwnd,
                                 ConfirmFileReplaceDlgProc );

            if( iRetVal == NO )
            {

                //
                //  Give the illusion the file was copied
                //
                SendMessage( hwnd,
                             WMX_FILECOPIED,
                             (WPARAM) 0,
                             (LPARAM) 0 );

                gnFilesCopied++;

                return( TRUE );

            }
            else if( iRetVal == YESTOALL )
            {

                iOverwriteFiles = YESTOALL;

            }
            else if( iRetVal == NOTOALL )
            {

                iOverwriteFiles = NOTOALL;
            }
            else if( iRetVal == CANCEL )
            {

                return( FALSE );

            }
            //
            //  Not handling the YES case because that is the default, let this
            //  function proceed and overwrite the file.
            //

        }

    }

    do
    {

        if ( CopyFile( Src, Dest, FALSE ) )
        {

            SetFileAttributes(Dest, FILE_ATTRIBUTE_NORMAL);

            SendMessage(hwnd,
                        WMX_FILECOPIED,
                        (WPARAM) 0,
                        (LPARAM) 0);

            gnFilesCopied++;

            bRetry = FALSE;

        }
        else
        {

            iRet = ReportErrorId(hwnd,
                                 MSGTYPE_RETRYCANCEL | MSGTYPE_WIN32,
                                 IDS_ERR_COPY_FILE,
                                 Src, Dest);

            if ( iRet != IDRETRY ) {
                if ( CheckIfCancel(hwnd) )
                    return FALSE;
            }
        }

    } while ( bRetry );

    return TRUE;
}

//---------------------------------------------------------------------------
//
//  Function: CopyTheFiles
//
//  Purpose: Recursive routine to oversee the copying of the bits.
//
//  Returns:
//      TRUE if the whole tree was copied,
//      FALSE if user bailed on the tree copy
//
//      Note that in the FALSE case CopySingleFile would have caused
//      thread0 to the FINISH2 wizard page (unsuccessful completion)
//      and thread1 (this code) will back out without further copies.
//
//---------------------------------------------------------------------------

BOOL CopyTheFiles(HWND   hwnd,
                  LPTSTR SrcRootPath,
                  LPTSTR DestRootPath)
{
    LPTSTR SrcRootPathEnd  = SrcRootPath  + lstrlen(SrcRootPath);
    LPTSTR DestRootPathEnd = DestRootPath + lstrlen(DestRootPath);
    HANDLE FindHandle;
    WIN32_FIND_DATA FindData;
    BOOL bRet = TRUE;

    //
    // Look for * in this dir
    //

    if ( ! ConcatenatePaths(SrcRootPath, _T("*"), NULL) )
        return bRet;

    FindHandle = FindFirstFile(SrcRootPath, &FindData);

    if ( FindHandle == INVALID_HANDLE_VALUE )
        return bRet;

    do {

        *SrcRootPathEnd  = _T('\0');
        *DestRootPathEnd = _T('\0');

        //
        //  Don't copy the . and .. files (obviously)
        //  If we run across an unattend.txt, don't copy it
        //
        if ( ( lstrcmp(FindData.cFileName, _T(".") )  == 0 ) ||
             ( lstrcmp(FindData.cFileName, _T("..") ) == 0 ) ||
             ( LSTRCMPI( FindData.cFileName, _T("unattend.txt") ) == 0 ) )
            continue;

        if ( ! ConcatenatePaths(SrcRootPath,  FindData.cFileName, NULL) ||
             ! ConcatenatePaths(DestRootPath, FindData.cFileName, NULL) )
            continue;

        if ( ! (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {

            if ( ! CopySingleFile(hwnd, SrcRootPath, DestRootPath) ) {
                bRet = FALSE;
                goto CleanupAndReturn;
            }

        } else {

            //
            // Create the dir and recurse
            //

            if ( ! EnsureDirExists(DestRootPath) ) {

                UINT iRet;

                iRet = ReportErrorId(
                            hwnd,
                            MSGTYPE_RETRYCANCEL | MSGTYPE_WIN32,
                            IDS_ERR_CREATE_FOLDER,
                            DestRootPath);

                if ( iRet != IDRETRY ) {
                    if ( CheckIfCancel(hwnd) ) {
                        bRet = FALSE;
                        goto CleanupAndReturn;
                    }
                }
            }

            if ( ! CopyTheFiles(hwnd, SrcRootPath, DestRootPath) ) {
                bRet = FALSE;
                goto CleanupAndReturn;
            }
        }

    } while ( FindNextFile(FindHandle, &FindData) );

CleanupAndReturn:
    *SrcRootPathEnd  = _T('\0');
    *DestRootPathEnd = _T('\0');
    FindClose(FindHandle);

    return bRet;
}

//----------------------------------------------------------------------------
//
//  Function: AsyncTreeCopy
//
//  Purpose: The real thread entry
//
//  Args: VOID *Args - really COPY_THREAD_PARAMS *
//
//  Returns: 0
//
//----------------------------------------------------------------------------

UINT AsyncTreeCopy(VOID* Args)
{
    COPY_THREAD_PARAMS *InputArgs = (COPY_THREAD_PARAMS*) Args;

    TCHAR *CopySrc  = InputArgs->lpSourceBuffer;
    TCHAR *CopyDest = InputArgs->lpDestBuffer;
    HWND  hwnd      = InputArgs->hwnd;

    BOOL bRet;
    LONGLONG llSpaceNeeded, llSpaceAvail;

    //
    // Figure out how much disk space is needed to copy the CD.
    //

    ghDiskSpaceList = SetupCreateDiskSpaceList(0, 0, 0);
    if (ghDiskSpaceList == NULL)
    {
        TerminateTheWizard(IDS_ERROR_OUTOFMEMORY);
    }
    
    CountSpaceNeeded(hwnd, CopySrc, CopyDest);

    //
    // Is there enough free space?
    //
    // NOTE:
    //
    // We give the user a retry_cancel, hopefully user can free up space
    // on the drive.
    //
    // We could popup and let them change the destpath.  However, we may
    // have copied files on the AdditionalDirs page.  So allowing them to
    // change the path means you need to check diskspace requirements for
    // OemFilesPath and treecopy it as well.  If we ever allow changing
    // OemFilesPath, then the script would have to be updated as well,
    // and it has already been written out.
    //
    // We could check way back on the DistFolder page.  But then we
    // would have to find the SourcePath before we know if it's a CD
    // or netpath and we couldn't know how much they might copy on the
    // AdditionalDirs page.
    //

    llSpaceNeeded =
            MySetupQuerySpaceRequiredOnDrive(ghDiskSpaceList, CopyDest);

    llSpaceAvail = MyGetDiskFreeSpace(CopyDest);

    if ( llSpaceAvail < llSpaceNeeded ) {

        UINT iRet;

        iRet = ReportErrorId(
                    hwnd,
                    MSGTYPE_RETRYCANCEL,
                    IDS_ERR_INSUFICIENT_SPACE,
                    CopyDest,                   // ISSUE-2002-02-28-stelo-
                    (UINT) (llSpaceNeeded / ONE_MEG),
                    (UINT) (llSpaceAvail  / ONE_MEG));

        if ( iRet != IDRETRY ) {
            if ( CheckIfCancel(hwnd) )
                goto CleanupAndReturn;
        }
    }

    //
    // Update the message on the wizard page and start copying the files
    //

    SetDlgItemText(hwnd, IDC_TEXT, StrCopyingFiles);

    if ( CopyTheFiles(hwnd, CopySrc, CopyDest) ) {
        SendMessage(hwnd, WMX_ENDCOPYING, (WPARAM) 0, (LPARAM) 0);
    }

    //
    // Cleanup and return
    //

CleanupAndReturn:
    SetupDestroyDiskSpaceList(ghDiskSpaceList);
    return 0;
}


//----------------------------------------------------------------------------
//
//  This section of code runs in thread0.
//
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
//
// Function: TreeCopyNtSources
//
// Purpose: Entry point for copying the NT sources (either from CD or
//          a net path).
//
//          The dialog proc calls this one, and it takes care of the
//          details of spawning the thread.
//
// Arguments:
//      HWND hwnd       - window to receive copy notifications (the dlgproc)
//      UINT Message    - message to send on copy notifications (to dlgproc)
//      LPTSTR lpSource - root of copy source
//      LPTSTR lpDest   - root of copy dest
//
// Returns: void
//
// Notes:
//  - Input strings will not be modified.
//
//----------------------------------------------------------------------------

VOID TreeCopyNtSources(HWND   hwnd,
                       LPTSTR lpSource,
                       LPTSTR lpDest)
{
    DWORD    dwThreadId;
    HANDLE   hCopyThread;

    static COPY_THREAD_PARAMS ThreadParams;

    //
    // Fill in the ThreadParams and spawn it
    //
    
    // NTRAID#NTBUG9-551874-2002/02/27-stelo,swamip - CreateDistFolder, ShareTheDistFolder should use the code from OEM mode, reduce attack surface
    lstrcpyn(ThreadParams.lpSourceBuffer, lpSource,AS(ThreadParams.lpSourceBuffer));
    lstrcpyn(ThreadParams.lpDestBuffer,   lpDest, AS(ThreadParams.lpDestBuffer));

    MyGetFullPath(ThreadParams.lpSourceBuffer);
    MyGetFullPath(ThreadParams.lpDestBuffer);

    ThreadParams.hwnd = hwnd;

    hCopyThread = CreateThread(NULL,
                               0,
                               AsyncTreeCopy,
                               &ThreadParams,
                               0,
                               &dwThreadId);
}

//----------------------------------------------------------------------------
//
// Function: BuildCopyDestPath
//
// Purpose:
//
//     DestPath is assumed to be of MAX_PATH length
//
// Arguments:
//
// Returns: VOID
//
//----------------------------------------------------------------------------
VOID
BuildCopyDestPath( IN TCHAR *DestPath, IN DWORD cbSize )
{
    HRESULT hrCat;

    //
    //  If the dist folder begins with a drive letter, just use that
    //  If it is a UNC, then build the computer and share name and use that
    //
    if( WizGlobals.UncDistFolder[0] != _T('\\') )
    {

        lstrcpyn( DestPath, WizGlobals.UncDistFolder, cbSize );

    }
    else
    {

        GetComputerNameFromUnc( WizGlobals.UncDistFolder, DestPath, cbSize  );

        hrCat=StringCchCat( DestPath, cbSize, _T("\\") );
        hrCat=StringCchCat( DestPath, cbSize, WizGlobals.DistShareName );

    }
    
    hrCat=StringCchCat( DestPath, cbSize, _T("\\") );
    hrCat=StringCchCat( DestPath, cbSize, WizGlobals.Architecture );
}

//----------------------------------------------------------------------------
//
// Function: OnCopyFilesInitDialog
//
// Purpose:
//
// Arguments: IN HWND hwnd - handle to the dialog
//
// Returns: VOID
//
//----------------------------------------------------------------------------
static VOID
OnCopyFilesInitDialog( IN HWND hwnd )
{
    StrBuildingList = MyLoadString( IDS_COPYMSG1 );

    StrCopyingFiles = MyLoadString( IDS_COPYMSG2 );

    StrFileAlreadyExists = MyLoadString( IDS_FILE_ALREADY_EXISTS );

    StrModified = MyLoadString( IDS_MODIFIED );

    StrBytes = MyLoadString( IDS_BYTES );

    SetDlgItemText(hwnd, IDC_TEXT, StrBuildingList);

    //
    //  Load Months
    //

    StrJanuary   = MyLoadString( IDS_JANUARY );
    StrFebruary  = MyLoadString( IDS_FEBRUARY );
    StrMarch     = MyLoadString( IDS_MARCH );
    StrApril     = MyLoadString( IDS_APRIL );
    StrMay       = MyLoadString( IDS_MAY );
    StrJune      = MyLoadString( IDS_JUNE );
    StrJuly      = MyLoadString( IDS_JULY );
    StrAugust    = MyLoadString( IDS_AUGUST );
    StrSeptember = MyLoadString( IDS_SEPTEMBER );
    StrOctober   = MyLoadString( IDS_OCTOBER );
    StrNovember  = MyLoadString( IDS_NOVEMBER );
    StrDecember  = MyLoadString( IDS_DECEMBER );

    rgMonthsOfYear[0] = StrJanuary;
    rgMonthsOfYear[1] = StrFebruary;
    rgMonthsOfYear[2] = StrMarch;
    rgMonthsOfYear[3] = StrApril;
    rgMonthsOfYear[4] = StrMay;
    rgMonthsOfYear[5] = StrJune;
    rgMonthsOfYear[6] = StrJuly;
    rgMonthsOfYear[7] = StrAugust;
    rgMonthsOfYear[8] = StrSeptember;
    rgMonthsOfYear[9] = StrOctober;
    rgMonthsOfYear[10] = StrNovember;
    rgMonthsOfYear[11] = StrDecember;

    //
    //  Load Days of Week
    //

    StrSunday    = MyLoadString( IDS_SUNDAY );
    StrMonday    = MyLoadString( IDS_MONDAY );
    StrTuesday   = MyLoadString( IDS_TUESDAY );
    StrWednesday = MyLoadString( IDS_WEDNESDAY );
    StrThursday  = MyLoadString( IDS_THURSDAY );
    StrFriday    = MyLoadString( IDS_FRIDAY );
    StrSaturday  = MyLoadString( IDS_SATURDAY );

    rgDaysOfWeek[0] = StrSunday;
    rgDaysOfWeek[1] = StrMonday;
    rgDaysOfWeek[2] = StrTuesday;
    rgDaysOfWeek[3] = StrWednesday;
    rgDaysOfWeek[4] = StrThursday;
    rgDaysOfWeek[5] = StrFriday;
    rgDaysOfWeek[6] = StrSaturday;
    rgDaysOfWeek[7] = StrSunday;

}

//----------------------------------------------------------------------------
//
// Function: DlgCopyFilesPage
//
// Purpose: This is the dialog procedure the copy files page
//
//----------------------------------------------------------------------------

INT_PTR CALLBACK DlgCopyFilesPage(
    IN HWND     hwnd,
    IN UINT     uMsg,
    IN WPARAM   wParam,
    IN LPARAM   lParam)
{
    UINT nPercent;
    BOOL bStatus = TRUE;

    switch (uMsg) {

        case WM_INITDIALOG:

            OnCopyFilesInitDialog( hwnd );

            break;

        case WMX_BEGINCOPYING:
            {
                TCHAR *SrcPath;
                TCHAR DestPath[MAX_PATH + 1];

                if ( WizGlobals.bCopyFromPath )
                    SrcPath = WizGlobals.CopySourcePath;
                else
                    SrcPath = WizGlobals.CdSourcePath;

                SendDlgItemMessage(hwnd,
                                   IDC_PROGRESS1,
                                   PBM_SETPOS,
                                   0,
                                   0);

                BuildCopyDestPath( DestPath, AS(DestPath) );

                TreeCopyNtSources(hwnd,
                                  SrcPath,
                                  DestPath);
            }
            break;

        case WMX_ENDCOPYING:

            SendDlgItemMessage(hwnd,
                               IDC_PROGRESS1,
                               PBM_SETPOS,
                               (WPARAM) 100,
                               0);

            PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);

            //
            //  The CD is done copying so Auto-Advance to the next page
            //

            // ISSUE-2002/02/28-stelo- this works, but I should really go through
            //  RouteToProperPage or send a NEXT message but neither work
            PostMessage( GetParent(hwnd),
                         PSM_SETCURSELID,
                         (WPARAM) 0,
                         (LPARAM) IDD_FINISH );

            break;

        case WMX_FILECOPIED:
            nPercent = (gnFilesCopied * 100) / gnTotalFiles;
            SendDlgItemMessage(hwnd,
                               IDC_PROGRESS1,
                               PBM_SETPOS,
                               (WPARAM) nPercent,
                               0);
            break;

        case WM_NOTIFY:
            {
                LPNMHDR pnmh = (LPNMHDR)lParam;
                switch( pnmh->code ) {

                    case PSN_QUERYCANCEL:
                        CancelTheWizard(hwnd);
                        break;

                    case PSN_SETACTIVE:
                        PropSheet_SetWizButtons(GetParent(hwnd), 0);
                        PostMessage(hwnd, WMX_BEGINCOPYING, 0, 0);
                        break;

                    // Can't go back in the wizard from here
                    case PSN_WIZBACK:
                        break;

                    case PSN_WIZNEXT:
                        break;

                    default:
                        bStatus = FALSE;
                        break;
                }
            }
            break;

        default:
            bStatus = FALSE;
            break;
    }
    return bStatus;
}

//----------------------------------------------------------------------------
//
// Function: ConfirmFileReplaceDlgProc
//
// Purpose: Confirm file replace dialog proc.  Allows the user to chose to
//   overwrite the file, overwrite all files, do not overwrite or cancel the
//   copy all together. Runs in the copying thread's context
//
// Arguments: standard Win32 dialog proc arguments
//
// Returns:  the button the user pressed(Yes, Yes to All, No, Cancel)
//
//----------------------------------------------------------------------------
INT_PTR CALLBACK
ConfirmFileReplaceDlgProc( IN HWND     hwnd,
                           IN UINT     uMsg,
                           IN WPARAM   wParam,
                           IN LPARAM   lParam ) {

    BOOL bStatus = TRUE;

    switch( uMsg ) {

        case WM_INITDIALOG: {

            SetWindowText( GetDlgItem( hwnd, IDC_REPLACE_FILE_TEXT),
                           g_szFileAlreadyExistsText );

            SetWindowText( GetDlgItem( hwnd, IDC_SRC_FILE_DATE),
                           g_szSrcFileDate );

            SetWindowText( GetDlgItem( hwnd, IDC_SRC_FILE_SIZE),
                           g_szSrcFileSize );

            SetWindowText( GetDlgItem( hwnd, IDC_DEST_FILE_DATE),
                           g_szDestFileDate );

            SetWindowText( GetDlgItem( hwnd, IDC_DEST_FILE_SIZE),
                           g_szDestFileSize );

            if( g_SetFocusYes ) {
                SetFocus( GetDlgItem( hwnd, IDC_YES_BUTTON ) );
            }
            else {
                SetFocus( GetDlgItem( hwnd, IDC_NO_BUTTON ) );
            }

            break;

        }

        case WM_COMMAND: {

            int nButtonId = LOWORD( wParam );

            switch ( nButtonId ) {

                case IDC_YES_BUTTON:
                {
                    EndDialog( hwnd, YES );

                    break;
                }

                case IDC_YESTOALL:
                {
                    EndDialog( hwnd, YESTOALL );

                    break;
                }

                case IDC_NO_BUTTON:
                {
                    EndDialog( hwnd, NO );

                    break;
                }

                case IDC_NOTOALL:
                {
                    EndDialog( hwnd, NOTOALL );

                    break;
                }

                case IDCANCEL:
                {
                    if( CheckIfCancel( hwnd ) )
                    {
                        EndDialog( hwnd, CANCEL );
                    }

                    break;
                }

            }

        }

        default:
            bStatus = FALSE;
            break;

    }

    return( bStatus );

}