//========= Copyright Valve Corporation, All rights reserved. ============//
//
//	SYNC_FILES.CPP
//
//	Sync Files Routines.
//=====================================================================================//
#include "vxconsole.h"

char		g_syncfiles_localPath[MAX_PATH];
char		g_syncfiles_targetPath[MAX_PATH];
fileNode_t	*g_syncfiles_targetFiles;
int			g_syncfiles_numTargetFiles;
CProgress	*g_syncFiles_progress;
BOOL		g_syncFiles_noWrite;
BOOL		g_syncFiles_force;
BOOL		g_syncFiles_verbose;

struct dvdimage_t
{
	char		szString[MAX_PATH];
	CUtlString	installPath;
	CUtlString	versionDetailString;
};
CUtlVector< dvdimage_t> g_install_dvdImages;
int			g_install_Selection;
bool		g_install_bForceSync;
bool		g_install_bCleanTarget;

//-----------------------------------------------------------------------------
//	SyncFilesDlg_Validate
//
//-----------------------------------------------------------------------------
bool SyncFilesDlg_Validate()
{
	if ( !g_connectedToXBox )
	{
		Sys_MessageBox( "Sync Error", "Cannot sync until connected to XBox." );
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_Setup
//
//-----------------------------------------------------------------------------
void SyncFilesDlg_Setup( HWND hWnd )
{
	SetDlgItemText( hWnd, IDC_SYNCFILES_LOCALPATH, g_syncfiles_localPath ); 
	SetDlgItemText( hWnd, IDC_SYNCFILES_TARGETPATH, g_syncfiles_targetPath );

	CheckDlgButton( hWnd, IDC_SYNCFILES_FORCESYNC, g_syncFiles_force ? BST_CHECKED : BST_UNCHECKED );
	CheckDlgButton( hWnd, IDC_SYNCFILES_NOWRITE, g_syncFiles_noWrite ? BST_CHECKED : BST_UNCHECKED );
	CheckDlgButton( hWnd, IDC_SYNCFILES_VERBOSE, g_syncFiles_verbose ? BST_CHECKED : BST_UNCHECKED );
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_GetChanges
//
//-----------------------------------------------------------------------------
bool SyncFilesDlg_GetChanges( HWND hwnd )
{
	char	localPath[MAX_PATH];
	char	targetPath[MAX_PATH];
	
	localPath[0]  = '\0';
	targetPath[0] = '\0';

	GetDlgItemText( hwnd, IDC_SYNCFILES_LOCALPATH, localPath, MAX_PATH ); 
	GetDlgItemText( hwnd, IDC_SYNCFILES_TARGETPATH, targetPath, MAX_PATH ); 
	
	strcpy( g_syncfiles_localPath, localPath );
	Sys_NormalizePath( g_syncfiles_localPath, true );

	strcpy( g_syncfiles_targetPath, targetPath );
	Sys_NormalizePath( g_syncfiles_targetPath, true );

	g_syncFiles_force = IsDlgButtonChecked( hwnd, IDC_SYNCFILES_FORCESYNC ) != 0;
	g_syncFiles_noWrite = IsDlgButtonChecked( hwnd, IDC_SYNCFILES_NOWRITE ) != 0;
	g_syncFiles_verbose = IsDlgButtonChecked( hwnd, IDC_SYNCFILES_VERBOSE ) != 0;

	// success
	return ( true );
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_Proc
//
//-----------------------------------------------------------------------------
BOOL CALLBACK SyncFilesDlg_Proc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 
{ 
	switch ( message ) 
	{ 
		case WM_INITDIALOG:
			SyncFilesDlg_Setup( hwnd );
			return ( TRUE );

		case WM_COMMAND: 
            switch ( LOWORD( wParam ) ) 
            {
				case IDC_OK: 
					if ( !SyncFilesDlg_GetChanges( hwnd ) )
						break;
					EndDialog( hwnd, wParam );
					return ( TRUE ); 

				case IDCANCEL:
				case IDC_CANCEL: 
					EndDialog( hwnd, wParam );
					return ( TRUE ); 
			}
			break; 
	}
	return ( FALSE ); 
} 

//-----------------------------------------------------------------------------
//	SyncFiles_FreeTargetList
//
//-----------------------------------------------------------------------------
void SyncFiles_FreeTargetList()
{
	g_syncfiles_numTargetFiles = 0;

	FreeTargetFileList( g_syncfiles_targetFiles );
	g_syncfiles_targetFiles = NULL;
}

//-----------------------------------------------------------------------------
//	SyncFiles_DoUpdates
//
//-----------------------------------------------------------------------------
bool SyncFiles_DoUpdates()
{
	WIN32_FILE_ATTRIBUTE_DATA	localAttributes;
	fileNode_t					*nodePtr;
	char						sourceFilename[MAX_PATH];
	char						statusBuff1[MAX_PATH];
	char						statusBuff2[MAX_PATH];
	int							targetPathLen;
	char						*ptr;
	int							progress;
	int							fileSyncMode;
	int							numSynced;
	int							retVal;

	g_syncFiles_progress->SetStatus( "Syncing Target Files...", "", "" );
	g_syncFiles_progress->SetMeter( 0, g_syncfiles_numTargetFiles );

	fileSyncMode = FSYNC_ANDEXISTSONTARGET;
	if ( g_syncFiles_force )
		fileSyncMode |= FSYNC_ALWAYS;
	else
		fileSyncMode |= FSYNC_IFNEWER;

	targetPathLen = strlen( g_syncfiles_targetPath );

	numSynced = 0;
	progress = 0;
	nodePtr = g_syncfiles_targetFiles;
	while ( nodePtr )
	{
		ptr = nodePtr->filename+targetPathLen;
		if ( *ptr == '\\' )
			ptr++;

		// replace with source path head
		strcpy( sourceFilename, g_syncfiles_localPath );
		Sys_AddFileSeperator( sourceFilename, sizeof( sourceFilename ) );
		strcat( sourceFilename, ptr );

		float sourceFileSize = 0;
		if ( GetFileAttributesEx( sourceFilename, GetFileExInfoStandard, &localAttributes ) )
		{
			sourceFileSize = (float)localAttributes.nFileSizeLow / (1024.0f * 1024.0f);
		}

		_snprintf( statusBuff1, sizeof( statusBuff1 ), "Local File: %s (%.2f MB)", sourceFilename, sourceFileSize );
		statusBuff1[sizeof( statusBuff1 ) - 1] = '\0';
		_snprintf( statusBuff2, sizeof( statusBuff2 ), "Target File: %s", nodePtr->filename );
		statusBuff1[sizeof( statusBuff2 ) - 1] = '\0';
		g_syncFiles_progress->SetStatus( NULL, statusBuff1, statusBuff2 );

		retVal = FileSyncEx( sourceFilename, nodePtr->filename, fileSyncMode, g_syncFiles_verbose != FALSE, g_syncFiles_noWrite != FALSE);
		if ( retVal == 1 )
		{
			ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Sync: %s -> %s\n", sourceFilename, nodePtr->filename );
			numSynced++;
		}

		progress++;
		g_syncFiles_progress->SetMeter( progress, -1 );
		if ( g_syncFiles_progress->IsCancel() )
		{
			return false;
		}

		nodePtr = nodePtr->nextPtr;
	}

	ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Synced %d/%d Files.\n", numSynced, g_syncfiles_numTargetFiles );

	return true;
}

//-----------------------------------------------------------------------------
//	SyncFiles_GetTargetList
//
//-----------------------------------------------------------------------------
bool SyncFiles_GetTargetList( char* pTargetPath )
{
	g_syncFiles_progress->SetStatus( "Getting Target Files...", "", "" );
	g_syncFiles_progress->SetMeter( 0, 100 );

	if ( !GetTargetFileList_r( pTargetPath, true, FA_NORMAL|FA_READONLY, 0, &g_syncfiles_targetFiles ) )
	{
		ConsoleWindowPrintf( RGB( 255,0,0 ), "Bad Target Path '%s'\n", pTargetPath );
		return false;
	}

	fileNode_t*	pFileNode;
	for ( pFileNode = g_syncfiles_targetFiles; pFileNode; pFileNode = pFileNode->nextPtr )
	{
		g_syncfiles_numTargetFiles++;
	}

	// success
	return true;
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_SaveConfig
// 
//-----------------------------------------------------------------------------
void SyncFilesDlg_SaveConfig()
{
	Sys_SetRegistryString( "syncfiles_localPath", g_syncfiles_localPath );
	Sys_SetRegistryString( "syncfiles_targetPath", g_syncfiles_targetPath );
	Sys_SetRegistryInteger( "syncfiles_force", g_syncFiles_force );
	Sys_SetRegistryInteger( "syncfiles_noWrite", g_syncFiles_noWrite );
	Sys_SetRegistryInteger( "syncfiles_verbose", g_syncFiles_verbose );
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_Open
//
//-----------------------------------------------------------------------------
void SyncFilesDlg_Open( void )
{
	int		result;
	bool	bError;
	bool	bValid;

	result = DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_SYNCFILES ), g_hDlgMain, ( DLGPROC )SyncFilesDlg_Proc );
	if ( LOWORD( result ) != IDC_OK )
		return;

	SyncFilesDlg_SaveConfig();

	if ( !SyncFilesDlg_Validate() )
		return;

	g_syncFiles_progress = new CProgress;
	g_syncFiles_progress->Open( "Sync Files...", true, true );

	ConsoleWindowPrintf( XBX_CLR_DEFAULT, "\nSyncing Files From '%s' to '%s'\n", g_syncfiles_localPath, g_syncfiles_targetPath );
	
	bError = true;

	// get a file listing from target
	bValid = SyncFiles_GetTargetList( g_syncfiles_targetPath );
	if ( !bValid )
		goto cleanUp;

	// mark all files needing update
	bValid = SyncFiles_DoUpdates();
	if ( !bValid )
		goto cleanUp;

	bError = false;

cleanUp:
	delete g_syncFiles_progress;
	g_syncFiles_progress = NULL;

	if ( bError )
		ConsoleWindowPrintf( XBX_CLR_RED, "Aborted.\n" );
	else
		ConsoleWindowPrintf( XBX_CLR_DEFAULT, "Finished.\n" );

	SyncFiles_FreeTargetList();
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_LoadConfig	
// 
//-----------------------------------------------------------------------------
void SyncFilesDlg_LoadConfig()
{
	// get our config
	Sys_GetRegistryString( "syncfiles_localPath", g_syncfiles_localPath, g_localPath, sizeof( g_syncfiles_localPath ) );
	Sys_GetRegistryString( "syncfiles_targetPath", g_syncfiles_targetPath, g_targetPath, sizeof( g_syncfiles_targetPath ) );
	Sys_GetRegistryInteger( "syncfiles_force", false, g_syncFiles_force );
	Sys_GetRegistryInteger( "syncfiles_noWrite", false, g_syncFiles_noWrite );
	Sys_GetRegistryInteger( "syncfiles_verbose", false, g_syncFiles_verbose );
}

//-----------------------------------------------------------------------------
//	SyncFilesDlg_Init
// 
//-----------------------------------------------------------------------------
bool SyncFilesDlg_Init()
{
	SyncFilesDlg_LoadConfig();
	return true;
}

//-----------------------------------------------------------------------------
//	InstallDlg_InstallImage
// 
//-----------------------------------------------------------------------------
void InstallDlg_InstallImage( const char *pInstallPath, bool bForce, bool bCleanTarget )
{
	int				errCode;
	char			*pToken;
	char			arg1[MAXTOKEN];
	char			arg2[MAXTOKEN];
	char			sourceFilename[MAX_PATH];
	char			sourcePath[MAX_PATH];
	char			targetFilename[MAX_PATH];
	char			filename[MAX_PATH];
	WIN32_FIND_DATA	findData;
	HANDLE			h;

	if ( !pInstallPath[0] )
	{
		return;
	}

	ConsoleWindowPrintf( XBX_CLR_DEFAULT, "\nSyncing From Install Depot: %s\n", pInstallPath );

	// get the install script
	char szScriptPath[MAX_PATH];
	V_ComposeFileName( pInstallPath, "../dvd_install.txt", szScriptPath, sizeof( szScriptPath ) );
	if ( !Sys_Exists( szScriptPath ) )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "Failed to open: %s\n", szScriptPath );
		return;
	}

	// sanity check
	// DVD image auto-build failure prevents the presence of the default.xex
	char szTempFilename[MAX_PATH];
	V_ComposeFileName( pInstallPath, "default.xex", szTempFilename, sizeof( szTempFilename ) );
	if ( !Sys_Exists( szTempFilename ) )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "Cancelled Installation! DVD Image Auto-Build Process Failed - Missing default.xex!\n" );
		return;
	}

	// get the version detail
	char szVersionPath[MAX_PATH];
	char *pVersionDetailString = NULL;
	V_ComposeFileName( pInstallPath, "version.txt", szVersionPath, sizeof( szVersionPath ) );
	if ( Sys_LoadFile( szVersionPath, (void**)&pVersionDetailString ) <= 0 )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "Cancelled Installation! DVD Image Auto-Build Process Failed! - Missing version.txt\n" );
		return;		
	}

	Sys_LoadScriptFile( szScriptPath );

	CProgress* pProgress = new CProgress;
	pProgress->Open( "Installing DVD Image...", true, false );

	int numFailed = 0;
	int numSkipped = 0;
	int numUpdated = 0;
	int version = 0;

	bool bFailed = true;
	bool bSyntaxError = false;
	bool bCancelled = false;
	while ( 1 )
	{
		pToken = Sys_GetToken( true );
		if ( !pToken || !pToken[0] )
			break;

		if ( pProgress->IsCancel() )
		{
			bCancelled = true;
			break;
		}

		if ( !stricmp( pToken, "$version" ) )
		{
			// version <num> <targetroot>
			pToken = Sys_GetToken( false );
			if ( !pToken || !pToken[0] )
			{
				bSyntaxError = true;
				break;
			}
			version = atoi( pToken );
			if ( version < 0 )
			{
				version = 0;
			}

			pToken = Sys_GetToken( false );
			if ( !pToken || !pToken[0] )
			{
				bSyntaxError = true;
				break;
			}
			Sys_StripQuotesFromToken( pToken );
			V_FixSlashes( pToken );
			if ( pToken[0] == CORRECT_PATH_SEPARATOR )
			{
				// remove initial slash
				memcpy( pToken, pToken+1, strlen( pToken ) );
			}	

			char szRootPath[MAX_PATH];
			V_ComposeFileName( g_targetPath, pToken, szRootPath, sizeof( szRootPath ) );

			if ( bCleanTarget )
			{
				// delete any exisiting files at target
				DM_FILE_ATTRIBUTES	fileAttributes;
				HRESULT hr = DmGetFileAttributes( szRootPath, &fileAttributes );
				if ( hr == XBDM_NOERR )
				{
					// delete all files at valid target
					V_ComposeFileName( szRootPath, "*.*", szTempFilename, sizeof( szTempFilename ) );
					char *pArgs[3];
					pArgs[0] = "*del";
					pArgs[1] = szTempFilename;
					pArgs[2] = "/s";
					if ( !lc_del( 3, pArgs ) )
					{
						ConsoleWindowPrintf( XBX_CLR_RED, "Failed To Delete Files At '%s'.\n", szRootPath );
						break;
					}
				}
			}

			char szTargetVersionPath[MAX_PATH];
			V_ComposeFileName( szRootPath, "version.txt", szTargetVersionPath, sizeof( szTargetVersionPath ) );

			if ( !bForce )
			{
				int fileSize;
				char *pTargetVersionDetailString;
				if ( !LoadTargetFile( szTargetVersionPath, &fileSize, (void**)&pTargetVersionDetailString ) )
				{
					// expected version does not exist
					// force full install
					bForce = true;
				}
				else if ( strcmp( pVersionDetailString, pTargetVersionDetailString ) != 0 )
				{
					// different versions
					bForce = true;
				}
				Sys_Free( pTargetVersionDetailString );
			}

			if ( bForce && !FileSyncEx( szVersionPath, szTargetVersionPath, FSYNC_ALWAYS, true, false ) )
			{
				numFailed++;
				break;
			}
		}
		else if ( !stricmp( pToken, "$print" ) )
		{
			// print <string>
			pToken = Sys_GetToken( false );
			if ( !pToken || !pToken[0] )
			{
				bSyntaxError = true;
				break;
			}
			Sys_StripQuotesFromToken( pToken );
			ConsoleWindowPrintf( XBX_CLR_DEFAULT, "%s\n", pToken );
		}
		else if ( !stricmp( pToken, "$copy" ) )
		{
			// copy <source> <target>
			pToken = Sys_GetToken( false );
			if ( !pToken || !pToken[0] )
			{
				bSyntaxError = true;
				break;
			}
			Sys_StripQuotesFromToken( pToken );
			V_FixSlashes( pToken );
			if ( !V_strnicmp( pToken, "DVD\\", 4 ) )
			{
				// skip past DVD, used as a placeholder
				pToken += 4;
			}
			V_ComposeFileName( pInstallPath, pToken, arg1, sizeof( arg1 ) );

			pToken = Sys_GetToken( false );
			if ( !pToken || !pToken[0] )
			{
				bSyntaxError = true;
				break;
			}
			Sys_StripQuotesFromToken( pToken );
			V_FixSlashes( pToken );
			if ( pToken[0] == CORRECT_PATH_SEPARATOR )
			{
				// remove initial slash
				memcpy( pToken, pToken+1, strlen( pToken ) );
			}	
			V_ComposeFileName( g_targetPath, pToken, arg2, sizeof( arg2 ) );

			Sys_StripFilename( arg1, sourcePath, sizeof( sourcePath ) );

			h = FindFirstFile( arg1, &findData );
			if ( h != INVALID_HANDLE_VALUE )
			{
				do
				{
					if ( pProgress->IsCancel() )
					{
						bCancelled = true;
						break;
					}

					if ( !stricmp( findData.cFileName, "." ) || !stricmp( findData.cFileName, ".." ) )
					{
						continue;
					}
					if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
					{
						continue;
					}

					strcpy( sourceFilename, sourcePath );
					Sys_AddFileSeperator( sourceFilename, sizeof( sourceFilename ) );
					strcat( sourceFilename, findData.cFileName );
					Sys_NormalizePath( sourceFilename, false );

					Sys_StripPath( arg2, filename, sizeof( filename ) );
					if ( filename[0] )
					{
						// target filename is specified
						strcpy( targetFilename, arg2 );
					}
					else
					{
						// target filename is path
						strcpy( targetFilename, arg2 );
						Sys_AddFileSeperator( targetFilename, sizeof( targetFilename ) );
						strcat( targetFilename, findData.cFileName );
						Sys_NormalizePath( targetFilename, false );
					}

					ConsoleWindowPrintf( XBX_CLR_DEFAULT, "\nCopying: %s -> %s\n", sourceFilename, targetFilename );

					WIN32_FILE_ATTRIBUTE_DATA localAttributes;
					float sourceFileSize = 0;
					if ( GetFileAttributesEx( sourceFilename, GetFileExInfoStandard, &localAttributes ) )
					{
						sourceFileSize = (float)localAttributes.nFileSizeLow / (1024.0f * 1024.0f);
					}

					char statusBuff1[MAX_PATH];
					V_snprintf( statusBuff1, sizeof( statusBuff1 ), "Copying (%.2f MB) ... Please Wait", sourceFileSize );
					pProgress->SetStatus( statusBuff1, sourceFilename, targetFilename );

					int fileSyncMode = bForce ? FSYNC_ALWAYS : FSYNC_IFNEWER;
					errCode = FileSyncEx( sourceFilename, targetFilename, fileSyncMode, true, false );
					if ( errCode < 0 )
					{
						ConsoleWindowPrintf( XBX_CLR_RED, "Sync Failure!\n" );
						numFailed++;
					}
					else if ( errCode == 0 )
					{
						ConsoleWindowPrintf( XBX_CLR_BLUE, "Sync Skipped!\n" );
						numSkipped++;
					}
					else if ( errCode == 1 )
					{
						ConsoleWindowPrintf( XBX_CLR_GREEN, "Sync Completed!\n" );
						numUpdated++;
					}
				}
				while ( FindNextFile( h, &findData ) );

				FindClose( h );
			}
		}
		else if ( !stricmp( pToken, "$end" ) )
		{
			bFailed = false;
			break;
		}
		else
		{
			ConsoleWindowPrintf( XBX_CLR_RED, "Unknown token: '%s' in '%s'\n", pToken, szScriptPath );
			bSyntaxError = true;
			break;
		}
	}

	if ( bSyntaxError )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "Syntax Error in '%s'.\n", szScriptPath );
	}

	if ( bCancelled )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "Cancelled Installation!\n" );
	}
	else if ( bFailed )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "Failed Installation!\n" );
	}
	else
	{
		char *pCRLF = V_stristr( pVersionDetailString, "\r\n" );
		if ( pCRLF )
		{
			*pCRLF = '\0';
		}

		// spew stats
		ConsoleWindowPrintf( XBX_CLR_BLACK, "\n" );
		ConsoleWindowPrintf( XBX_CLR_BLACK, "Installation Completed.\n" );
		ConsoleWindowPrintf( XBX_CLR_BLACK, "-----------------------\n" );
		ConsoleWindowPrintf( XBX_CLR_BLACK, "Version: %s\n", pVersionDetailString ? pVersionDetailString : "???" );

		if ( numFailed )
		{
			ConsoleWindowPrintf( XBX_CLR_RED, "%d Failures.\n", numFailed );
		}		
		ConsoleWindowPrintf( XBX_CLR_BLACK, "%d Files In Sync.\n", numSkipped );
		ConsoleWindowPrintf( XBX_CLR_BLACK, "%d Files Updated.\n", numUpdated );
	}

	delete pProgress;
	Sys_Free( pVersionDetailString );
	Sys_FreeScriptFile();
}

