/*
**	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/>.
*/

/*********************************************************************************************** 
 ***                            Confidential - Westwood Studios                              *** 
 *********************************************************************************************** 
 *                                                                                             * 
 *                 Project Name : Installer                                                    * 
 *                                                                                             * 
 *                     $Archive:: /Commando/Code/Installer/Installer.cpp  $* 
 *                                                                                             * 
 *                      $Author:: Ian_l                   $* 
 *                                                                                             * 
 *                     $Modtime:: 1/17/02 7:43p                 $* 
 *                                                                                             * 
 *                    $Revision:: 18                    $* 
 *                                                                                             * 
 *---------------------------------------------------------------------------------------------* 
 * Functions:                                                                                  * 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

// Include files.
#include "Installer.h"
#include "Argv.h"
#include "AssetMgr.h"
#include "AssetStatus.h"
#include "AudibleSound.h"
#include "BINKMovie.h"
#include "CardSelectionDialog.h"
#include "CodeControl.h"
#include "CopyDialog.h"
#include "DialogMgr.h"
#include "DirectoryDialog.h"
#include "DX8Wrapper.h"
#include "ErrorHandler.h"
#include "FinalDialog.h"
#include "FolderDialog.h"
#include "Ini.h"
#include "LicenseDialog.h"
#include "MixFile.h"
#include "Msgloop.h"
#include "Registry.h"
#include "ReviewDialog.h"
#include "SafeTimer.h"
#include "SerialDialog.h"
#include "Timer.h"
#include "TransitionDialog.h"
#include "Translator.h"
#include "Utilities.h"
#include "WelcomeDialog.h"
#include "WhatToInstallDialog.h"
#include "Ww3D.h"
#include "Wwmath.h"
#include "WOL1Dialog.h"
#include "WOL2Dialog.h"
#include <io.h>
#include <objbase.h>
#include <shlobj.h>
#include <intshcut.h>

// Section names and entry names for IDS_GAME_INI_FILENAME.
#define INI_CD_LABEL_SECTION					"CDLabel"
#define INI_SUBDIRECTORY_SECTION				"SubDirectories"
#define INI_FILES_SECTION						"Files"
#define INI_VOLUME_LABEL_ENTRY				"VolumeLabel"		
#define INI_SUBDIRECTORY_ENTRY_PREFIX		"Dir"
#define INI_TOTAL_DISK_SPACE_NEEDED_ENTRY	"TotalDiskSpaceNeeded"
#define INI_NUM_FILES_ENTRY					"NumFiles"

// Section names and entry names for IDS_WOLAPI_INI_FILENAME.
#define INI_WOLAPI_SECTION						"Wolapi"
#define INI_WOLREGISTER_SECTION				"Register"
#define INI_WOLBROWSER_SECTION 				"Browser"
#define INI_VERSION_MAJOR_ENTRY				"Major"
#define INI_VERSION_MINOR_ENTRY				"Minor"

// Section names and entry names for IDS_SETUP_INI_FILENAME.
#define INI_SETUP_SECTION						"Setup"
#define INI_LANGUAGE_ENTRY						"Language"	
#define INI_SKU_ENTRY							"SKU"
#define INI_CAN_USE_IGR_SETTNGS_ENTRY		"CanUseIGRSettings"

// Miscellaneous.
#define ENCRYPTION_STRING_LENGTH				128	
#define INSTALLER_NOMINAL_BYTE_COUNT		0x40000

// Registry access.
#define APPLICATION_SUB_KEY_NAME				"Software\\Westwood\\Renegade"
#define APPLICATION_SUB_KEY_NAME_RENDER	"Software\\Westwood\\Renegade\\Render"
#define APPLICATION_SUB_KEY_NAME_OPTIONS	"Software\\Westwood\\Renegade\\Options"

// Macros.
#define END_DIALOG(dialog) \
	if (dialog != NULL) { \
		dialog->End_Dialog(); \
	}

#define REGISTER_DIALOG(dialogclass, dialog, args) \
  if (dialog == NULL) { \
		dialog = NEW_REF (dialogclass, args); \
		dialog->Start_Dialog();	\
	} else { \
		DialogMgrClass::Register_Dialog (dialog); \
	} 

#define UNREGISTER_DIALOG(dialog) \
	DialogMgrClass::UnRegister_Dialog (dialog); 

#define DO_CANCEL \
	CancelApplication = true; \
	PostQuitMessage (0); 


// Foward declarations.
void WWDebug_Message_Callback (DebugType type, const char *message);
void WWAssert_Callback (const char *message);


// Prototypes.
bool CALLBACK Default_On_Command (DialogBaseClass *dialog, int ctrl_id, int mesage_id, DWORD param);


// Singleton.
InstallerClass _Installer;


// Type definitions.
typedef HRESULT (*DLLREGISTERSERVER)(void);
typedef HRESULT (*DLLUNREGISTERSERVER)(void);


/***********************************************************************************************
 * InstallerClass::InstallerClass --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
InstallerClass::InstallerClass() 
	:	SetupIni (NULL),
		GameIni (NULL),
		WOLIni (NULL),
		AssetManager (NULL),
		FreshGameInstall (true),
		FreshWOLInstall (true),
		TargetWOLOlder (true),
		WwmathInitialized (false),
		DialogMgrInitialized (false),
		AudioSystem (NULL),
		TransitionMusic (NULL),
		InstallMusic (NULL),
		UIInput (NULL),
		CancelIntroduction (false),
		CancelApplication (false),
		WelcomeDialog (NULL),
		NDADialog (NULL),
		LicenseDialog (NULL),
		SerialDialog (NULL),
		WhatToInstallDialog (NULL),
		GameDirectoryDialog (NULL),
		GameFolderDialog (NULL),
		WOLDirectoryDialog (NULL),
		WOLFolderDialog (NULL),
		ReviewDialog (NULL),
		CopyDialog (NULL),
		WOL1Dialog (NULL),
		WOL2Dialog (NULL),
		FinalDialog (NULL),
		GameFileCount (0),
		GameSize (0),
		WOLFileCount (0),
		WOLSize (0),
		CardSelectionDialog (NULL),
		ProgEndAction (ACTION_NONE)
{
	SetupIni = new INIClass;
	GameIni	= new INIClass;
	WOLIni	= new INIClass;
}


/***********************************************************************************************
 * InstallerClass::~InstallerClass --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
InstallerClass::~InstallerClass()
{
	delete WOLIni;
	delete GameIni;
	delete SetupIni;
}


/***********************************************************************************************
 * InstallerClass::Install --																						  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Install (MixFileFactoryClass *mixfilefactory)
{
	try {

		const Vector3 black (0.0f, 0.0f, 0.0f);

		MixFileFactory = mixfilefactory;

		// Install debug callbacks.
		WWDebug_Install_Message_Handler (WWDebug_Message_Callback);
		WWDebug_Install_Assert_Handler (WWAssert_Callback);

		// Check that the source data for the install is present.
		if (Check_Source()) {

			const char *intromoviename	= "InstallMovie.bik";
			const int	playouttime		= 2000;

			bool cancelapplication = false;	
			int  time;	

			Check_Existing_Install();
			Initialize();

			// If the user has more than one video card, run the video card selection dialog.
			CardSelectionDialog = NEW_REF (CardSelectionDialogClass, (false));
			while (CardSelectionDialog->Get_Card_Count() > 1) {

				int cardselection;	

				CardSelectionDialog->Start_Dialog();
				while (CardSelectionDialog->Is_Running()) {

					DialogMgrClass::On_Frame_Update();
					WW3D::Begin_Render (true, true, black);
					if (GameInFocus) {
						DialogMgrClass::Render();
					} else {
						Sleep (50);
					}
					WW3D::End_Render();
					Windows_Message_Handler();		
				}
				cardselection = CardSelectionDialog->Get_Card_Selection();
				cancelapplication = CardSelectionDialog->Cancel_Application();

				if (cancelapplication) break;
				
				// NOTE: Must release all dialogs before attempting initialization.
				REF_PTR_RELEASE (CardSelectionDialog);
				WW3D_Shutdown();
				if (WW3D_Initialize (cardselection)) break;

				// Re-initialize with the default video card and driver and inform the user
				// that their selected card would not initialize.
				WW3D_Shutdown();
				WW3D_Initialize();
				CardSelectionDialog = NEW_REF (CardSelectionDialogClass, (true));
			}
			REF_PTR_RELEASE (CardSelectionDialog);

			// Does the user wish to exit?
			if (!cancelapplication) {

				if (!Beta_Test()) {

					// Play BINK intro movie.
					BINKMovie::Init();
					BINKMovie::Play (intromoviename);
					while (!BINKMovie::Is_Complete()) {
						if (CancelIntroduction) {
							BINKMovie::Stop();
							break;
						}
						BINKMovie::Update();
						WW3D::Begin_Render (false, false, black);
						if (GameInFocus) {
							BINKMovie::Render();
						} else {
							Sleep (50);
						}
						WW3D::End_Render();
						Windows_Message_Handler();		
					}
					BINKMovie::Shutdown();

					// For artistic reasons, end the movie on a black frame.
					WW3D::Begin_Render (true, true, black);
					WW3D::End_Render();

					// If the user cancelled during the intro movie, then don't bother with the transition.
					if (!CancelIntroduction) {

						TransitionDialogClass *transition;

						// Create the transition animation.
						transition = NEW_REF (TransitionDialogClass, ());
						transition->Start_Dialog();

						// Start the transition music.
						if (TransitionMusic != NULL) TransitionMusic->Play();

						TTimerClass <SafeTimerClass> timer;
						
						// Play.
						while (true) {

							DialogMgrClass::On_Frame_Update();

							// Has the dialog 'played out'?
							// NOTE: Wait for both video and audio to finish.
							if (!transition->Is_Running()) {
								if (TransitionMusic != NULL) {
									if (!TransitionMusic->Is_Playing()) break;
								} else {
									break;
								}
							}

							WW3D::Begin_Render (true, true, black);
							if (GameInFocus) {
								DialogMgrClass::Render();
								if (TransitionMusic != NULL) {
									if (TransitionMusic->Get_State() == AudibleSoundClass::STATE_PAUSED) TransitionMusic->Resume();
								}
								if (!timer.Is_Active()) timer.Start();
							} else {
								if (TransitionMusic != NULL) {
									if (TransitionMusic->Get_State() == AudibleSoundClass::STATE_PLAYING) TransitionMusic->Pause();
								}
								if (timer.Is_Active()) timer.Stop();
								Sleep (50);
							}
							WW3D::End_Render();

							WW3D::Sync (timer.Value());
							WWAudioClass::Get_Instance()->On_Frame_Update();
							Windows_Message_Handler();		

							// Has the user cancelled the transition?
							if (CancelIntroduction) {
								transition->End_Dialog();
								if (TransitionMusic != NULL) TransitionMusic->Stop();
								break;
							}
						}
						REF_PTR_RELEASE (transition);
					}
				}

				// Set the command handler for all subsequent dialogs.
				DialogBaseClass::Set_Default_Command_Handler (Default_On_Command);

				// Put up the first dialog.
				Default_On_Command (NULL, 0, 0, 0);

				// Start the install music.
				if (InstallMusic != NULL) InstallMusic->Play();

				TTimerClass <SafeTimerClass> timer;

				// Run dialog loop.
				while (!CancelApplication) {

					DialogMgrClass::On_Frame_Update();

					WW3D::Begin_Render (true, true, black);
					if (GameInFocus) {
						DialogMgrClass::Render();
						if (InstallMusic != NULL) {
							if (InstallMusic->Get_State() == AudibleSoundClass::STATE_PAUSED) InstallMusic->Resume();
						}
						if (!timer.Is_Active()) timer.Start();
					} else {
						if (InstallMusic != NULL) {
							if (InstallMusic->Get_State() == AudibleSoundClass::STATE_PLAYING) InstallMusic->Pause();
						}
						if (timer.Is_Active()) timer.Stop();
						Sleep (50);
					}
					WW3D::End_Render();

					WW3D::Sync (timer.Value());
					WWAudioClass::Get_Instance()->On_Frame_Update();
					Windows_Message_Handler();
				}

				// Before shutting down, allow a short time for sound effects to 'play out'.
				time = timer.Value();
				while (timer.Value() < time + playouttime) {
					WW3D::Sync (timer.Value());
					WWAudioClass::Get_Instance()->On_Frame_Update();
					Windows_Message_Handler();
				}

				// Clear the frame buffer.
				WW3D::Begin_Render (true, true, black);
				WW3D::End_Render();
			}
			Shutdown();
		}
	}
	
	catch (const WideStringClass &errormessage) {

		Shutdown();
		throw (errormessage);
	}
}


/***********************************************************************************************
 * InstallerClass::Check_Source --																				  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Check_Source()
{
	ArgvClass commandlineparser (false, true);			

	// Use the simple file factory to read INI files from the current working directory.
	_TheFileFactory = _TheSimpleFileFactory;

	#if PERMIT_INSTALL_FROM_NETWORK
	if (!commandlineparser.Find ("-N")) {
	#else
	if (true) {
	#endif
 	
		char sourcedriveletter;	

		// Prompt for a CD with correct volume label.
		if (Prompt_Install_CD (RxWideStringClass (IDS_INSTALL_CD_VOLUME_NAME), sourcedriveletter)) {
		
			char sourcedrive [] = "?:";
		
			sourcedrive [0] = tolower (sourcedriveletter);
			SourceGamePath  = sourcedrive;
			SourceWOLPath   = sourcedrive;

		} else {

			// No CD found.
			return (false);
		}

	} else {

		#if PERMIT_INSTALL_FROM_NETWORK

		Get_Current_Directory (SourceGamePath);
		Remove_Trailing_Name (SourceGamePath);
		SourceWOLPath = SourceGamePath;
		#endif
	}

	// Initialize the game and WOL source paths.
	SourceGamePath += L"\\";
	SourceGamePath += RxWideStringClass (IDS_GAME_DIRECTORY_NAME);
	SourceWOLPath	+= L"\\";
	SourceWOLPath  += RxWideStringClass (IDS_WOL_DIRECTORY_NAME);

	// Look in the current directory for Setup.ini file.
	if (!SetupIni->Is_Loaded()) {
		if (!SetupIni->Load (RxStringClass (IDS_SETUP_INI_FILENAME))) FATAL_APP_ERROR (IDS_INI_FILE_NOT_FOUND_OR_LOADED);
	}

	// Look in the current directory for <product name>.ini file.
	if (!GameIni->Is_Loaded()) {
		if (!GameIni->Load (RxStringClass (IDS_GAME_INI_FILENAME))) FATAL_APP_ERROR (IDS_INI_FILE_NOT_FOUND_OR_LOADED);
	}

	// Look in the WOL source directory for WOLAPI.ini file.
	if (!WOLIni->Is_Loaded()) {

		WideStringClass wolinipathname;
		StringClass		 multibytewolinipathname;

		wolinipathname  = SourceWOLPath;
		wolinipathname += L"\\";
		wolinipathname += RxWideStringClass (IDS_WOL_INI_FILENAME);
	
		multibytewolinipathname = wolinipathname;
		if (!WOLIni->Load (multibytewolinipathname)) FATAL_APP_ERROR (IDS_INI_FILE_NOT_FOUND_OR_LOADED);
	}

	// Count no. and total file size of contents of game directory.
	Count_Source_Files (SourceGamePath, GameFileCount, GameSize);
	
	//	Add game file count and size contained in CAB files.
	// WARNING: Only values < 2Gb will be reported correctly due to 32-bit limit on INIClass::Get_Int().
	//				However, result is returned in 64-bits so that all callers are ready to handle >= 2Gb when the need arises.
	GameFileCount += GameIni->Get_Int (INI_FILES_SECTION, INI_NUM_FILES_ENTRY);
	GameSize		  += GameIni->Get_Int (INI_FILES_SECTION, INI_TOTAL_DISK_SPACE_NEEDED_ENTRY);

	// Count no. and total file size of contents of WOL directory.
	// NOTE: WOL directory does not contain CAB files.
	Count_Source_Files (SourceWOLPath, WOLFileCount, WOLSize);

	// Return success.
	return (true);
}


/***********************************************************************************************
 * InstallerClass::Count_Source_Files --																		  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Count_Source_Files (const WideStringClass &sourcepath, unsigned &filecount, __int64 &filesize)
{
	const WCHAR *wildcardname = L"*.*";

	WideStringClass sourcepathname (sourcepath);
	StringClass		 multibytesourcewolpath;	
	WIN32_FIND_DATA finddata;	
	HANDLE			 handle;
	bool				 done = false;

	sourcepathname += L"\\";
	sourcepathname += wildcardname;
	multibytesourcewolpath = sourcepathname;
	handle = FindFirstFile (multibytesourcewolpath, &finddata);
	if (handle != INVALID_HANDLE_VALUE) {
		while (!done) {
			
			WideStringClass filename (finddata.cFileName);
			
			if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			
				// Filter out system diretories.
				if (filename [0] != L'.') {
				
					WideStringClass subsourcepath;

					// Recurse on subdirectory..
					subsourcepath  = sourcepath;
					subsourcepath += L"\\";
					subsourcepath += filename;
					Count_Source_Files (subsourcepath, filecount, filesize);
				}

			} else {
				
				const WCHAR *cabextension = L".cab";

  				WCHAR extension [_MAX_EXT];

				// Filter out CAB files.
				_wsplitpath (filename, NULL, NULL, NULL, extension);
				if (_wcsicmp (cabextension, extension) != 0) {
					filecount++;
					filesize += (finddata.nFileSizeHigh * ((__int64) MAXDWORD) + 1) + finddata.nFileSizeLow;
				}
			}
			if (done = FindNextFile (handle, &finddata) == 0) {
				if (GetLastError() != ERROR_NO_MORE_FILES) FATAL_SYSTEM_ERROR;
			}
		}
		if (!FindClose (handle)) FATAL_SYSTEM_ERROR;
	}
}


/***********************************************************************************************
 * InstallerClass::Check_Existing_Install --																	  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Check_Existing_Install()
{
	WideStringClass pathname;
	StringClass		 multibytepathname;	
	WIN32_FIND_DATA finddata;	
	HANDLE			 handle;
	bool				 fresh;
	bool				 older;

	// Check for existence of target game .exe.
	fresh = true;
	if (_RegistryManager.Get_Target_Game_Pathname (pathname)) {
		multibytepathname = pathname;
		handle = FindFirstFile (multibytepathname, &finddata);
		if (handle != INVALID_HANDLE_VALUE) {
			FindClose (handle);
			fresh = false;
		}
	}
	FreshGameInstall = fresh;

 	// Iterate over all WOL components. Install is a fresh install if one or more components are
	// missing from the user's system. The target is deemed older that the source if one or more
	// target version nos. is older than source.
	const char *sectionname [RegistryManagerClass::COMPONENT_COUNT] = {INI_WOLAPI_SECTION, INI_WOLREGISTER_SECTION, INI_WOLBROWSER_SECTION};

	fresh = false;
	older = false;
	for (int c = RegistryManagerClass::COMPONENT_FIRST; c < RegistryManagerClass::COMPONENT_COUNT; c++) {
		
		WideStringClass pathname;
		StringClass		 multibytepathname;	
		WIN32_FIND_DATA finddata;	
		HANDLE			 handle;
		DWORD				 major, minor, sourceversion, targetversion;

		// Check for existence of target .exe.
		if (!_RegistryManager.Get_Target_WOL_Pathname ((RegistryManagerClass::WOLComponentEnum) c, pathname)) {
			fresh = true;
			older = true;
			break;
		}
		multibytepathname = pathname;
		handle = FindFirstFile (multibytepathname, &finddata); 
		if (handle == INVALID_HANDLE_VALUE) {
			fresh = true;
			older = true;
			break;
		}
		FindClose (handle);

		// Compare source and target versions.
		if (!_RegistryManager.Get_Target_WOL_Version ((RegistryManagerClass::WOLComponentEnum) c, targetversion)) {
			older = true;
			break;
		}
		major = WOLIni->Get_Int (sectionname [c], INI_VERSION_MAJOR_ENTRY, 0x1);
		minor = WOLIni->Get_Int (sectionname [c], INI_VERSION_MINOR_ENTRY, 0x0);
		sourceversion = (major << 16) + minor;
		if (sourceversion > targetversion) {
			older = true;
			break;
		}
	}

	FreshWOLInstall = fresh;
	TargetWOLOlder  = older;	
}


/***********************************************************************************************
 * InstallerClass::Initialize --																					  *
 *																															  *			
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Initialize()
{
	const char *transitionmusicfilename	= "TransitionMusic.mp3";
	const char *installmusicfilename		= "InstallMusic.mp3";
	
	// All subsequent file factory access uses the mix file.
	_TheFileFactory = MixFileFactory;

	// Initialize WW systems.
	WWMath::Init();
	WwmathInitialized = true;
	if (!WW3D_Initialize()) FATAL_APP_ERROR (IDS_CANNOT_INITIALIZE_ANY_D3D_DEVICE);

	// Initialize the music.
	AudioSystem = new WWAudioClass();
	WWAudioClass::Get_Instance()->Initialize();
	WWAudioClass::Get_Instance()->Set_File_Factory (_TheFileFactory);
	TransitionMusic = WWAudioClass::Get_Instance()->Create_Sound_Effect (transitionmusicfilename);
	if (TransitionMusic != NULL) {
		TransitionMusic->Set_Type (AudibleSoundClass::TYPE_MUSIC);
	}
	InstallMusic = WWAudioClass::Get_Instance()->Create_Sound_Effect (installmusicfilename);
	if (InstallMusic != NULL) {
		InstallMusic->Set_Type (AudibleSoundClass::TYPE_MUSIC);
		InstallMusic->Set_Loop_Count (0);
	}
}


/***********************************************************************************************
 * InstallerClass::Shutdown --																					  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Shutdown()
{
	// Close any open dialogs.
	END_DIALOG (WelcomeDialog)
	REF_PTR_RELEASE (WelcomeDialog)
	END_DIALOG (NDADialog)
	REF_PTR_RELEASE (NDADialog)
	END_DIALOG (LicenseDialog)
	REF_PTR_RELEASE (LicenseDialog)
	END_DIALOG (SerialDialog)
	REF_PTR_RELEASE (SerialDialog)
	END_DIALOG (WhatToInstallDialog)
	REF_PTR_RELEASE (WhatToInstallDialog)
	END_DIALOG (GameDirectoryDialog)
	REF_PTR_RELEASE (GameDirectoryDialog)
	END_DIALOG (GameFolderDialog)
	REF_PTR_RELEASE (GameFolderDialog)
	END_DIALOG (WOLDirectoryDialog)
	REF_PTR_RELEASE (WOLDirectoryDialog)
	END_DIALOG (WOLFolderDialog)
	REF_PTR_RELEASE (WOLFolderDialog)
	END_DIALOG (ReviewDialog)
	REF_PTR_RELEASE (ReviewDialog)
	END_DIALOG (CopyDialog)
	REF_PTR_RELEASE (CopyDialog)
	END_DIALOG (WOL1Dialog)
	REF_PTR_RELEASE (WOL1Dialog)
	END_DIALOG (WOL2Dialog)
	REF_PTR_RELEASE (WOL2Dialog)
	END_DIALOG (FinalDialog)
	REF_PTR_RELEASE (FinalDialog)
	END_DIALOG (CardSelectionDialog)
	REF_PTR_RELEASE (CardSelectionDialog)

	// Shutdown the music.
	if (TransitionMusic != NULL) {
		TransitionMusic->Stop();
		TransitionMusic->Release_Ref();
	}
	if (InstallMusic != NULL) {
		InstallMusic->Stop();
		InstallMusic->Release_Ref();
	}
	if (AudioSystem != NULL) {
		WWAudioClass::Get_Instance()->Shutdown();
		delete AudioSystem;
	}

	// Shutdown WW systems.
	WW3D_Shutdown();
	if (WwmathInitialized) WWMath::Shutdown();
}


/***********************************************************************************************
 * InstallerClass::WW3D_Initialize --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::WW3D_Initialize (int cardselection)
{
	const char *stylemgrini = "StyleMgr.ini";
	
	bool windowedmode;

	// Initialize WW3D.
	AssetManager = new WW3DAssetManager;
	AssetManager->Set_WW3D_Load_On_Demand (true);
	WW3D::Init (MainWindow);	
	WW3D::Enable_Sorting (true);
	WW3D::Set_Thumbnail_Enabled (false);
	AssetStatusClass::Peek_Instance()->Enable_Reporting (false);

	// All subsequent file factory access uses the mix file.
	_TheFileFactory = MixFileFactory;

	ArgvClass commandlineparser (false, true);
	
	windowedmode = (commandlineparser.Find ("-W") != NULL);

	// For each available video card...
	for (int d = 0; d < WW3D::Get_Render_Device_Count(); d++) {

		// Does this card index match the supplied card index or should we try all cards until success?
		if ((cardselection == -1) || (cardselection == d)) {
		
			// Set the card index and whether windowed mode in the registry.
			WW3D::Registry_Save_Render_Device (APPLICATION_SUB_KEY_NAME_RENDER, d, -1, -1, -1, 0, -1);
		
			// Configure video for the user.
			Auto_Configure();

			// Set UV bias according to registry.
			RegistryClass optionsregistry (APPLICATION_SUB_KEY_NAME_OPTIONS);
			if (optionsregistry.Is_Valid()) {
				WW3D::Set_Screen_UV_Bias (optionsregistry.Get_Int ("ScreenUVBias", 1) != 0);
			}

			// Override the windowed mode.
			RegistryClass renderregistry (APPLICATION_SUB_KEY_NAME_RENDER);
			if (optionsregistry.Is_Valid()) {
				renderregistry.Set_Int ("RenderDeviceWindowed", windowedmode ? 1 : 0);
			}

			// Attempt to initialize the device.
			if (WW3D::Registry_Load_Render_Device (APPLICATION_SUB_KEY_NAME_RENDER, true) == WW3D_ERROR_OK) {

				// Clear frame buffers.
				for (int frame = 0; frame < 3; frame++) {
					WW3D::Begin_Render (true, true, Vector3 (0.0f,0.0f,0.0f));
					WW3D::End_Render();
				}

				// Success! Initialize dialog system.
				DialogMgrClass::Initialize (stylemgrini);
				DialogMgrInitialized = true;
				UIInput = NEW_REF (InstallerUIInputClass, ());
				UIInput->InitIME (MainWindow);
				DialogMgrClass::Install_Input (UIInput);

				return (true);
			}
		}
	}

	// Failure.
	return (false);
}


/***********************************************************************************************
 * InstallerClass::WW3D_Shutdown --																				  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::WW3D_Shutdown()
{
	// Clear frame buffers.
	for (int frame = 0; frame < 3; frame++) {
		WW3D::Begin_Render (true, true, Vector3 (0.0f,0.0f,0.0f));
		WW3D::End_Render();
	}

	// Shutdown WW3D (if previously initialized).
	REF_PTR_RELEASE (UIInput);
	if (DialogMgrInitialized) DialogMgrClass::Shutdown();
	if (WW3D::Is_Initted()) WW3D::Shutdown();
	if (AssetManager != NULL) {
		AssetManager->Free_Assets();
		delete AssetManager;
	}
}


/***********************************************************************************************
 * InstallerClass::Auto_Configure --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Auto_Configure()
{
   PROCESS_INFORMATION processinfo;
	STARTUPINFO			  startupinfo;

	memset (&startupinfo, 0, sizeof (STARTUPINFO));
	startupinfo.cb = sizeof (STARTUPINFO);

   // Start the child process. 
   if (CreateProcess (NULL,								// Address of module name
							 "WWConfig.exe -autoconfig",	// Address of command line
							 NULL,								// Address of process security attributes
							 NULL,								// Address of thread security attributes
							 FALSE,								// Does new process inherit handles?
							 0,									// Creation flags
							 NULL,								// Address of new environment block
							 NULL,								// Address of current directory name
							 &startupinfo,						// Address of STARTUPINFO
							 &processinfo)) {					// Address of PROCESS_INFORMATION

		// Wait until child process exits.
	   WaitForSingleObject (processinfo.hProcess, INFINITE);

		// Close process and thread handles. 
	   CloseHandle (processinfo.hProcess);
	   CloseHandle (processinfo.hThread);
   }
}


/***********************************************************************************************
 * InstallerClass::Install_* --																					  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Install_Game()
{
	WWASSERT (WhatToInstallDialog != NULL);
	return (WhatToInstallDialog->Install_Game());
}
	  
bool InstallerClass::Install_WOL()
{
	WWASSERT (WhatToInstallDialog != NULL);
	return (WhatToInstallDialog->Install_WOL());
}

bool InstallerClass::Install_Game_Shortcut()
{
	WWASSERT (WhatToInstallDialog != NULL);
	return (WhatToInstallDialog->Install_Game() && WhatToInstallDialog->Install_Game_Shortcut());
}


/***********************************************************************************************
 * InstallerClass::Get_Target_* --																				  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
const WCHAR *InstallerClass::Get_Target_Game_Path (WideStringClass &path)
{
	WWASSERT (GameDirectoryDialog != NULL);
	return (GameDirectoryDialog->Get_Path (path));
}

const WCHAR *InstallerClass::Get_Target_WOL_Path (WideStringClass &path)
{
	WWASSERT (WOLDirectoryDialog != NULL);
	return (WOLDirectoryDialog->Get_Path (path));
}

const WCHAR *InstallerClass::Get_Target_Game_Folder (WideStringClass &folder)
{
	WWASSERT (GameFolderDialog != NULL);
	return (GameFolderDialog->Get_Folder (folder));
}

const WCHAR *InstallerClass::Get_Target_WOL_Folder (WideStringClass &folder)
{
	WWASSERT (WOLFolderDialog != NULL);
	return (WOLFolderDialog->Get_Folder (folder));
}


/***********************************************************************************************
 * InstallerClass::Get_Target_Sub_Path --																		  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Get_Target_Sub_Path (unsigned index, WideStringClass &subpath)
{
	StringClass		 multibytesubdirectoryname, entryname;
	WideStringClass subdirectoryname, targetpath;
	char				 numberstring [MAX_NUMBER_STRING_LENGTH];	

	entryname  = INI_SUBDIRECTORY_ENTRY_PREFIX;
	entryname += ltoa (index, numberstring, 10);
	GameIni->Get_String (multibytesubdirectoryname, INI_SUBDIRECTORY_SECTION, entryname);
	subdirectoryname = multibytesubdirectoryname;
	if (subdirectoryname.Get_Length() > 0) {
		subpath  = Get_Target_Game_Path (targetpath);
		subpath += L"\\";
		subpath += subdirectoryname;
		return (true);
	} else {
		return (false);
	}
}


/***********************************************************************************************
 * InstallerClass::Get_Game_Size --																				  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
__int64 InstallerClass::Get_Game_Size (bool ondisk)
{
	__int64 bytecount = 0;

	if (Install_Game()) {

		bytecount = GameSize;

		if (ondisk) {

			// Account for cluster padding.
			bytecount += Cluster_Padding (GameFileCount);

			// Account for any .key (Encryption) and .wsu (Uninstall log) files that will be created by the Installer.
			bytecount += INSTALLER_NOMINAL_BYTE_COUNT;
		}
	}

	return (bytecount);
}


/***********************************************************************************************
 * InstallerClass::Get_WOL_Size --																				  *		
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
__int64 InstallerClass::Get_WOL_Size (bool ondisk)
{
	__int64 bytecount = 0;

	if (Install_WOL()) {

		bytecount = WOLSize;

		if (ondisk) {
		
			// Account for cluster padding.
			bytecount += Cluster_Padding (WOLFileCount);

			// Account for any .key (Encryption) and .wsu (Uninstall log) files that will be created by the Installer.
			bytecount += INSTALLER_NOMINAL_BYTE_COUNT;
		}
	}

	return (bytecount);
}


/***********************************************************************************************
 * InstallerClass::Get_Total_Size --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
__int64 InstallerClass::Get_Total_Size (bool ondisk)
{
	return (Get_Game_Size (ondisk) + Get_WOL_Size (ondisk));
}


/***********************************************************************************************
 * InstallerClass::Get_WOL_Space_Available --																  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Get_WOL_Space_Available (const WideStringClass &path, __int64 &diskspace)
{
	WideStringClass targetgamepath, targetwolpath;

	// If the game is to be installed on the same drive, subtract game space needed from disk space available.
	if (Get_Disk_Space_Available (path, diskspace)) {
		if (Install_Game() && (Get_Target_Game_Path (targetgamepath) [0] == Get_Target_WOL_Path (targetwolpath) [0])) {
			diskspace -= Get_Game_Size (true);
			WWASSERT (diskspace >= 0);
		}
		return (true);
	}
	return (false);
}


/***********************************************************************************************
 * InstallerClass:Get_Serial_Number: --																		  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
const char *InstallerClass::Get_Serial_Number (StringClass &serialnumber)
{
	bool valid;
	
	WWASSERT (SerialDialog != NULL);
	valid = SerialDialog->Get_Serial_Number (serialnumber);
	WWASSERT (valid);
	return (serialnumber);
}


/***********************************************************************************************
 * InstallerClass::Dialog_Callback --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Dialog_Callback (DialogBaseClass *dialog, int ctrl_id, int message_id, DWORD param)
{
	InstallMenuDialogClass *installmenudialog = (InstallMenuDialogClass*) dialog;
	
	// If no dialog is currently active display the welcome dialog.
	if (installmenudialog == NULL) {

		REGISTER_DIALOG (WelcomeDialogClass, WelcomeDialog, ())

	} else {

 		// Is the current dialog the Welcome dialog?
		if (installmenudialog->As_WelcomeDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					if (!Beta_Test()) {
						REGISTER_DIALOG (LicenseDialogClass, LicenseDialog, ())
					} else {
						REGISTER_DIALOG (NDADialogClass, NDADialog, ())
					}
					return;
			}
		}

		// Is the current dialog the NDA agreement?
		if (installmenudialog->As_NDADialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (LicenseDialogClass, LicenseDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (WelcomeDialogClass, WelcomeDialog, ())
					return;
			}
		}

		// Is the current dialog the License dialog?
		if (installmenudialog->As_LicenseDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (SerialDialogClass, SerialDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					if (!Beta_Test()) {
						REGISTER_DIALOG (WelcomeDialogClass, WelcomeDialog, ())
					} else {
						REGISTER_DIALOG (NDADialogClass, NDADialog, ())
					}
					return;
			}
		}

		// Is the current dialog the Serial dialog?
		if (installmenudialog->As_SerialDialogClass()) {

			switch (ctrl_id) {
					
				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (WhatToInstallDialogClass, WhatToInstallDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (LicenseDialogClass, LicenseDialog, ())
					return;
			}
		}

		// Is the current dialog the What To Install dialog?
		if (installmenudialog->As_WhatToInstallDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)

					// Does the user want to install the game? If not skip to WOL dialogs.	
					if (Install_Game()) {
						REGISTER_DIALOG (GameDirectoryDialogClass, GameDirectoryDialog, ())
					} else {
						REGISTER_DIALOG (WOLDirectoryDialogClass, WOLDirectoryDialog, ())
					}
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (SerialDialogClass, SerialDialog, ())
					return;
			}
		}

		// Is the current dialog the Game Directory dialog?
		if (installmenudialog->As_GameDirectoryDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (GameFolderDialogClass, GameFolderDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (WhatToInstallDialogClass, WhatToInstallDialog, ())
					return;
			}
		}

		// Is the current dialog the Game Folder dialog?
		if (installmenudialog->As_GameFolderDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					
					// Does the user want to install WOL? If not skip to Review dialog.	
					if (Install_WOL()) {
						REGISTER_DIALOG (WOLDirectoryDialogClass, WOLDirectoryDialog, ())
					} else {
						REGISTER_DIALOG (ReviewDialogClass, ReviewDialog, ())
					}
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (GameDirectoryDialogClass, GameDirectoryDialog, ())
					return;
			}
		}

		// Is the current dialog the Online Directory dialog?
		if (installmenudialog->As_WOLDirectoryDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (WOLFolderDialogClass, WOLFolderDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					
					// Does the user want to install the game? If not skip to the What To Install dialog.
					if (Install_Game()) {
						REGISTER_DIALOG (GameFolderDialogClass, GameFolderDialog, ())
					} else {
						REGISTER_DIALOG (WhatToInstallDialogClass, WhatToInstallDialog, ())
					}
					return;
			}
		}
			
		// Is the current dialog the Online Folder dialog?
		if (installmenudialog->As_WOLFolderDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (ReviewDialogClass, ReviewDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (WOLDirectoryDialogClass, WOLDirectoryDialog, ())
					return;
			}
		}

		// Is the current dialog the Review dialog?
		if (installmenudialog->As_ReviewDialogClass()) {

			switch (ctrl_id) {

	 			case IDCANCEL:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					return;
	
				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (CopyDialogClass, CopyDialog, ())
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)
					
					// Does the user want to install WOL? If not skip to game folder.
					if (Install_WOL()) {
						REGISTER_DIALOG (WOLFolderDialogClass, WOLFolderDialog, ())
					} else {
						REGISTER_DIALOG (GameFolderDialogClass, GameFolderDialog, ())
					}
					return;
			}
		}

		// Is the current dialog the Copy dialog.
		if (installmenudialog->As_CopyDialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
					
					if (((CopyDialogClass*) installmenudialog->As_CopyDialogClass())->Was_Successful()) {
							
						// Copying was successful - advance straight to final dialog.
						UNREGISTER_DIALOG (dialog)
						REGISTER_DIALOG (FinalDialogClass, FinalDialog, (false))

					} else {

						// Copying was not successful - cancel as normal.
						DO_CANCEL
						UNREGISTER_DIALOG (dialog)
					}
					return;

				case IDOK:
					UNREGISTER_DIALOG (dialog)
					
					// Does the user have existing Westwood Online accounts?
					if (Has_WOL_Account()) {
						REGISTER_DIALOG (WOL1DialogClass, WOL1Dialog, ())
					} else {
						REGISTER_DIALOG (FinalDialogClass, FinalDialog, (false))
					}
					return;
			}
		}	

		// Is the current dialog the WOL1 dialog?
		if (installmenudialog->As_WOL1DialogClass()) {

			switch (ctrl_id) {

				case IDC_BUTTON_YES:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (WOL2DialogClass, WOL2Dialog, ())
					return;

				case IDC_BUTTON_NO:
				case IDCANCEL:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (FinalDialogClass, FinalDialog, (true))
					return;
			}
		}

		// Is the current dialog the WOL2 dialog?
		if (installmenudialog->As_WOL2DialogClass()) {

			switch (ctrl_id) {

				case IDCANCEL:
				case IDOK:
					UNREGISTER_DIALOG (dialog)
					REGISTER_DIALOG (FinalDialogClass, FinalDialog, (true))
					return;
			}
		}

		// Is the current dialog the Final dialog?
		if (installmenudialog->As_FinalDialogClass()) {

			switch (ctrl_id) {

				case IDOK:
					DO_CANCEL
					UNREGISTER_DIALOG (dialog)
					
					// Extract any information from dialogs that will be needed on program end.
					if (Install_Game()) {
						Get_Target_Game_Path (TargetGamePath);
						if (FinalDialog->Run_Game()) {
							ProgEndAction = ACTION_RUN_GAME;
						} else {
							if (FinalDialog->Display_Readme()) {
								ProgEndAction = ACTION_DISPLAY_README;
							}
						}
					}
					return;

				case IDC_BUTTON_BACK:
					UNREGISTER_DIALOG (dialog)

					// If WOL2Dialog exists return to it - otherwise return to WOL1Dialog.
					if (WOL2Dialog != NULL) {
						REGISTER_DIALOG (WOL2DialogClass, WOL2Dialog, ())
					} else {
						WWASSERT (WOL1Dialog != NULL);
						REGISTER_DIALOG (WOL1DialogClass, WOL1Dialog, ())
					}
					return;
			}
		}
	}
}


/***********************************************************************************************
 * Default_On_Command -- Callback for all menu dialogs.													  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool CALLBACK Default_On_Command (DialogBaseClass *dialog, int ctrl_id, int message_id, DWORD param)
{
	_Installer.Dialog_Callback (dialog, ctrl_id, message_id, param);
	return (true);
}


/***********************************************************************************************
 * WWDebug_Message_Callback -- WWDebug message callback to define the behavior of WWDEBUG_SAY()*
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void WWDebug_Message_Callback (DebugType type, const char *message)
{
	OutputDebugString (message);
}


/***********************************************************************************************
 * WWAssert_Callback -- WWAssert callback to define the behavior of WWASSERT().					  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void WWAssert_Callback (const char *message)
{
	OutputDebugString (message);
	_asm int 0x03
}


/***********************************************************************************************
 * InstallerClass::Can_Use_IGR_Settings --																	  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Can_Use_IGR_Settings()
{
	return (SetupIni->Get_Hex (INI_SETUP_SECTION, INI_CAN_USE_IGR_SETTNGS_ENTRY, 0x0) > 0x0);
}


/***********************************************************************************************
 * InstallerClass::Use_IGR_Settings --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Use_IGR_Settings()
{
	WWASSERT (WhatToInstallDialog != NULL);
	return (WhatToInstallDialog->Use_IGR_Settings());
}


/***********************************************************************************************
 * InstallerClass::Beta_Test --																					  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Beta_Test()
{
	const char *ndafilename = "NDA.txt";

	HANDLE			 handle;
	WIN32_FIND_DATA finddata;	
	
	// Use existence of Beta NDA to indicate whether this is the Beta test version.
	handle = FindFirstFile (ndafilename, &finddata);
	if (handle != INVALID_HANDLE_VALUE) {
		FindClose (handle);
		return (true);
	}
	return (false);
}


/***********************************************************************************************
 * InstallerClass::Update_Registry --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Update_Registry()
{
	if (Install_Game()) {

		WideStringClass installpathname, folderpath, shortcutpathname;
		StringClass		 serialnumber, encryptedserialnumber;
		DWORD				 languageid, sku, version;
		WideStringClass pathname, gamefolder, gamepath;
		DWORD				 major, minor;

		Get_Target_Game_Path (installpathname);
		installpathname += L"\\";
		installpathname += RxWideStringClass (IDS_GAME_EXECUTABLE_FILENAME);

		_RegistryManager.Get_Folder_Path (folderpath);
		folderpath += L"\\";
		folderpath += Get_Target_Game_Folder (gamefolder);
		folderpath += L"\\";
		folderpath += TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME);

		_RegistryManager.Get_Desktop_Path (shortcutpathname);
		shortcutpathname += L"\\";
		shortcutpathname += TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME);
		shortcutpathname += L".lnk";

		languageid = SetupIni->Get_Hex (INI_SETUP_SECTION, INI_LANGUAGE_ENTRY, 0x0);

		// Create encryption file in target directory.
		Get_Target_Game_Path (pathname);
		pathname += L"\\";
		pathname += RxWideStringClass (IDS_SERIAL_ENCRYPTION_FILENAME);
		Create_Encryption_File (pathname);

		// Encrypt the serial no.
		Encrypt (Get_Serial_Number (serialnumber), pathname, encryptedserialnumber);

		sku = SetupIni->Get_Int (INI_SETUP_SECTION, INI_SKU_ENTRY, 0x0);

		major	  = SetupIni->Get_Int (INI_SETUP_SECTION, INI_VERSION_MAJOR_ENTRY, 0x1);
		minor	  = SetupIni->Get_Int (INI_SETUP_SECTION, INI_VERSION_MINOR_ENTRY, 0x0);
		version = (major << 16) + minor;

		_RegistryManager.Register_Game (TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME),
												  installpathname,
												  folderpath,
												  shortcutpathname,
												  languageid,
												  encryptedserialnumber,
												  sku,
												  version);
	}

	if (Install_WOL()) {
	
		WideStringClass folderpath, wolapipathname, wolregisterpathname, wolbrowserpathname;
		DWORD				 sku, version;
		WideStringClass WOLfolder;
		DWORD				 major, minor;

		_RegistryManager.Get_Folder_Path (folderpath);
		folderpath += L"\\";
		folderpath += Get_Target_WOL_Folder (WOLfolder);
		folderpath += L"\\";
		folderpath += TxWideStringClass (IDS_WOL_PRODUCT_NAME, IDS_RESOURCE_WOL_PRODUCT_NAME);

		Get_Target_WOL_Path (wolapipathname);
		wolapipathname += L"\\";
		wolapipathname += RxWideStringClass (IDS_WOLAPI_DLL_FILENAME);

		sku = atol (RxStringClass (IDS_WOLAPI_SKU));

		major	  = WOLIni->Get_Int (INI_WOLAPI_SECTION, INI_VERSION_MAJOR_ENTRY, 0x1);
		minor	  = WOLIni->Get_Int (INI_WOLAPI_SECTION, INI_VERSION_MINOR_ENTRY, 0x0);
		version = (major << 16) + minor;

		_RegistryManager.Register_WOLAPI (folderpath,
													 wolapipathname,
													 TxWideStringClass (IDS_WOL_PRODUCT_NAME, IDS_RESOURCE_WOL_PRODUCT_NAME),
													 sku,
													 version,
													 Use_IGR_Settings());

		Get_Target_WOL_Path (wolregisterpathname);
		wolregisterpathname += L"\\";
		wolregisterpathname += RxWideStringClass (IDS_INTERNET_REGISTRATION_FILENAME);

		sku = atol (RxStringClass (IDS_WOLREGISTER_SKU));

		major	  = WOLIni->Get_Int (INI_WOLREGISTER_SECTION, INI_VERSION_MAJOR_ENTRY, 0x1);
		minor	  = WOLIni->Get_Int (INI_WOLREGISTER_SECTION, INI_VERSION_MINOR_ENTRY, 0x0);
		version = (major << 16) + minor;

		_RegistryManager.Register_WOLRegister (folderpath,
															wolregisterpathname,
															TxWideStringClass (IDS_WOLREGISTER_NAME, IDS_RESOURCE_WOLREGISTER_NAME),
															sku,
															version);

		Get_Target_WOL_Path (wolbrowserpathname);
		wolbrowserpathname += "\\";
		wolbrowserpathname += RxWideStringClass (IDS_WOLBROWSER_DLL_FILENAME);

		major	  = WOLIni->Get_Int (INI_WOLBROWSER_SECTION, INI_VERSION_MAJOR_ENTRY, 0x1);
		minor	  = WOLIni->Get_Int (INI_WOLBROWSER_SECTION, INI_VERSION_MINOR_ENTRY, 0x0);
		version = (major << 16) + minor;

		_RegistryManager.Register_WOLBrowser (wolbrowserpathname,
														  TxWideStringClass (IDS_WOLBROWSER_NAME, IDS_RESOURCE_WOLBROWSER_NAME),
														  version);

		// Now register the DLL's.
		Register_COM_Server (wolapipathname);
		Register_COM_Server (wolbrowserpathname);
	}

	// Update WOLAPI.
	_RegistryManager.Update_WOLAPI();
}


/***********************************************************************************************
 * InstallerClass::Create_Links --																				  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Create_Links()
{
	if (CoInitialize (NULL) != S_OK) FATAL_APP_ERROR (IDS_COM_ERROR);

	if (Install_Game()) {

		WideStringClass folderpath, gamefolder, gamepathname, readmepathname;
		WideStringClass readmeiconpathname, uninstallpathname, internetpathname, wwconfigpathname;
		WideStringClass grabpatches ("GrabPatches");

		// Create subdirectory in folder path for menu item links.
		_RegistryManager.Get_Folder_Path (folderpath);
		folderpath += L"\\";
		folderpath += Get_Target_Game_Folder (gamefolder);
		folderpath += L"\\";
		folderpath += TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME);
		Create_Directory (folderpath);

		// Create Renegade menu item.
		Get_Target_Game_Path (gamepathname);
		gamepathname += L"\\";
		gamepathname += RxWideStringClass (IDS_GAME_EXECUTABLE_FILENAME);
	  	Create_File_Link (folderpath, TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME), gamepathname, gamepathname);

		// Create Auto Update menu item.
		Create_File_Link (folderpath, TxWideStringClass (IDS_AUTO_UPDATE_TITLE, IDS_RESOURCE_AUTO_UPDATE_TITLE), gamepathname, gamepathname, &grabpatches);

		// Create Readme menu item.
		Get_Target_Game_Path (readmepathname);
		readmepathname += L"\\";
		readmepathname += RxWideStringClass (IDS_WORD_README_FILENAME);
		Get_Target_Game_Path (readmeiconpathname);
		readmeiconpathname += L"\\";
		readmeiconpathname += RxWideStringClass (IDS_README_ICON_FILENAME);
		Create_File_Link (folderpath, TxWideStringClass (IDS_README_TITLE, IDS_RESOURCE_README_TITLE), readmepathname, readmeiconpathname);

		// Create Uninstall menu item.
		Get_Target_Game_Path (uninstallpathname);
		uninstallpathname += L"\\";
		uninstallpathname += RxWideStringClass (IDS_GAME_UNINSTALL_FILENAME);
		Create_File_Link (folderpath, TxWideStringClass (IDS_GAME_UNINSTALL_TITLE, IDS_RESOURCE_GAME_UNINSTALL_TITLE), uninstallpathname, uninstallpathname); 

		// Create Internet Registration menu item.
		Get_Target_Game_Path (internetpathname);
		internetpathname += L"\\";
		internetpathname += RxWideStringClass (IDS_INTERNET_REGISTRATION_FILENAME);
		Create_File_Link (folderpath, TxWideStringClass (IDS_INTERNET_REGISTRATION_TITLE, IDS_RESOURCE_INTERNET_REGISTRATION_TITLE), internetpathname, internetpathname);

		// Create GameSpy Arcade menu item.
		// LOCALIZATION NOTE: The French have decided not to include this link.
		if (SetupIni->Get_Hex (INI_SETUP_SECTION, INI_LANGUAGE_ENTRY, 0x0) != 0x3) {
			Create_URL_Link (folderpath, TxWideStringClass (IDS_PLAY_ONLINE_WITH_GAMESPY, IDS_RESOURCE_PLAY_ONLINE_WITH_GAMESPY), L"http://www.gamespyarcade.com/features/launch.asp?svcname=ccrenegade&distID=391");
		}

		// Create link to WWConfig.
		Get_Target_Game_Path (wwconfigpathname);
		wwconfigpathname += L"\\";
		wwconfigpathname += RxWideStringClass (IDS_WWCONFIG_FILENAME);
		Create_File_Link (folderpath, TxWideStringClass (IDS_WWCONFIG_TITLE, IDS_RESOURCE_WWCONFIG_TITLE), wwconfigpathname, wwconfigpathname);

		// Create desktop icon (if requested).
		if (Install_Game_Shortcut()) {
		
			WideStringClass shortcutpath;

			_RegistryManager.Get_Desktop_Path (shortcutpath);
			Create_File_Link (shortcutpath, TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME), gamepathname, gamepathname);
		}
	}

	if (Install_WOL()) {

		WideStringClass folderpath, wolfolder, registerpathname, registericonpathname, uninstallpathname;

		// Create subdirectory in folder path for menu item links.
		_RegistryManager.Get_Folder_Path (folderpath);
		folderpath += L"\\";
		folderpath += Get_Target_WOL_Folder (wolfolder);
		folderpath += L"\\";
		folderpath += TxWideStringClass (IDS_WOL_PRODUCT_NAME, IDS_RESOURCE_WOL_PRODUCT_NAME);
		Create_Directory (folderpath);

		// Create Internet Registration menu item.
		Get_Target_WOL_Path (registerpathname);
		registerpathname += L"\\";
		registerpathname += RxWideStringClass (IDS_INTERNET_REGISTRATION_FILENAME);
		Create_File_Link (folderpath, TxWideStringClass (IDS_INTERNET_REGISTRATION_TITLE, IDS_RESOURCE_INTERNET_REGISTRATION_TITLE), registerpathname, registerpathname);
		
		// Create Uninstall Registration menu item.
		Get_Target_WOL_Path (uninstallpathname);
		uninstallpathname += L"\\";
		uninstallpathname += RxWideStringClass (IDS_WOL_UNINSTALL_FILENAME);
		Create_File_Link (folderpath, TxWideStringClass (IDS_WOL_UNINSTALL_TITLE, IDS_RESOURCE_WOL_UNINSTALL_TITLE), uninstallpathname, uninstallpathname);
	}

	CoUninitialize();
}


/***********************************************************************************************
 * InstallerClass::Create_File_Link --																			  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Create_File_Link (const WideStringClass &linkpath, const WideStringClass &title, const WideStringClass &targetpathname, const WideStringClass &iconpathname, const WideStringClass *arguments)
{
	WideStringClass  linkpathname, trimmedtitle, targetpath;
	StringClass		  multibytelinkpathname, multibytetargetpath, multibytetitle, multibytetargetpathname, multibyteiconpathname;	
	IShellLink		 *psl;
	IPersistFile	 *ppf;
	bool				  success;	

	// The title is used in a pathname so it should be freed of any leading or trailing whitespace.
	trimmedtitle = title;
	trimmedtitle.Trim();

	linkpathname  = linkpath;
	linkpathname += L"\\";
	linkpathname += trimmedtitle;
	linkpathname += L".lnk";

	targetpath = targetpathname;
	Remove_Trailing_Name (targetpath);

	multibytelinkpathname	= linkpathname;
	multibytetargetpath		= targetpath;
	multibytetitle				= trimmedtitle;
	multibytetargetpathname = targetpathname;
	multibyteiconpathname	= iconpathname;

	// Remove previous icon (if it exists).  
	DeleteFile (multibytelinkpathname);

	success = CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**) &psl) == S_OK;
	if (success) {

		try {

			if (psl->SetPath (multibytetargetpathname) != NOERROR) throw (IDS_COM_ERROR);
			if (psl->SetDescription (multibytetitle) != NOERROR) throw (IDS_COM_ERROR);
			if (psl->SetIconLocation (multibyteiconpathname, 0) != NOERROR) throw (IDS_COM_ERROR);
			if (arguments != NULL) {
		
				StringClass multibytearguments (*arguments);
		
				if (psl->SetArguments (multibytearguments) != NOERROR) throw (IDS_COM_ERROR);
			}

			if (psl->SetWorkingDirectory (multibytetargetpath) != NOERROR) throw (IDS_COM_ERROR);
			if (psl->SetShowCmd (SW_NORMAL) != NOERROR) throw (IDS_COM_ERROR);
			if (psl->SetHotkey (0) != NOERROR) throw (IDS_COM_ERROR);
			if (psl->QueryInterface (IID_IPersistFile, (void**) &ppf) == S_OK) {
				success = ppf->Save (linkpathname, TRUE) == S_OK;
				ppf->Release();
			}

		} catch (...) {
			success = false;
		}
		psl->Release();
	}

	return (success);
}


/***********************************************************************************************
 * InstallerClass::Create_URL_Link --																			  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Create_URL_Link (const WideStringClass &linkpath, const WideStringClass &title, const WideStringClass &url)
{
	WideStringClass			 trimmedtitle, linkpathname;	
	StringClass					 multibytelinkpathname, multibyteurl;
	IUniformResourceLocator *purl;
	IPersistFile				*ppf;
  	bool							 success;	

	// The title is used in a pathname so it should be freed of any leading or trailing whitespace.
	trimmedtitle = title;
	trimmedtitle.Trim();

	linkpathname  = linkpath;
	linkpathname += L"\\";
	linkpathname += trimmedtitle;
	linkpathname += L".url";

	multibytelinkpathname = linkpathname;
	multibyteurl			 = url;

	// Remove previous icon (if it exists).  
	DeleteFile (multibytelinkpathname);

	// Create an IUniformResourceLocator object
	success = CoCreateInstance (CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IUniformResourceLocator, (void**) &purl) == S_OK;
	if (success) {
		success = purl->SetURL (multibyteurl, 0) == S_OK;
		if (success) {
			success = purl->QueryInterface (IID_IPersistFile, (void**) &ppf) == S_OK;
			if (success) {
				success = ppf->Save (linkpathname, TRUE) == S_OK;
				ppf->Release();
			}
		}
		purl->Release();
	}
	
	return (success);
}


/***********************************************************************************************
 * InstallerClass::Has_WOL_Account --																				  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Has_WOL_Account()
{
	WideStringClass name, password;

	return (_RegistryManager.Get_WOL_Account (0, name, password));
}


/***********************************************************************************************
 * InstallerClass::Get_WOL_Account --																				  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Get_WOL_Account (unsigned index, WideStringClass &name, WideStringClass &password)
{
	return (_RegistryManager.Get_WOL_Account (index, name, password));
}


/***********************************************************************************************
 * InstallerClass::Log --																							  *		
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Log (const WideStringClass &pathname, __int64 size)
{
	WideStringClass path;

	if (Install_Game()) {
	
		// Does the start of the pathname match either the game or WOL target path?
		if (wcsstr (pathname, Get_Target_Game_Path (path)) != NULL) {
		
			if (size == -1) {

				WideStringClass subdirectoryname (((WideStringClass) pathname).Peek_Buffer() + path.Get_Length() + 1);

				GameSubdirectories.Add (subdirectoryname);

			} else {

				FileLogClass filelog;

				filelog.RelativePathname = ((WideStringClass) pathname).Peek_Buffer() + path.Get_Length() + 1;
				filelog.Size = size;	
				GameFiles.Add (filelog);
			}

			return;
		}
	}
	
	if (Install_WOL()) {

		if (wcsstr (pathname, Get_Target_WOL_Path (path)) != NULL) {

			FileLogClass filelog;

			// NOTE: Logging WOL subdirectories is not supported.
			WWASSERT (size != -1);

			filelog.RelativePathname = ((WideStringClass) pathname).Peek_Buffer() + path.Get_Length() + 1;
			filelog.Size = size;	
			WOLFiles.Add (filelog);
		}
	}
}


/***********************************************************************************************
 * InstallerClass::Create_Uninstall_Logs --																	  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Create_Uninstall_Logs()
{
	if (Install_Game()) {
		Create_Game_Uninstall_Log();
	}
	if (Install_WOL()) {
		Create_WOL_Uninstall_Log();
	}
}


/***********************************************************************************************
 * InstallerClass::Create_Game_Uninstall_Log --																  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Create_Game_Uninstall_Log()
{
	WideStringClass log;
	WideStringClass installpathname, folderpath, gamefolder, shortcutpath;
	WideStringClass s;
	WideStringClass logfilepathname;
	WideStringClass gamefilepathname;
	HANDLE			 handle;	
	int				 i;
	DWORD				 byteswritten;
	StringClass		 multibytelogfilepathname, multibytelog;
	__int64			 totalfilesize;
	
	_RegistryManager.Get_Folder_Path (folderpath);
	_RegistryManager.Get_Desktop_Path (shortcutpath);

	// Log temporary files that will/may be created by the game.
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"commands.txt";
	Log (gamefilepathname, 0);
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"_logfile.txt";
	Log (gamefilepathname, 0);
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"_asserts.txt";
	Log (gamefilepathname, 0);
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"_except.txt";
	Log (gamefilepathname, 0);
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"sysinfo.txt";
	Log (gamefilepathname, 0);
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"history.txt";
	Log (gamefilepathname, 0);
	Get_Target_Game_Path (gamefilepathname);
	gamefilepathname += L"\\";
	gamefilepathname += L"asset_report.txt";
	Log (gamefilepathname, 0);

	// Create log pathname and log this file so it gets written to the uninstall log.
	// NOTE: File size is not important - substitute zero.
	Get_Target_Game_Path (logfilepathname);
	logfilepathname += L"\\";
	logfilepathname += RxWideStringClass (IDS_GAME_UNINSTALL_LOG_FILENAME);
	Log (logfilepathname, 0);

	// Build the log.
	log += L"[Product]\n";
	log += L"Name=";
	log += TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME);
	log += L"\n";
	log += L"FullName=";
	log += TxWideStringClass (IDS_VERBOSE_GAME_PRODUCT_NAME, IDS_RESOURCE_VERBOSE_GAME_PRODUCT_NAME);
	log += L"\n";
	log += L"Key=";
	log += RxWideStringClass (IDS_GAME_PRODUCT_KEY);
	log += L"\n";
	log += L"Exe=";
	log += Get_Target_Game_Path (installpathname);
	log += L"\\";
	log += RxWideStringClass (IDS_GAME_EXECUTABLE_FILENAME);
	log += L"\n\n";
	log += L"[Icons]\n";
	log += L"LNK=";
	log += folderpath;
	log += L"\\";
	log += Get_Target_Game_Folder (gamefolder);
	log += L"\\";
	log += TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME);
	log += L"\n";
	log += L"Desktop=";
	log += shortcutpath;
	log += L"\\";
	log += TxWideStringClass (IDS_GAME_PRODUCT_NAME, IDS_RESOURCE_GAME_PRODUCT_NAME);
	log += L".lnk";
	log += L"\n\n";

	// Append subdirectory information.
	log += L"[SubDirectories]\n";
	for (i = 0; i < GameSubdirectories.Count(); i++) {
		s.Format (L"Dir%d=%s\n", i, GameSubdirectories [i]);
		log += s;
	}
	s.Format (L"NumFiles=%u\n\n", GameSubdirectories.Count());
	log += s;

	// Append relative pathname information.
	log += L"[Files]\n";
	for (i = 0; i < GameFiles.Count(); i++) {
		s.Format (L"F:%d=%s\n", i, GameFiles [i].RelativePathname);
		log += s;
	}
	s.Format (L"NumFiles=%u\n\n", GameFiles.Count());
	log += s;

	// Append relative file size information.
	log += L"[FileSizes]\n";
	totalfilesize = 0;
	for (i = 0; i < GameFiles.Count(); i++) {
		s.Format (L"S:%d=%I64d\n", i, GameFiles [i].Size);
		log += s;
		totalfilesize += GameFiles [i].Size;
	}
	s.Format (L"Total=%I64d\n", totalfilesize);
	log += s;

	// Write the log file.
	// NOTE: Convert strings from wide to multibyte.
	multibytelogfilepathname = logfilepathname;
	handle = CreateFile (multibytelogfilepathname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (handle == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR;
	multibytelog = log;
	if (!WriteFile (handle, multibytelog.Peek_Buffer(), multibytelog.Get_Length() * sizeof (TCHAR), &byteswritten, NULL)) FATAL_SYSTEM_ERROR;
	if (!CloseHandle (handle)) FATAL_SYSTEM_ERROR;
}


/***********************************************************************************************
 * InstallerClass::Create_WOL_Uninstall_Log --																  *	
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Create_WOL_Uninstall_Log()
{
	WideStringClass log;
	WideStringClass s;
	WideStringClass logfilepathname;
	HANDLE			 handle;	
	int				 i;
	DWORD				 byteswritten;
	StringClass		 multibytelogfilepathname, multibytelog;

	// Create log pathname and log this file so it gets written to the uninstall log.
	// NOTE: File size is not important - substitute zero.
	Get_Target_WOL_Path (logfilepathname);
	logfilepathname += L"\\";
	logfilepathname += RxWideStringClass (IDS_WOL_UNINSTALL_LOG_FILENAME);
	Log (logfilepathname, 0);

	// Append relative pathname information.
	for (i = 0; i < WOLFiles.Count(); i++) {
		s.Format (L"%s\n", WOLFiles [i].RelativePathname);
		log += s;
	}

	// Write the log file.
	multibytelogfilepathname = logfilepathname;
	handle = CreateFile (multibytelogfilepathname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (handle == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR;
	multibytelog = log;
	if (!WriteFile (handle, multibytelog.Peek_Buffer(), multibytelog.Get_Length() * sizeof (TCHAR), &byteswritten, NULL)) FATAL_SYSTEM_ERROR;
	if (!CloseHandle (handle)) FATAL_SYSTEM_ERROR;
}


/***********************************************************************************************
 * InstallerClass::Create_Encryption_File --																	  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Create_Encryption_File (const WideStringClass &pathname)
{
	StringClass multibytepathname (pathname); 
	char			stringbuffer [ENCRYPTION_STRING_LENGTH];
   HANDLE		handle;
	DWORD			byteswritten;

	handle = CreateFile (multibytepathname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
	if (handle == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR;

	// Seed the random no. generator.
	srand (timeGetTime());

	for (unsigned i = 0; i < ENCRYPTION_STRING_LENGTH; i++) {

		char digit;
		
		digit	= rand() % 10;
		stringbuffer [i] = digit + '0';
	}

	if (!WriteFile (handle, stringbuffer, sizeof (stringbuffer), &byteswritten, NULL)) FATAL_SYSTEM_ERROR;
	if (!CloseHandle (handle)) FATAL_SYSTEM_ERROR;

	// Log the file so it gets written to the uninstall log.
	// NOTE: File size is not important - substitute zero.
	Log (pathname, 0);
}


/***********************************************************************************************
 * InstallerClass::Encrypt --																						  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Encrypt (const char *number, const WideStringClass &pathname, StringClass &encryptednumber)
{
	StringClass  multibytepathname (pathname);
	char			*s;		
	int			 numberlength;
	HANDLE		 handle;	
	DWORD			 bytesread;
	char			 stringbuffer [ENCRYPTION_STRING_LENGTH];
	int			 p;
	
	numberlength = strlen (number);
	s = new char [numberlength + 1];
	memcpy (s, number, numberlength + 1);

	handle = CreateFile (multibytepathname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if (handle == INVALID_HANDLE_VALUE) FATAL_SYSTEM_ERROR;
	if (!ReadFile (handle, stringbuffer, sizeof (stringbuffer), &bytesread, NULL)) FATAL_SYSTEM_ERROR;
	
	p = 0;
	for (unsigned i = 0; i < ENCRYPTION_STRING_LENGTH; i++) {

		int  t;
		char c;

		t  = s [p] - '0';
		t %= 10;
		t += stringbuffer [i];
		t %= 10;
		c  = t + '0';
		s [p] = c;
		p++;
		if (p == numberlength) p = 0;
	}

	encryptednumber = s;

	// Clean-up.
	delete [] s;
}


/***********************************************************************************************
 * InstallerClass::Register_COM_Server --																		  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
bool InstallerClass::Register_COM_Server (const WideStringClass &comdll)
{
	StringClass multibytecomdll (comdll);
	
	HMODULE module = LoadLibrary (multibytecomdll);

	if (module) {

		HRESULT hr = E_FAIL;

		DLLREGISTERSERVER dllRegister = (DLLREGISTERSERVER) GetProcAddress (module, "DllRegisterServer");

		if (dllRegister) {
			hr = dllRegister();
		}
		FreeLibrary (module);
		module = NULL;
		return (SUCCEEDED (hr));
	}
 
	return (false);
}


/***********************************************************************************************
 * InstallerClass::Finish --																						  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::On_Prog_End()
{
	switch (ProgEndAction) {
	
		case ACTION_NONE:
			break;

		case ACTION_RUN_GAME:
			Run_Game();
			break;

		case ACTION_DISPLAY_README:
			Display_Readme();
			break;
	}
}


/***********************************************************************************************
 * InstallerClass::Run_Game --																					  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Run_Game()
{
	WideStringClass	  modulepathname;
	StringClass			  multibytemodulepathname, multibytetargetgamepath;
	PROCESS_INFORMATION processinfo; 
	STARTUPINFO			  startupinfo;

	modulepathname  = TargetGamePath;
	modulepathname +=	L"\\";
	modulepathname += RxWideStringClass (IDS_GAME_EXECUTABLE_FILENAME);

	multibytemodulepathname = modulepathname;
	multibytetargetgamepath	= TargetGamePath;

	memset (&startupinfo, 0, sizeof (STARTUPINFO));
	startupinfo.cb = sizeof (STARTUPINFO);
	
	if (!CreateProcess (multibytemodulepathname.Peek_Buffer(),					// Address of module name
							  NULL,																// Address of command line
							  NULL,																// Address of process security attributes
							  NULL,																// Address of thread security attributes
							  FALSE,																// Does new process inherit handles?
							  CREATE_NEW_PROCESS_GROUP | NORMAL_PRIORITY_CLASS,	// Creation flags
							  NULL,																// Address of new environment block
							  multibytetargetgamepath.Peek_Buffer(),					// Address of current directory name
							  &startupinfo,													// Address of STARTUPINFO
							  &processinfo))	{												// Address of PROCESS_INFORMATION

		FATAL_SYSTEM_ERROR;
	}
}


/***********************************************************************************************
 * InstallerClass::Display_Readme --																			  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/22/01    IML : Created.                                                                * 
 *=============================================================================================*/