//-----------------------------------------------------------------------------
//	InstallDlg_GetChanges
//
//-----------------------------------------------------------------------------
bool InstallDlg_GetChanges( HWND hWnd )
{
	g_install_bForceSync = IsDlgButtonChecked( hWnd, IDC_INSTALL_FORCESYNC ) != 0;
	g_install_bCleanTarget = IsDlgButtonChecked( hWnd, IDC_INSTALL_CLEANTARGET ) != 0;

	g_install_Selection = -1;
	for ( int i = 0; i < g_install_dvdImages.Count(); i++ )
	{
		int state = ListView_GetItemState( GetDlgItem( hWnd, IDC_INSTALL_LIST ), i, LVIS_FOCUSED|LVIS_SELECTED );
		if ( state == ( LVIS_FOCUSED|LVIS_SELECTED ) )
		{
			g_install_Selection = i;
			break;
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
//	SortDVDImages
//
//-----------------------------------------------------------------------------
int SortDVDImages( const dvdimage_t *pA, const dvdimage_t *pB )
{
	char szStringA[256];
	char szStringB[256];

	V_strncpy( szStringA, pA->szString, sizeof( szStringA ) );
	V_strncpy( szStringB, pB->szString, sizeof( szStringB ) );

	// sort staging first
	char *pCommentA = V_stristr( szStringA, " (" );
	char *pCommentB = V_stristr( szStringB, " (" );
	if ( pCommentA )
	{
		*pCommentA = '\0';
	}
	if ( pCommentB )
	{
		*pCommentB = '\0';
	}
	
	return stricmp( szStringB, szStringA );
}

//-----------------------------------------------------------------------------
//	InstallDlg_Populate
//
//-----------------------------------------------------------------------------
void InstallDlg_Populate( HWND hWnd )
{
	HWND hWndListView = GetDlgItem( hWnd, IDC_INSTALL_LIST );

	ListView_DeleteAllItems( hWndListView );
	g_install_dvdImages.Purge();

	// get list of DVD images
	char szPath[MAX_PATH];
	V_ComposeFileName( g_installPath, "DVD_*", szPath, sizeof( szPath ) );
	WIN32_FIND_DATA	findData;	
	HANDLE h = FindFirstFile( szPath, &findData );
	if ( h != INVALID_HANDLE_VALUE )
	{
		do
		{
			if ( !stricmp( findData.cFileName, "." ) || !stricmp( findData.cFileName, ".." ) )
			{
				continue;
			}
			if ( !( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
			{
				// skip files
				continue;
			}

			char szInstallPath[MAX_PATH];
			V_strncpy( szInstallPath, g_installPath, sizeof( szInstallPath ) );
			V_AppendSlash( szInstallPath, sizeof( szInstallPath ) );
			V_strncat( szInstallPath, findData.cFileName, sizeof( szInstallPath ) );

			// qualify dvd dirs that are images
			char szBooterPath[MAX_PATH];
			V_ComposeFileName( szInstallPath, "default.xex", szBooterPath, sizeof( szBooterPath ) );
			if ( !Sys_Exists( szBooterPath ) )
			{
				continue;
			}
			char szVersionPath[MAX_PATH];
			V_ComposeFileName( szInstallPath, "version.txt", szVersionPath, sizeof( szVersionPath ) );
			char *pVersionDetailString = NULL;
			if ( Sys_LoadFile( szVersionPath, (void**)&pVersionDetailString ) == -1 )
			{
				continue;
			}

			char *pCRLF = V_stristr( pVersionDetailString, "\r\n" );
			if ( pCRLF )
			{
				*pCRLF = '\0';
			}

			dvdimage_t image;

			int year = 0;
			int month = 0;
			int day = 0;
			int hour = 0;
			int minute = 0;
			char timeOfDay[256];
			sscanf( findData.cFileName, "DVD_%d_%d_%d_%d_%d_%s", &year, &month, &day, &hour, &minute, timeOfDay );

			const char *pInfoString = timeOfDay;
			if ( !V_strnicmp( timeOfDay, "PM", 2 ) )
			{
				if ( hour != 12 )
				{
					// 24 hour time for correct sorting
					hour += 12;
				}
				pInfoString += 2;
			}
			else if ( !V_strnicmp( timeOfDay, "AM", 2 ) )
			{
				if ( hour == 12 )
				{
					// 24 hour time for correct sorting
					hour = 0;
				}
				pInfoString += 2;
			}

			char szCommentBuff[128];
			if ( pInfoString[0] == '_' )
			{
				// optional info after AM/PM
				pInfoString++;
				V_snprintf( szCommentBuff, sizeof( szCommentBuff ), " (%s)", pInfoString );
			}
			else
			{
				szCommentBuff[0] = '\0';
			}
			
			V_snprintf( image.szString, sizeof( image.szString ), "%2.2d/%2.2d/%4.4d %2.2d:%2.2d%s", month, day, year, hour, minute, szCommentBuff );

			image.installPath = szInstallPath;
			image.versionDetailString = pVersionDetailString;
			g_install_dvdImages.AddToTail( image );
			Sys_Free( pVersionDetailString );
		}
		while ( FindNextFile( h, &findData ) );
		FindClose( h );
	}

	// current image will be at head
	g_install_dvdImages.Sort( SortDVDImages );

	for ( int i = 0; i < g_install_dvdImages.Count(); i++ )
	{
		// setup and insert at end of list
		LVITEM	lvi;
		memset( &lvi, 0, sizeof( lvi ) );
		lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; 
		lvi.iItem = i;
		lvi.iSubItem = 0;
		lvi.state = 0; 
		lvi.stateMask = 0;
		lvi.pszText = LPSTR_TEXTCALLBACK;  
		lvi.lParam = (LPARAM)i;

		ListView_InsertItem( hWndListView, &lvi );
	}

	if ( g_install_dvdImages.Count() )
	{
		ListView_SetItemState( hWndListView, 0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED );
	}
}

//-----------------------------------------------------------------------------
//	InstallDlg_Setup
//
//-----------------------------------------------------------------------------
void InstallDlg_Setup( HWND hWnd )
{
	g_install_Selection = -1;
	g_install_bForceSync = false;
	g_install_bCleanTarget = false;

	HWND hWndListView = GetDlgItem( hWnd, IDC_INSTALL_LIST );

	// initialize columns
	LVCOLUMN lvc; 
	memset( &lvc, 0, sizeof( lvc ) );
	lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM; 
	lvc.iSubItem = 0;
	lvc.fmt = LVCFMT_LEFT;
	lvc.cx = 200;
	lvc.pszText = ( LPSTR )"Date Built:";
	ListView_InsertColumn( hWndListView, 0, &lvc );
	lvc.iSubItem = 0;
	lvc.fmt = LVCFMT_LEFT;
	lvc.cx = 500;
	lvc.pszText = ( LPSTR )"Perforce Changelist:";
	ListView_InsertColumn( hWndListView, 1, &lvc );

	ListView_SetBkColor( hWndListView, g_backgroundColor );
	ListView_SetTextBkColor( hWndListView, g_backgroundColor );

	DWORD style = LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES;
	ListView_SetExtendedListViewStyleEx( hWndListView, style, style );

	InstallDlg_Populate( hWnd );
}

//-----------------------------------------------------------------------------
//	InstallDlg_Proc
//
//-----------------------------------------------------------------------------
BOOL CALLBACK InstallDlg_Proc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) 
{ 
	switch ( message ) 
	{ 
	case WM_INITDIALOG:
		InstallDlg_Setup( hWnd );
		return ( TRUE );

	case WM_NOTIFY:
		switch ( ( ( LPNMHDR )lParam )->code )
		{
		case LVN_GETDISPINFO:
			NMLVDISPINFO* plvdi;
			plvdi = (NMLVDISPINFO*)lParam;
			int item = (int)plvdi->item.lParam;
			switch ( plvdi->item.iSubItem )
			{
			case 0:
				plvdi->item.pszText = g_install_dvdImages[item].szString;
				return TRUE;
			case 1:
				plvdi->item.pszText = (LPSTR)g_install_dvdImages[item].versionDetailString.String();
				return TRUE;
			}
		}
		break;

	case WM_COMMAND: 
		switch ( LOWORD( wParam ) ) 
		{
		case IDC_OK: 
			InstallDlg_GetChanges( hWnd );
			EndDialog( hWnd, wParam );
			return ( TRUE ); 

		case IDC_INSTALL_REFRESH:
			InstallDlg_Populate( hWnd );
			return TRUE;

		case IDCANCEL:
		case IDC_CANCEL: 
			EndDialog( hWnd, wParam );
			return ( TRUE ); 
		}
		break; 
	}
	return ( FALSE ); 
} 

//-----------------------------------------------------------------------------
//	InstallDlg_Open
//
//-----------------------------------------------------------------------------
void InstallDlg_Open( void )
{
	int result = DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_INSTALL ), g_hDlgMain, ( DLGPROC )InstallDlg_Proc );
	if ( LOWORD( result ) == IDC_OK && g_install_Selection != -1 )
	{
		InstallDlg_InstallImage( 
			g_install_dvdImages[g_install_Selection].installPath.String(),
			g_install_bForceSync,
			g_install_bCleanTarget );
	}

	g_install_dvdImages.Purge();
}