void InstallerClass::Display_Readme()
{
	const unsigned  applicationcount								= 3;
	const WCHAR		*winwordprogramname							= L"WINWORD.EXE";
	const WCHAR		*wordprocessornames [applicationcount] = {L"Wordpad.exe", L"Notepad.exe", L"Write.exe"};

	WideStringClass		pathname;
	WCHAR						docapppathname [_MAX_PATH];
	WCHAR					  *t;
	unsigned				   i;	
	PROCESS_INFORMATION  processinfo; 
	STARTUPINFO			   startupinfo;
	WideStringClass	   commandline;
	StringClass				multibytecommandline;
	BOOL					   success = false;	

	// See if Winword is the owner of .doc files.  
	_RegistryManager.Get_Document_Application_Pathname (pathname);
	wcscpy (docapppathname, pathname);
	_wcsupr (docapppathname);
	t = wcsstr (docapppathname, winwordprogramname);
	if (t != NULL) {
		 
		t += wcslen (winwordprogramname);
		*t = L'\0';

		// Launch Winword with the Readme.doc file.
		commandline  = docapppathname;
		commandline +=	L" \"";
		commandline += TargetGamePath;
		commandline += L"\\";
		commandline += RxWideStringClass (IDS_WORD_README_FILENAME);
		commandline +=	L"\"";

		memset (&startupinfo, 0, sizeof (STARTUPINFO));
		startupinfo.cb = sizeof (STARTUPINFO);
		multibytecommandline = commandline;
		success = CreateProcess (NULL,															// Address of module name                
										 multibytecommandline.Peek_Buffer(), 					// Address of command line               
										 NULL,															// Address of process security attributes
										 NULL,															// Address of thread security attributes
										 FALSE,															// Does new process inherit handles?
										 CREATE_NEW_PROCESS_GROUP | NORMAL_PRIORITY_CLASS, // Creation flags
										 NULL,															// Address of new environment block
										 NULL,															// Address of current directory name
										 &startupinfo,													// Address of STARTUPINFO
										 &processinfo);												// Address of PROCESS_INFORMATION
	}


	// No success? Try to launch any of the common text processor applications with the Readme.txt file.
	i = 0;
	while ((!success) && (i < applicationcount)) {

		commandline  = wordprocessornames [i];
		commandline +=	L" \"";
		commandline += TargetGamePath;
		commandline += L"\\";
		commandline += RxWideStringClass (IDS_TEXT_README_FILENAME);
		commandline +=	L"\"";

		memset (&startupinfo, 0, sizeof (STARTUPINFO));
		startupinfo.cb = sizeof (STARTUPINFO);
		multibytecommandline = commandline;
		success = CreateProcess (NULL,															// Address of module name
										 multibytecommandline.Peek_Buffer(),					// Address of command line
										 NULL,															// Address of process security attributes
										 NULL,															// Address of thread security attributes
										 FALSE,															// Does new process inherit handles?
										 CREATE_NEW_PROCESS_GROUP | NORMAL_PRIORITY_CLASS, // Creation flags
										 NULL,															// Address of new environment block
										 NULL,															// Address of current directory name
										 &startupinfo,													// Address of STARTUPINFO
										 &processinfo);												// Address of PROCESS_INFORMATION

		i++;
	}

	// If still no success, throw error.
	if (!success) FATAL_SYSTEM_ERROR;
}


#undef END_DIALOG
#undef REGISTER_DIALOG
#undef UNREGISTER_DIALOG
#undef DO_CANCEL
