////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1998  Transarc Corporation.
// All rights reserved.
//
//
//    File Name:  Setup.rul
//
//  Description:  InstallShield script
//
////////////////////////////////////////////////////////////////////////////////


// Include header files

    #include "sdlang.h"
    #include "sddialog.h"

////////////////////// string defines ////////////////////////////

	// If the user places a file with this name in the same directory as
	// the setup files, only the client component will be available for
	// install.  The contents of this file are not used.
	#define ALLOW_ONLY_CLIENT_FLAG_FILENAME		"setup.co"

	// The release types
	#define RELEASE_TYPE_GA	        "GA"
	#define RELEASE_TYPE_BETA		"Beta"
	
	// Client logon dll names
	#define FULL_CLIENT_LOGON_DLL	"Client\\PROGRAM\\afslogon.dll"
	#define LIGHT_CLIENT_LOGON_DLL	"Client\\PROGRAM\\afslog95.dll"

	// Uninstall log file names for each component
    #define SERVER_UNINST_LOGFILE_NAME      	"Server_Uninst.isu"
    #define CLIENT_UNINST_LOGFILE_NAME      	"Client_Uninst.isu"
    #define LIGHT_CLIENT_UNINST_LOGFILE_NAME	"Light_Client_Uninst.isu"
    #define CC_UNINST_LOGFILE_NAME      		"CC_Uninst.isu"
    #define DOCS_UNINST_LOGFILE_NAME      		"Docs_Uninst.isu"

	// Uninstall dll names for each component
	#define SERVER_UNINST_DLL_NAME				"ServerUninst.dll"
	#define CLIENT_UNINST_DLL_NAME				"ClientUninst.dll"
	#define CC_UNINST_DLL_NAME					"CCUninst.dll"
	#define LIGHT_CLIENT_UNINST_DLL_NAME		"LightClientUninst.dll"
	#define DOCS_UNINST_DLL_NAME				"DocsUninst.dll"
	
	// These are the components the user sees
	#define CLIENT_COMP_NAME			"Client"
	#define LIGHT_CLIENT_COMP_NAME		"Light_Client"
	#define SERVER_COMP_NAME			"Server"
	#define CC_COMP_NAME				"Control_Center"

	// Light Client platform components
	#define LIGHT95_CLIENT_COMP_NAME		"Light_Client\\Win95"
	#define LIGHT98_CLIENT_COMP_NAME		"Light_Client\\Win98"

	// This is added to autoexec.bat on Win95 systems
	#define LIGHT95_CLIENT_PROTSEQ			"AFS_RPC_PROTSEQ=ncacn_ip_tcp"
		
    // Language components
	#define LANG_COMP_NAME					"Languages"
    #define LANG_ENGLISH_COMP_NAME          "Languages\\Lang_English"
    #define LANG_SIMP_CHINESE_COMP_NAME     "Languages\\Lang_Simp_Chinese"
    #define LANG_TRAD_CHINESE_COMP_NAME     "Languages\\Lang_Trad_Chinese"
    #define LANG_KOREAN_COMP_NAME           "Languages\\Lang_Korean"
    #define LANG_JAPANESE_COMP_NAME         "Languages\\Lang_Japanese"
    #define LANG_GERMAN_COMP_NAME           "Languages\\Lang_German"
    #define LANG_SPANISH_COMP_NAME          "Languages\\Lang_Spanish"
    #define LANG_PORTUGUESE_COMP_NAME       "Languages\\Lang_Portuguese"

	// Documentation components
	#define DOCS_COMP_NAME					"Documentation"
	#define DOCS_ENGLISH_COMP_NAME			"Documentation\\English"
	#define DOCS_JAPANESE_COMP_NAME			"Documentation\\Japanese"
	#define DOCS_KOREAN_COMP_NAME			"Documentation\\Korean"
	#define DOCS_SIMP_CHINESE_COMP_NAME		"Documentation\\Simp_Chinese"
	#define DOCS_TRAD_CHINESE_COMP_NAME		"Documentation\\Trad_Chinese"
	#define DOCS_GERMAN_COMP_NAME			"Documentation\\German"
	#define DOCS_SPANISH_COMP_NAME			"Documentation\\Spanish"
	#define DOCS_PORTUGUESE_COMP_NAME		"Documentation\\Portuguese"
	
	// Readme files components
	#define README_COMP_NAME				"Readme"
	#define README_ENGLISH_COMP_NAME		"Readme\\English"
	#define README_JAPANESE_COMP_NAME		"Readme\\Japanese"
	#define README_KOREAN_COMP_NAME			"Readme\\Korean"
	#define README_SIMP_CHINESE_COMP_NAME	"Readme\\Simp_Chinese"
	#define README_TRAD_CHINESE_COMP_NAME	"Readme\\Trad_Chinese"
	#define README_GERMAN_COMP_NAME		"Readme\\German"
	#define README_SPANISH_COMP_NAME		"Readme\\Spanish"
	#define README_PORTUGUESE_COMP_NAME		"Readme\\Portuguese"

	// License filenames
	#define LICENSE_ENGLISH_FILENAME		"License\\en_US.rtf"
	#define LICENSE_JAPANESE_FILENAME		"License\\ja_JP.rtf"
	#define LICENSE_KOREAN_FILENAME			"License\\ko_KR.rtf"
	#define LICENSE_SIMP_CHINESE_FILENAME	"License\\zh_CN.rtf"
	#define LICENSE_TRAD_CHINESE_FILENAME	"License\\zh_TW.rtf"
	#define LICENSE_GERMAN_FILENAME			"License\\de_DE.rtf"
	#define LICENSE_SPANISH_FILENAME		"License\\es_ES.rtf"
	#define LICENSE_PORTUGUESE_FILENAME		"License\\pt_BR.rtf"

	#define SETUP_UTILS_DLL_PATH	"InstallUtils.dll"

	// Some registry keys
	#define MSUNINSTALL_KEY         "\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
	#define COMPANY_REG_KEY			"Software\\TransarcCorporation\\"
	#define CONFIG_INFO_KEY			"Software\\AfsPreservedConfigInfo"
	
	#define CLIENT_SERVICE_NAME		"TransarcAFSDaemon"

	// Client help file names
	#define CLIENT_HELP_FILE_NAME		"Common\\afs-nt.hlp"
	#define LIGHT_CLIENT_HELP_FILE_NAME	"Common\\afs-light.hlp"
	
	#define MAX_STRING_LEN	512

	// Install actions
	#define ACTION_NONE			0
	#define	ACTION_INSTALL		1
	#define ACTION_REINSTALL	2
	#define ACTION_UPGRADE		3
	#define ACTION_DOWNGRADE	4

	// Documenation start menu info
	#define DOCUMENTATION_CMD_PATH			"Documentation\\Html\\index.htm"
	#define DOCUMENTATION_ARGS				""

	#define DOCUMENTATION_LICENSE_PATH		"Documentation\\Html\\license.rtf"

	// Application start menu info
	#define CFG_WIZARD_CMD_PATH				 "Server\\usr\\afs\\bin\\afssvrcfg.exe"
	#define CFG_WIZARD_ARGS					"/wizard"
	#define CREDS_TOOL_CMD_PATH				"Client\\Program\\afscreds.exe"
	#define CREDS_TOOL_ARGS					"/show"
	#define CLIENT_HELP_CMD_PATH			"Common\\afs-nt.hlp"
	#define CLIENT_HELP_ARGS				""
	#define LIGHT_CLIENT_HELP_CMD_PATH		"Common\\afs-light.hlp"
	#define LIGHT_CLIENT_HELP_ARGS			""
	#define SERVER_MANAGER_CMD_PATH			"Control Center\\TaAfsServerManager.exe"
	#define SERVER_MANAGER_ARGS				""
	#define ACCT_MANAGER_CMD_PATH			"Control Center\\TaAfsAccountManager.exe"
	#define ACCT_MANAGER_ARGS				""

	#define CELLSERVDB_FILENAME				"afsdcell.ini"

	#define SERVER  1
	#define CLIENT  2
	#define LCLIENT 4
	#define CC      8
	#define DOCS    16

		

//////////////////// installation declarations ///////////////////

typedef VERINFO
begin
	int nMajorVersion;
	int nMinorVersion;
	int nPatchLevel;
	int nBetaLevel;
	string szReleaseType[32];
	string szTitle[32];
end;


//	NOTE:	There is a problem with the IS registry set functionality in that if
//			you specify to write a value under an existing key, at uninstall time
//			not just the value will be removed, but the KEY AND ALL ITS SUBKEYS
//			will also be removed.  So the registry sets can only be used to
//			create entries in the registry that are for values under keys that
//			we create.
//
//			There are some values that we need to create that live under existing
//			NT keys, so when we create them we must turn off logging.  The
//			szNoLogRegistrySets tell us which to create without logging.

typedef APPINFO
begin
	string  szCompName[64];
	string  szCompanyName[64];
	string  szProductName[128];
	VERINFO Ver;						// Version of this product
	VERINFO iVer;						// Verison of this product that is already installed
	string  szProductKey[128];
	BOOL    bUseUninstDLL;
	string  szUninstDllName[64];
	string  szUninstLogfileName[64];
	string  szUninstKey[128];
	string  szAppPath[256];				// Below TARGETDIR
	string  szBinPath[256];				// Path to binaries and uninstall dll
	BOOL    bAddToPath;					// TRUE if bin path should be added to path env var
	string  szUninstDisplayName[128];
	string  szRegistrySets[256];		// Comma separated list of registry sets to install
	string  szNoLogRegistrySets[256];	// Comma separated list of registry sets to install without logging
	int     nInstallAction;
	BOOL    bInstallSucceeded;
end;



// ----- DLL function prototypes -----


// ----- DLL prototypes -----

     // your DLL prototypes
    prototype int InstallUtils.InstallServerService(string);
    prototype int InstallUtils.InstallClientService(string);
    prototype int InstallUtils.AddToPath(string);
	prototype int InstallUtils.AddToNetworkProviderOrder(string);
	prototype int InstallUtils.CheckIfAdmin();
	prototype int InstallUtils.SetSilentMode();
	prototype int InstallUtils.WriteToInstallErrorLog(string);
	prototype int InstallUtils.RestoreConfigInfo(int);
	prototype int InstallUtils.Eradicate34Client();
	prototype int InstallUtils.Upgrade34ClientConfigInfo();
    prototype int InstallUtils.IsWin98();
	prototype int InstallUtils.ShowLicense(string,string);


// ---- script function prototypes -----

    // generated by the Project Wizard
    prototype ShowDialogs();
    prototype MoveFileData();
    prototype HandleMoveDataError( NUMBER );
    prototype ProcessBeforeDataMove(APPINFO POINTER);
    prototype ProcessAfterDataMove();
    prototype SetupRegistry();
    prototype CleanUpInstall();
    prototype SetupInstall();
    prototype SetupScreen();
    prototype CheckRequirements();
    prototype DialogShowSdWelcome();
    prototype DialogShowSdAskDestPath();
    prototype DialogShowSdSetupType();
    prototype DialogShowSdSelectFolder();
    prototype DialogShowSdFinishReboot();

    // your script function prototypes
	prototype FixTargetPaths();
	prototype SetupAppInfo();
	prototype LocalizeComponents();
	prototype LocalizeComponentStatusText (STRING);
	prototype InstallApp(APPINFO POINTER);	
	prototype DialogShowSdAskComponentDialog();
	prototype ShowIsSelected(string);
	prototype ShowSelectedComponents();
	prototype ConfigClientService();
	prototype ConfigServerService();
	prototype AddPath(string);
	prototype AddBinPaths();
	prototype ConfigUninstallDLL(APPINFO POINTER);
	prototype CreateServerDirs();
	prototype SetUserVars();
	prototype SetNetworkProviderOrder();
	prototype CreateRegistrySets(string);
	prototype ConfigAfsCreds();
	prototype GetLicenseFilename(BYREF string);
	prototype GetNlsComps(BYREF string, BYREF string, BYREF string, BYREF string);
	prototype DetectInstalledComponents();
	prototype IsAppInstalled(APPINFO POINTER);
	prototype ProcessCompsOnCmdLn(string);
	prototype ParseCommandLine();
	prototype GetVersionNumber(string);
	prototype GetInstallAction(string, BOOL, string, string, string, string, APPINFO POINTER);
	prototype GetInstallActions();
	prototype ClientSelectedOrInstalled();
	prototype RenameUninstDll(APPINFO POINTER);	
	prototype RemoveInstalledBits(string);
	prototype PrepareForWSPPInstall();
	prototype CreateStartMenuEntries();
    prototype ShowError(string, int);
    prototype GetDateTimeString(BYREF string);
	prototype OnlyInstallingDocs();
	prototype CreateDirs();
	prototype AddShortCut(string, string, string, string);
	prototype CheckCopyCellServDB();
	prototype RestorePreviousConfigs();
	prototype RemoveCurrentVersion(APPINFO POINTER);
	prototype DoesConfigInfoExist(APPINFO POINTER);
	prototype UnselectComps();
    prototype AnyNonUpgradableSoftwareInstalled();
	prototype RemoveAllCurrentVersions();
	prototype AppVersionOk(BOOL, BOOL, APPINFO POINTER);
	prototype SetupVerInfo(APPINFO POINTER, string, string, string, string, string, string, BOOL);
	prototype NotUpgradable(APPINFO POINTER);
	prototype CompareVersions(APPINFO POINTER);

    // ----- global variables ------

    // generated by the Project Wizard
    BOOL        bWin95, bWin98, bWinNT, bIsShellExplorer, bInstallAborted, bIs32BitSetup;
    STRING      svDir;
    STRING      svName, svCompany, svSerial;
    STRING      svDefGroup;
    STRING      szAppPath;
    STRING      svSetupType;
	NUMBER		nResult;
	
    // your global variables
	APPINFO		appFullClient;
	APPINFO		appLightClient;
	APPINFO		appServer;
	APPINFO		appControlCenter;
    APPINFO     appDocs;
	APPINFO POINTER pApp;

	BOOL		bFullClientSel, bServerSel, bCcSel, bLightClientSel, bDocsSel;
	string		szCommonBinPath;
	string		szCodePage;
	BOOL		bClientInstalled, bServerInstalled, bCcInstalled, bLightClientInstalled, bDocsInstalled;
    string 		szLangComp, szReadmeComp, szDocsComp;

	// WSPP support vars
	BOOL 		bWSPPInstall;
	BOOL 		bPreinstallReplace;


///////////////////////////////////////////////////////////////////////////////
//
//   MAIN PROGRAM
//
//      The setup begins here by hiding the visible setup
//      window.  This is done to allow all the titles, images, etc. to
//      be established before showing the main window.  The setup is
//      then performed in a series of calls to script defined functions,
//      with error checking at each step.
//
///////////////////////////////////////////////////////////////////////////////
program
    Disable( BACKGROUND );

	if (UseDLL(SUPPORTDIR ^ SETUP_UTILS_DLL_PATH) < 0) then
        ShowError(@SETUPUTILS_NOT_FOUND, SEVERE);
        bInstallAborted = TRUE;
        goto end_install;
    endif;

    SetupInstall();

    CheckRequirements();

	ParseCommandLine();
	
	SetupAppInfo();

	LocalizeComponents();

	DetectInstalledComponents();

	// Can't upgrade, downgrade, or reinstall the light client, so if it is
	// already installed, we must abort.  This is because on Win9x, we can't
	// move in-use files, and so we can't silently uninstall the light client,
	// which upgrade, downgrade, and reinstall require.
	if (bLightClientInstalled && !bWinNT) then
		ShowError(@LIGHT_CLIENT_ALREADY_INSTALLED_MSG, SEVERE);
		bInstallAborted = TRUE;
		goto end_install;
	endif;

	if (AnyNonUpgradableSoftwareInstalled()) then
		ShowError(@NON_UPGRADABLE_SOFTWARE_INSTALLED_ERROR_MSG, SEVERE);
		bInstallAborted = TRUE;
		goto end_install;
	endif;

	GetInstallActions();

	if (bWSPPInstall) then
		SetSilentMode();	// Tells the InstallUtils dll to run silently
		PrepareForWSPPInstall();
	else
		// Ask user what to install
		SetupScreen();
    	if (ShowDialogs()<0) goto end_install;
    endif;

	// The AFS path must not contain spaces.  AFS code will break if it does.
	LongPathToShortPath(TARGETDIR);

	// Determine which comps to install to get the proper NLS files.
    GetNlsComps(szLangComp, szReadmeComp, szDocsComp, szCodePage);

	SetUserVars();

	CreateDirs();

	// Uninstall current versions before we install
	RemoveAllCurrentVersions();

	// Install the Client
	if (bFullClientSel) then
		pApp = &appFullClient;
		if (InstallApp(pApp) < 0) goto end_install;
	endif;

	// Install the Light Client
	if (bLightClientSel) then
		pApp = &appLightClient;
		if (InstallApp(pApp) < 0) goto end_install;
		if (bWin95) then
			EzBatchAddString(LIGHT95_CLIENT_PROTSEQ, "", AFTER | REPLACE);
		endif;
	endif;

	// Install the Server
	if (bServerSel) then
		pApp = &appServer;
		if (InstallApp(pApp) < 0) goto end_install;
		CreateServerDirs();
	endif;

	// Install the Control Center
	if (bCcSel) then
		pApp = &appControlCenter;
		if (InstallApp(pApp) < 0) goto end_install;
	endif;

	// Install the Docs
	if (bDocsSel) then
		appDocs.szCompName = szDocsComp;
		pApp = &appDocs;
		if (InstallApp(pApp) < 0) goto end_install;
	endif;

	// If we installed the server, then configure the bos service
	if (bServerSel) then
		if (ConfigServerService() < 0) goto end_install;
	endif;

	// If we installed the full client, then configure the client service
	// and add the NetworkProviderOrder value.
	if (bFullClientSel) then
		if (ConfigClientService() < 0) goto end_install;
        // I'm commenting out the line below.  This will result in integrated
        // login being off after an install.  If you want it to be on after an
        // install, then uncomment the line.
		// if (SetNetworkProviderOrder() < 0) goto end_install;
	endif;

	if (bFullClientSel || bLightClientSel) then
		ConfigAfsCreds();
	endif;

	RestorePreviousConfigs();

	AddPath(szCommonBinPath);

    // If not only installing docs, then set BATCH_INSTALL to true,
    // which will cause setup to want to reboot the computer.
	if (OnlyInstallingDocs() == FALSE) then
		BATCH_INSTALL = TRUE;
	endif;

	CreateStartMenuEntries();

	if (bFullClientSel || bLightClientSel || bCcSel) then
		CheckCopyCellServDB();
	endif;

	end_install:

	UnUseDLL(SUPPORTDIR ^ SETUP_UTILS_DLL_PATH);

    CleanUpInstall();

    // If an unrecoverable error occurred, clean up
    // the partial installation. Otherwise, exit normally.
    if (bInstallAborted) then
        abort;
    endif;

endprogram

///////////////////////////////////////////////////////////////////////////////
//
// Function:  ShowDialogs
//
//  Purpose:  This function manages the display and navigation of
//            the standard dialogs that exist in a setup.
//
///////////////////////////////////////////////////////////////////////////////
function ShowDialogs()
    NUMBER  nResult;
    STRING	szTargetLicense;
    STRING	szSourceLicense;
begin
    Dlg_Start:
    // beginning of dialogs label

    Dlg_SdWelcome:
    nResult = DialogShowSdWelcome();
    if (nResult = BACK) goto Dlg_Start;

    Dlg_License:
    szTargetLicense = DOCUMENTATION_LICENSE_PATH;
    GetLicenseFilename (szSourceLicense);
    Disable( DIALOGCACHE );
    nResult = ShowLicense(szTargetLicense,szSourceLicense);
    if (nResult = 0) then
    	abort;
    endif;
    Enable( DIALOGCACHE );

	Dlg_SdCompDlg:
	nResult = DialogShowSdAskComponentDialog();
	if (nResult = BACK) goto Dlg_License;

    Dlg_SdSelectFolder:
    nResult = DialogShowSdSelectFolder();
    if (nResult = BACK) goto Dlg_SdCompDlg;

    return 0;

end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: NotUpgradable
//
//  Purpose: This function will return TRUE if the passed in version is not
//           upgradable.
//
///////////////////////////////////////////////////////////////////////////////
function NotUpgradable(pApp)
begin
	// The 3.5 beta is not upgradable
	if ((pApp->iVer.nMajorVersion = 3) && (pApp->iVer.nMinorVersion = 5) && (pApp->iVer.nPatchLevel = 0) && (pApp->iVer.szReleaseType = RELEASE_TYPE_BETA)) then
		return TRUE;
	endif;
	
	return FALSE;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: AnyNonUpgradableSoftwareInstalled
//
//  Purpose: This function will return TRUE if any non-upgradable software is
//           installed.
//
///////////////////////////////////////////////////////////////////////////////
function AnyNonUpgradableSoftwareInstalled()
begin
	if (NotUpgradable(&appFullClient) ||
	    NotUpgradable(&appServer) ||
		NotUpgradable(&appControlCenter) ||
		NotUpgradable(&appLightClient) ||
		NotUpgradable(&appDocs))
	then
		return TRUE;
	endif;
	
	return FALSE;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: UnselectComps
//
//  Purpose: This function unselects all of the componenets so that we can
//           completely control what gets installed.  We will select each
//           component at the time when we want to install it.
//
///////////////////////////////////////////////////////////////////////////////
function UnselectComps()
begin
	ComponentSelectItem(MEDIA, SERVER_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, CLIENT_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LIGHT_CLIENT_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, CC_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_ENGLISH_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_SIMP_CHINESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_TRAD_CHINESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_KOREAN_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_JAPANESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_GERMAN_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_SPANISH_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, LANG_PORTUGUESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_ENGLISH_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_JAPANESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_KOREAN_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_SIMP_CHINESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_TRAD_CHINESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_GERMAN_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_SPANISH_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, README_PORTUGUESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_ENGLISH_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_JAPANESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_KOREAN_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_SIMP_CHINESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_TRAD_CHINESE_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_GERMAN_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_SPANISH_COMP_NAME, FALSE);
	ComponentSelectItem(MEDIA, DOCS_PORTUGUESE_COMP_NAME, FALSE);
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: DoesConfigInfoExist
//
//  Purpose: This function determines if config info exists for an app.
//
///////////////////////////////////////////////////////////////////////////////
function DoesConfigInfoExist(pApp)
begin
	RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);

	if (RegDBKeyExist(CONFIG_INFO_KEY ^ pApp->szProductName) = 1) then
		return TRUE;
	endif;
	
	return FALSE;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: RestorePreviousConfigs
//
//  Purpose: This function restores config info for the apps we just installed.
//
///////////////////////////////////////////////////////////////////////////////
function RestorePreviousConfigs()
begin
	if (appServer.bInstallSucceeded && DoesConfigInfoExist(&appServer)) then
		RestoreConfigInfo(SERVER);
	endif;

    if (appFullClient.bInstallSucceeded) then
        if (appFullClient.iVer.szTitle = "3.4a") then
            Upgrade34ClientConfigInfo();
        elseif (DoesConfigInfoExist(&appFullClient) = TRUE) then
		    RestoreConfigInfo(CLIENT);
		endif;
	endif;

	if (appLightClient.bInstallSucceeded && DoesConfigInfoExist(&appLightClient)) then
		RestoreConfigInfo(LCLIENT);
	endif;
	
	// Only restore control center info if client not installed
	if (appControlCenter.bInstallSucceeded && !bClientInstalled && !appFullClient.bInstallSucceeded
		&& DoesConfigInfoExist(&appControlCenter))
	then
		RestoreConfigInfo(CC);
	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: OnlyInstallingDocs
//
//  Purpose: This function determines if only the doc files are being installed.
//
///////////////////////////////////////////////////////////////////////////////
function OnlyInstallingDocs()
begin
	if (!bServerSel && !bFullClientSel && !bLightClientSel && !bCcSel && bDocsSel) then
		return TRUE;
	endif;
	
	return FALSE;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: CreateDirs
//
//  Purpose: This function creates directories that we don't want IS to make.
//
///////////////////////////////////////////////////////////////////////////////
function CreateDirs()
begin
	// Turn off logging.  It shouldn't be on when this func is called, but we
	// turn it off anyway just to make sure.  We turn it off so that IS won't
	// try to delete the dirs we are about to make during an uninstall.  If
	// we don't do this, then the first app installed that uses one of these
	// dirs will think that it owns that dir and won't know that other apps
	// are sharing it.
	Disable(LOGGING);

	// Create the target dir and the shared dirs
	CreateDir(TARGETDIR);
	CreateDir(TARGETDIR ^ "Documentation");
	CreateDir(TARGETDIR ^ "Documentation\\Html");
	CreateDir(TARGETDIR ^ "Documentation\\Html\\InstallGd");
	CreateDir(TARGETDIR ^ "Documentation\\Html\\ReleaseNotes");

	// We need to manually create the Server dir so that if we have to leave
	// it behind during an uninstall (so that config info is preserved), IS
	// won't complain that it can't be deleted.
	if (bServerSel) then
		CreateDir(TARGETDIR ^ "Server\\usr\\afs\\bin");
	endif;

    // Docs don't use the common dir
	if (OnlyInstallingDocs() = FALSE) then
		CreateDir(TARGETDIR ^ "Common");
	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: PrepareForWSPPInstall
//
//  Purpose: This function decides what to install when doing a WSPP install.
//
///////////////////////////////////////////////////////////////////////////////
function PrepareForWSPPInstall()
begin
	SHELL_OBJECT_FOLDER = @WSPP_FOLDER_NAME;

	// Never install the Docs during a WSPP install
	appDocs.nInstallAction = ACTION_NONE;

	// If we have been told to replace any preinstalled software, then set
	// our reinstall mechanism in motion.
	if (bPreinstallReplace) then
		if (bCcInstalled && bCcSel) then
			appControlCenter.nInstallAction = ACTION_REINSTALL;
		endif;
		if (bClientInstalled && bFullClientSel) then
			appFullClient.nInstallAction = ACTION_REINSTALL;
		endif;
		if (bServerInstalled && bServerSel) then
			appServer.nInstallAction = ACTION_REINSTALL;
		endif;
		if (bLightClientInstalled && bLightClientSel) then
			appLightClient.nInstallAction = ACTION_REINSTALL;
		endif;
	else
		// If we have been told to not install something that is already
		// installed, then do just that.
		if (bCcInstalled && bCcSel) then
			bCcSel = FALSE;
		endif;
		if (bClientInstalled && bFullClientSel) then
			bFullClientSel = FALSE;
		endif;
		if (bServerInstalled && bServerSel) then
			bServerSel = FALSE;
		endif;
		if (bLightClientInstalled && bLightClientSel) then
			bLightClientSel = FALSE;
		endif;
	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: RemoveCurrentVersion
//
//  Purpose: This function removes the currently installed version of the app.
//			 It provides a message to the user so they know what is going on.
//
///////////////////////////////////////////////////////////////////////////////
function RemoveCurrentVersion(pApp)
	string szMsg;
	string szFormat;
begin
	szFormat = "";

	switch(pApp->nInstallAction)
		case ACTION_REINSTALL:	szFormat = @REINSTALL_PREP_MSG;
		case ACTION_UPGRADE:	szFormat = @UPGRADE_PREP_MSG;
		case ACTION_DOWNGRADE:	szFormat = @DOWNGRADE_PREP_MSG;
	endswitch;

	if (szFormat == "") then
		return;
	endif;
	
	Sprintf(szMsg, szFormat, pApp->szProductName);
	
	if (!bWSPPInstall) then
   		SdShowMsg(szMsg, TRUE);
   	endif;

	RemoveInstalledBits(pApp->szProductName);

	if (!bWSPPInstall) then
   		SdShowMsg(szMsg, FALSE);
   	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: RemoveAllCurrentVersions
//
// Purpose: This function removes the current install of all apps that are
//          begin reinstalled, upgraded, or downgraded.
//
///////////////////////////////////////////////////////////////////////////////
function RemoveAllCurrentVersions()
begin
	if (bFullClientSel) then	
		RemoveCurrentVersion(&appFullClient);
	endif;
	
	if (bServerSel) then
		RemoveCurrentVersion(&appServer);
	endif;
	
	if (bCcSel) then
		RemoveCurrentVersion(&appControlCenter);
	endif;
	
	if (bLightClientSel) then
		RemoveCurrentVersion(&appLightClient);
	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: InstallApp
//
// Purpose: This function installs an application that is part of a suite.
//
///////////////////////////////////////////////////////////////////////////////
function InstallApp(pApp)
	NUMBER nResult;
begin
	if (pApp->nInstallAction = ACTION_NONE) then
		return 0;
	endif;

	// Start fresh with nothing selected
	UnselectComps();

	// Select the comps we want to install.  We will install the passed in app
	// as well as the comps that are shared by all the apps.  Because of weird
	// IS stuff, the app comp must be selected last else IS will autoselect
	// things we don't want.
	ComponentSelectItem(MEDIA, szReadmeComp, TRUE);

	// Lang comps are shared by all apps except the docs app
	if (pApp != &appDocs) then
		ComponentSelectItem(MEDIA, szLangComp, TRUE);
	endif;

    // Select the app's component
    ComponentSelectItem(MEDIA, pApp->szCompName, TRUE);

//	MessageBox("Install app: " + pApp->szCompName, INFORMATION);

    nResult = ProcessBeforeDataMove(pApp);
    if (nResult < 0) goto error;

    nResult = MoveFileData();
    if (nResult < 0) goto error;

    nResult = ProcessAfterDataMove();
    if (nResult < 0) goto error;
	
	nResult = CreateRegistrySets(pApp->szRegistrySets);
	if (nResult < 0) goto error;

	Disable(LOGGING);

	nResult = CreateRegistrySets(pApp->szNoLogRegistrySets);
	if (nResult < 0) goto error;

	nResult = ConfigUninstallDLL(pApp);
	if (nResult < 0) goto error;

	AddPath(pApp->szBinPath);

	// Now unselect it again
	ComponentSelectItem(MEDIA, pApp->szCompName, FALSE);

	pApp->bInstallSucceeded = TRUE;

	return 0;
	
error:
	return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: ProcessBeforeDataMove
//
//  Purpose: This function performs any necessary operations prior to the
//           actual file transfer.
//
///////////////////////////////////////////////////////////////////////////////
function ProcessBeforeDataMove(pApp)
    STRING svLogFile;
    NUMBER nResult;
begin
  	// Create app information and App Paths registry keys.
    InstallationInfo(pApp->szCompanyName, pApp->szProductName, pApp->Ver.szTitle, pApp->szProductKey);

    svLogFile = pApp->szUninstLogfileName;

    // Create the uninstall reg key and open the log file
    nResult = DeinstallStart(TARGETDIR, svLogFile, pApp->szUninstKey, 0 );
    if (nResult < 0) then
        ShowError( @ERROR_UNINSTSETUP, WARNING );
    endif;

    szAppPath = pApp->szAppPath;

	// Fill in reg values for this apps reg key and uninstall reg key
    RegDBSetItem( REGDB_APPPATH, szAppPath );
    RegDBSetItem( REGDB_APPPATH_DEFAULT, szAppPath ^ pApp->szProductKey);
    RegDBSetItem( REGDB_UNINSTALL_NAME, pApp->szUninstDisplayName);

    return 0;
end;


/*
function ShowIsSelected(comp)
	number nSel;
	string str;
begin
    nSel = ComponentIsItemSelected(MEDIA , comp);
    if (nSel) then
    	str = comp + " is selected.";
    	MessageBox(str, INFORMATION);
    else
    	str = comp + " is not selected.";
    endif;
end;


function ShowSelectedComponents()
begin
	// Visible components
	ShowIsSelected(CLIENT_COMP_NAME);
	ShowIsSelected(LIGHT_CLIENT_COMP_NAME);
	ShowIsSelected(SERVER_COMP_NAME);
	ShowIsSelected(CC_COMP_NAME);
	ShowIsSelected(DOCS_COMP_NAME);

    // Language components
	ShowIsSelected(LANG_COMP_NAME);
    ShowIsSelected(LANG_ENGLISH_COMP_NAME);
    ShowIsSelected(LANG_SIMP_CHINESE_COMP_NAME);
    ShowIsSelected(LANG_TRAD_CHINESE_COMP_NAME);
    ShowIsSelected(LANG_KOREAN_COMP_NAME);
    ShowIsSelected(LANG_JAPANESE_COMP_NAME);
    ShowIsSelected(LANG_GERMAN_COMP_NAME);
    ShowIsSelected(LANG_SPANISH_COMP_NAME);
    ShowIsSelected(LANG_PORTUGUESE_COMP_NAME);

	// Documentation components
	ShowIsSelected(DOCS_ENGLISH_COMP_NAME);
	ShowIsSelected(DOCS_JAPANESE_COMP_NAME);
	ShowIsSelected(DOCS_KOREAN_COMP_NAME);
	ShowIsSelected(DOCS_SIMP_CHINESE_COMP_NAME);
	ShowIsSelected(DOCS_TRAD_CHINESE_COMP_NAME);
	ShowIsSelected(DOCS_GERMAN_COMP_NAME);
	ShowIsSelected(DOCS_SPANISH_COMP_NAME);
	ShowIsSelected(DOCS_PORTUGUESE_COMP_NAME);
	
	// Readme files components
	ShowIsSelected(README_COMP_NAME);
	ShowIsSelected(README_ENGLISH_COMP_NAME);
	ShowIsSelected(README_JAPANESE_COMP_NAME);
	ShowIsSelected(README_KOREAN_COMP_NAME);
	ShowIsSelected(README_SIMP_CHINESE_COMP_NAME);
	ShowIsSelected(README_TRAD_CHINESE_COMP_NAME);
	ShowIsSelected(README_GERMAN_COMP_NAME);
	ShowIsSelected(README_SPANISH_COMP_NAME);
	ShowIsSelected(README_PORTUGUESE_COMP_NAME);
end;
*/


///////////////////////////////////////////////////////////////////////////////
//
//  Function:  MoveFileData
//
//  Purpose:  This function handles the file transfer for
//            the setup.
//
///////////////////////////////////////////////////////////////////////////////
function MoveFileData()
    NUMBER nResult, nDisk;

begin
	if (!bWSPPInstall) then
		Enable( INDVFILESTATUS );
    	SetStatusWindow( 0, "" );
    	Disable( DIALOGCACHE );
    	Enable( STATUS );
    	StatusUpdate( ON, 100 );
	endif;

    nResult = ComponentMoveData( MEDIA, nDisk, 0 );
	
	// Reinit this call so the progress indicator resets
	ComponentMoveData("", nDisk, 0);

    HandleMoveDataError( nResult );

    Disable( STATUS );

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: HandleMoveDataError
//
//  Purpose: This function handles the error (if any) during the file transfer
//           operation.
//
///////////////////////////////////////////////////////////////////////////////
function HandleMoveDataError( nResult )
    STRING szErrMsg, svComponent , svFileGroup , svFile;
	string szMsg;
begin
    svComponent = "";
    svFileGroup = "";
    svFile = "";

    switch (nResult)
        case 0:
            return 0;
        default:
            ComponentError ( MEDIA , svComponent , svFileGroup , svFile , nResult );
            szErrMsg = @ERROR_MOVEDATA  + "\n\n" +
                @ERROR_COMPONENT + " " + svComponent + "\n" +
                @ERROR_FILEGROUP + " " + svFileGroup + "\n" +
                @ERROR_FILE      + " " + svFile;
            Sprintf(szMsg, szErrMsg, nResult);
            ShowError(szMsg, SEVERE);
            bInstallAborted = TRUE;
            return nResult;
    endswitch;

end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: ProcessAfterDataMove
//
//  Purpose: This function performs any necessary operations needed after
//           all files have been transferred.
//
///////////////////////////////////////////////////////////////////////////////
function ProcessAfterDataMove()
    STRING szReferenceFile;

begin

    // TO DO : update self-registered files and other processes that
    //        should be performed after the files have been transferred.

    // DeinstallSetReference specifies a file to be checked before
    // uninstallation. If the file is in use, uninstallation will not proceed.

//	szReferenceFile = svDir ^ pApp->szProductKey; // TO DO : If your file is in a
                                            // subfolder of svDir add that here

//    DeinstallSetReference( szReferenceFile );

    return 0;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: CleanUpInstall
//
//  Purpose: This cleans up the setup.  Anything that should
//           be released or deleted at the end of the setup should
//           be done here.
//
///////////////////////////////////////////////////////////////////////////////
function CleanUpInstall()

begin
    if (bInstallAborted) then
        return 0;
    endif;

    if (!bWSPPInstall) then
    	DialogShowSdFinishReboot();
    endif;

    if (BATCH_INSTALL) then // ensure locked files are properly transferred
        CommitSharedFiles(0);
    endif;

    return 0;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: GetLicenseFilename
//
//  Purpose: This will determine the full path to the appropriate-language
//           license on the CD.  Will default to English if the locale cannot
//           be determined.
//
///////////////////////////////////////////////////////////////////////////////
function GetLicenseFilename(szLicense)
	number MajorLanguage;
begin
	MajorLanguage = SELECTED_LANGUAGE & 255;

	switch(MajorLanguage)
	case ISLANG_CHINESE:
		if (SELECTED_LANGUAGE == ISLANG_CHINESE_TAIWAN) then
			szLicense = SRCDIR ^ LICENSE_TRAD_CHINESE_FILENAME;
		else
			szLicense = SRCDIR ^ LICENSE_SIMP_CHINESE_FILENAME;
		endif;

	case ISLANG_KOREAN:
		szLicense = SRCDIR ^ LICENSE_KOREAN_FILENAME;

	case ISLANG_JAPANESE:
		szLicense = SRCDIR ^ LICENSE_JAPANESE_FILENAME;

	case ISLANG_GERMAN:
		szLicense = SRCDIR ^ LICENSE_GERMAN_FILENAME;

	case ISLANG_PORTUGUESE:
		szLicense = SRCDIR ^ LICENSE_PORTUGUESE_FILENAME;

	case ISLANG_SPANISH:
		szLicense = SRCDIR ^ LICENSE_SPANISH_FILENAME;

	default:
		szLicense = SRCDIR ^ LICENSE_ENGLISH_FILENAME;

	endswitch;

	return 0;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: GetLanguageComp
//
//  Purpose: This will determine which language the locale is set to.  Uses
//           this info to determine which lang, readme, and doc comps to
//           install.  Will default to English if the locale cannot be
//           determined.
//
///////////////////////////////////////////////////////////////////////////////
function GetNlsComps(szLangComp, szReadmeComp, szDocsComp, szCodePage)
	number MajorLanguage;
begin
	MajorLanguage = SELECTED_LANGUAGE & 255;

	switch(MajorLanguage)
		case ISLANG_CHINESE:
			if (SELECTED_LANGUAGE == ISLANG_CHINESE_TAIWAN) then
               szLangComp = LANG_TRAD_CHINESE_COMP_NAME;
               szReadmeComp = README_TRAD_CHINESE_COMP_NAME;
               szDocsComp = DOCS_TRAD_CHINESE_COMP_NAME;
               szCodePage = "1028";
			else
               szLangComp = LANG_SIMP_CHINESE_COMP_NAME;
               szReadmeComp = README_SIMP_CHINESE_COMP_NAME;
               szDocsComp = DOCS_SIMP_CHINESE_COMP_NAME;
               szCodePage = "2052";
			endif;

		case ISLANG_KOREAN:
               szLangComp = LANG_KOREAN_COMP_NAME;
               szReadmeComp = README_KOREAN_COMP_NAME;
               szDocsComp = DOCS_KOREAN_COMP_NAME;
               szCodePage = "1042";

		case ISLANG_JAPANESE:
               szLangComp = LANG_JAPANESE_COMP_NAME;
               szReadmeComp = README_JAPANESE_COMP_NAME;
               szDocsComp = DOCS_JAPANESE_COMP_NAME;
               szCodePage = "1041";

        case ISLANG_GERMAN:
               szLangComp = LANG_GERMAN_COMP_NAME;
               szReadmeComp = README_GERMAN_COMP_NAME;
               szDocsComp = DOCS_GERMAN_COMP_NAME;
               szCodePage = "1032";

        case ISLANG_PORTUGUESE:
               szLangComp = LANG_PORTUGUESE_COMP_NAME;
               szReadmeComp = README_PORTUGUESE_COMP_NAME;
               szDocsComp = DOCS_PORTUGUESE_COMP_NAME;
               szCodePage = "1046";

        case ISLANG_SPANISH:
               szLangComp = LANG_SPANISH_COMP_NAME;
               szReadmeComp = README_SPANISH_COMP_NAME;
               szDocsComp = DOCS_SPANISH_COMP_NAME;
               szCodePage = "1034";

		default:
               szLangComp = LANG_ENGLISH_COMP_NAME;
               szReadmeComp = README_ENGLISH_COMP_NAME;
               szDocsComp = DOCS_ENGLISH_COMP_NAME;
               szCodePage = "1033";

	endswitch;

	return 0;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: SetupVerInfo
//
//  Purpose: This will initialize the VERINFO structures for a product.
//
///////////////////////////////////////////////////////////////////////////////
function SetupVerInfo(pApp, szMajor, szMinor, szPatchLevel, szBetaLevel, szReleaseType, szTitle, bInstalledVerInfo)
	int nMajor;
	int nMinor;
	int nPatchLevel;
	int nBetaLevel;
begin
	StrToNum(nMajor, szMajor);
	StrToNum(nMinor, szMinor);
	StrToNum(nPatchLevel, szPatchLevel);
	StrToNum(nBetaLevel, szBetaLevel);

	if (bInstalledVerInfo) then
		pApp->iVer.nMajorVersion = nMajor;
		pApp->iVer.nMinorVersion = nMinor;
		pApp->iVer.nPatchLevel = nPatchLevel;
		pApp->iVer.nBetaLevel = nBetaLevel;
		pApp->iVer.szReleaseType = szReleaseType;
		pApp->iVer.szTitle = szTitle;	
	else
		pApp->Ver.nMajorVersion = nMajor;
		pApp->Ver.nMinorVersion = nMinor;
		pApp->Ver.nPatchLevel = nPatchLevel;
		pApp->Ver.nBetaLevel = nBetaLevel;
		pApp->Ver.szReleaseType = szReleaseType;
		pApp->Ver.szTitle = szTitle;
	endif;
end;


	
///////////////////////////////////////////////////////////////////////////////
//
// Function: FixTargetPaths
//
//  Purpose: This function updates the CommonBinPath and app paths if you
//			 choose to install to a different directory
//
///////////////////////////////////////////////////////////////////////////////
function FixTargetPaths()
begin
	// The AFS path must not contain spaces.  AFS code will break if it does.
	LongPathToShortPath(TARGETDIR);

	szCommonBinPath = TARGETDIR ^ "Common";
	
	// Full Client
	appFullClient.szAppPath = TARGETDIR ^ "Client";
	appFullClient.szBinPath = TARGETDIR ^ "Client\\Program";
	
	// Light Client
	appLightClient.szAppPath = TARGETDIR ^ "Client";
	appLightClient.szBinPath = TARGETDIR ^ "Client\\Program";

	// Server
	appServer.szAppPath = TARGETDIR ^ "Server";
	appServer.szBinPath = TARGETDIR ^ "Server\\usr\\afs\\bin";
	
	// Control Center
	appControlCenter.szAppPath = TARGETDIR ^ "Control Center";
	appControlCenter.szBinPath = TARGETDIR ^ "Control Center";
	
    // Docs
	appDocs.szAppPath = TARGETDIR ^ "Documentation";
	appDocs.szBinPath = TARGETDIR ^ "Documentation";
end;
	

	
///////////////////////////////////////////////////////////////////////////////
//
// Function: SetupAppInfo
//
//  Purpose: This will initialize the APPINFO structures for each product
//			 that we can install.
//
///////////////////////////////////////////////////////////////////////////////
function SetupAppInfo()
int n;
begin
	szCommonBinPath = TARGETDIR ^ "Common";
	
	// Full Client
	appFullClient.szCompName = CLIENT_COMP_NAME;		// Component name
	appFullClient.szCompanyName = @COMPANY_NAME;
	appFullClient.szProductName = @PRODUCT_NAME_CLIENT;
	appFullClient.szProductKey = @PRODUCT_KEY_CLIENT;
	appFullClient.bUseUninstDLL = TRUE;
	appFullClient.szUninstDllName = CLIENT_UNINST_DLL_NAME;
	appFullClient.szUninstLogfileName = CLIENT_UNINST_LOGFILE_NAME;
	appFullClient.szUninstKey = @UNINST_KEY_CLIENT;
	appFullClient.szAppPath = TARGETDIR ^ "Client";
	appFullClient.szBinPath = TARGETDIR ^ "Client\\Program";
	appFullClient.bAddToPath = TRUE;
	appFullClient.szUninstDisplayName = @UNINST_DISPLAY_NAME_CLIENT;
	appFullClient.szRegistrySets = "Client,Full_Client";
	appFullClient.szNoLogRegistrySets = "Client_NoUninstall,WinNT_Client_NoUninstall";
	appFullClient.bInstallSucceeded = FALSE;
	SetupVerInfo(&appFullClient, @PRODUCT_VERSION_MAJOR, @PRODUCT_VERSION_MINOR, @PRODUCT_VERSION_PATCH_LEVEL, @PRODUCT_VERSION_BETA_LEVEL, @PRODUCT_VERSION_RELEASE_TYPE, @PRODUCT_VERSION_TITLE, FALSE);
	
	// Light Client
	if (bWin95) then
		appLightClient.szCompName = LIGHT95_CLIENT_COMP_NAME;
	elseif (bWin98) then
		appLightClient.szCompName = LIGHT98_CLIENT_COMP_NAME;
	endif;
	appLightClient.szCompanyName = @COMPANY_NAME;
	appLightClient.szProductName = @PRODUCT_NAME_LIGHT_CLIENT;
	appLightClient.szProductKey = @PRODUCT_KEY_LIGHT_CLIENT;
	appLightClient.bUseUninstDLL = TRUE;
	appLightClient.szUninstDllName = LIGHT_CLIENT_UNINST_DLL_NAME;
	appLightClient.szUninstLogfileName = LIGHT_CLIENT_UNINST_LOGFILE_NAME;
	appLightClient.szUninstKey = @UNINST_KEY_LIGHT_CLIENT;
	appLightClient.szAppPath = TARGETDIR ^ "Client";
	appLightClient.szBinPath = TARGETDIR ^ "Client\\Program";
	appLightClient.bAddToPath = TRUE;
	appLightClient.szUninstDisplayName = @UNINST_DISPLAY_NAME_LIGHT_CLIENT;
	appLightClient.szRegistrySets = "Client,LightClient";
	appLightClient.szNoLogRegistrySets = "Client_NoUninstall";
	appLightClient.bInstallSucceeded = FALSE;
	SetupVerInfo(&appLightClient, @PRODUCT_VERSION_MAJOR, @PRODUCT_VERSION_MINOR, @PRODUCT_VERSION_PATCH_LEVEL, @PRODUCT_VERSION_BETA_LEVEL, @PRODUCT_VERSION_RELEASE_TYPE, @PRODUCT_VERSION_TITLE, FALSE);

	// Server
	appServer.szCompName = SERVER_COMP_NAME;
	appServer.szCompanyName = @COMPANY_NAME;
	appServer.szProductName = @PRODUCT_NAME_SERVER;
	appServer.szProductKey = @PRODUCT_KEY_SERVER;
	appServer.bUseUninstDLL = TRUE;
	appServer.szUninstDllName = SERVER_UNINST_DLL_NAME;
	appServer.szUninstLogfileName = SERVER_UNINST_LOGFILE_NAME;
	appServer.szUninstKey = @UNINST_KEY_SERVER;
	appServer.szAppPath = TARGETDIR ^ "Server";
	appServer.szBinPath = TARGETDIR ^ "Server\\usr\\afs\\bin";
	appServer.bAddToPath = TRUE;
	appServer.szUninstDisplayName = @UNINST_DISPLAY_NAME_SERVER;
	appServer.szRegistrySets = "Server";
	appServer.szNoLogRegistrySets = "";
	appServer.bInstallSucceeded = FALSE;
	SetupVerInfo(&appServer, @PRODUCT_VERSION_MAJOR, @PRODUCT_VERSION_MINOR, @PRODUCT_VERSION_PATCH_LEVEL, @PRODUCT_VERSION_BETA_LEVEL, @PRODUCT_VERSION_RELEASE_TYPE, @PRODUCT_VERSION_TITLE, FALSE);
	
	// Control Center
	appControlCenter.szCompName = CC_COMP_NAME;
	appControlCenter.szCompanyName = @COMPANY_NAME;
	appControlCenter.szProductName = @PRODUCT_NAME_CC;
	appControlCenter.szProductKey = @PRODUCT_KEY_CC;
	appControlCenter.bUseUninstDLL = TRUE;
	appControlCenter.szUninstDllName = CC_UNINST_DLL_NAME;
	appControlCenter.szUninstLogfileName = CC_UNINST_LOGFILE_NAME;
	appControlCenter.szUninstKey = @UNINST_KEY_CC;
	appControlCenter.szAppPath = TARGETDIR ^ "Control Center";
	appControlCenter.szBinPath = TARGETDIR ^ "Control Center";
	appControlCenter.bAddToPath = TRUE;
	appControlCenter.szUninstDisplayName = @UNINST_DISPLAY_NAME_CC;
	appControlCenter.szRegistrySets = "Control_Center";
	appControlCenter.szNoLogRegistrySets = "";
	appControlCenter.bInstallSucceeded = FALSE;
	SetupVerInfo(&appControlCenter, @PRODUCT_VERSION_MAJOR, @PRODUCT_VERSION_MINOR, @PRODUCT_VERSION_PATCH_LEVEL, @PRODUCT_VERSION_BETA_LEVEL, @PRODUCT_VERSION_RELEASE_TYPE, @PRODUCT_VERSION_TITLE, FALSE);
	
    // Docs
	appDocs.szCompName = DOCS_COMP_NAME;
	appDocs.szCompanyName = @COMPANY_NAME;
	appDocs.szProductName = @PRODUCT_NAME_DOCS;
	appDocs.szProductKey = @PRODUCT_KEY_DOCS;
	appDocs.bUseUninstDLL = TRUE;
	appDocs.szUninstDllName = DOCS_UNINST_DLL_NAME;
	appDocs.szUninstLogfileName = DOCS_UNINST_LOGFILE_NAME;
	appDocs.szUninstKey = @UNINST_KEY_DOCS;
	appDocs.szAppPath = TARGETDIR ^ "Documentation";
	appDocs.szBinPath = TARGETDIR ^ "Documentation";
	appDocs.bAddToPath = FALSE;
	appDocs.szUninstDisplayName = @UNINST_DISPLAY_NAME_DOCS;
	appDocs.szRegistrySets = "Docs";
	appDocs.szNoLogRegistrySets = "";
	appDocs.bInstallSucceeded = FALSE;
	SetupVerInfo(&appDocs, @PRODUCT_VERSION_MAJOR, @PRODUCT_VERSION_MINOR, @PRODUCT_VERSION_PATCH_LEVEL, @PRODUCT_VERSION_BETA_LEVEL, @PRODUCT_VERSION_RELEASE_TYPE, @PRODUCT_VERSION_TITLE, FALSE);
end;
	

///////////////////////////////////////////////////////////////////////////////
//
// Function: SetupInstall
//
//  Purpose: This will initialize the setup.  Any general initialization
//           needed for the installation should be performed here.
//
///////////////////////////////////////////////////////////////////////////////
function SetupInstall()
begin
    Enable( CORECOMPONENTHANDLING );

    bInstallAborted = FALSE;

    //  Create list of end user selections to be displayed by DialogShowSdStartCopy()

    svDir = PROGRAMFILES ^ @DEFAULT_INSTALL_DIR;

    TARGETDIR  = svDir;

    SdProductName( @PRODUCT_NAME );

    Enable( DIALOGCACHE );

    SetDialogTitle(DLG_ASK_YESNO, @TITLE_CAPTIONBAR);
  	SetDialogTitle(DLG_MSG_INFORMATION, @TITLE_CAPTIONBAR);
  	SetDialogTitle(DLG_MSG_WARNING, @TITLE_CAPTIONBAR);
  	SetDialogTitle(DLG_MSG_SEVERE, @TITLE_CAPTIONBAR);
  	SetDialogTitle(DLG_USER_CAPTION, @TITLE_CAPTIONBAR);
end;

///////////////////////////////////////////////////////////////////////////////
//
// Function:  SetupScreen
//
//  Purpose:  This function establishes the screen look.  This includes
//            colors, fonts, and text to be displayed.
//
///////////////////////////////////////////////////////////////////////////////
function SetupScreen()
begin
    Enable( FULLWINDOWMODE );
    SetTitle( @TITLE_MAIN, 24, WHITE );

    SetTitle( @TITLE_CAPTIONBAR, 0, BACKGROUNDCAPTION ); // Caption bar text.

    Enable( BACKGROUND );

    Delay( 1 );
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function:  CheckRequirements
//
//  Purpose:  This function checks all minimum requirements for the
//            application being installed.  If any fail, then the user
//            is informed and the setup is terminated.
//
///////////////////////////////////////////////////////////////////////////////
function CheckRequirements()
    NUMBER  nvDx, nvDy, nvResult;
    STRING  svResult;
begin
    bWinNT           = FALSE;
    bWin95			 = FALSE;
    bWin98			 = FALSE;
    bIsShellExplorer = FALSE;

    // Check screen resolution.
    GetExtents( nvDx, nvDy );

    if (nvDy < 480) then
        ShowError( @ERROR_VGARESOLUTION, WARNING );
        abort;
    endif;

    // set 'setup' operation mode
    bIs32BitSetup = TRUE;
    GetSystemInfo( ISTYPE, nvResult, svResult );
    if (nvResult = 16) then
        bIs32BitSetup = FALSE; // running 16-bit setup
        return 0; // no additional information required
    endif;

    // --- 32-bit testing after this point ---

    // Determine the target system's operating system.
    GetSystemInfo( OS, nvResult, svResult );

    if (nvResult =  IS_WINDOWSNT) then
        // Running Windows NT.
        bWinNT = TRUE;

        // If the user is not an admin, then abort
        if (CheckIfAdmin() = -1) then
            ShowError(@MUST_BE_AN_ADMIN_MSG, SEVERE);
            abort;
        endif;

        // Check to see if the shell being used is EXPLORER shell.
        if (GetSystemInfo( OSMAJOR, nvResult, svResult ) = 0) then
            if (nvResult >= 4) then
                bIsShellExplorer = TRUE;
            endif;
        endif;

    elseif (nvResult = IS_WINDOWS95) then
        // IS puts us here even for Win98
        if (IsWin98() = 0) then
            bWin98 = TRUE;
		else
		    bWin95 = TRUE;
		endif;

        bIsShellExplorer = TRUE;
    endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: DialogShowSdWelcome
//
//  Purpose: This function handles the standard welcome dialog.
//
//
///////////////////////////////////////////////////////////////////////////////
function DialogShowSdWelcome()
    NUMBER nResult;
    STRING szTitle, szMsg;

begin

    szTitle = "";
    szMsg   = "";
    nResult = SdWelcome( szTitle, szMsg );

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: DialogShowSdAskDestPath
//
//  Purpose: This function asks the user for the destination folder.
//
///////////////////////////////////////////////////////////////////////////////
function DialogShowSdAskDestPath()
    NUMBER nResult;
    STRING szTitle, szMsg;

begin

    szTitle = "";
    szMsg   = "";
    nResult = SdAskDestPath( szTitle, szMsg, svDir, 0 );

    TARGETDIR = svDir;
    FixTargetPaths();

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: GetVersionNumber
//
//  Purpose: This function converts a version number string into an integer.
//
///////////////////////////////////////////////////////////////////////////////
function GetVersionNumber(szVerion)
	int nVersion;
	int nMajorVersion;
	int nMinorVersion;
	string szVer[64];
begin
	// This code assumes a majorversion.minorversion format.
	szVer = szVerion;
		
	// This will get everything up to the '.'
	StrToNum(nMajorVersion, szVer);
	
	// Delete the part of the string we just converted
	StrSub(szVer, szVer, StrFind(szVer, ".") + 1, 64);
	
	// Now convert the part after the '.'
	StrToNum(nMinorVersion, szVer);
	
	// Turn in into a single number
	nVersion = (nMajorVersion * 10) + nMinorVersion;
	
	return nVersion;
end;
	

///////////////////////////////////////////////////////////////////////////////
//
// Function: CompareVersions
//
//  Purpose: This function compares an apps two version info structures and
//			 determines if one is greater than the other, or if they are equal.
//
///////////////////////////////////////////////////////////////////////////////
function CompareVersions(pApp)
	int nResult;
begin
	// Compare major versions
	nResult = pApp->Ver.nMajorVersion - pApp->iVer.nMajorVersion;
	if (nResult > 0) then return 1; endif;
	if (nResult < 0) then return -1; endif;
	
	// Major versions are equal, move on to minor versions
	nResult = pApp->Ver.nMinorVersion - pApp->iVer.nMinorVersion;
	if (nResult > 0) then return 1; endif;
	if (nResult < 0) then return -1; endif;

	// Minor versions are equal, move on to patch level.
	nResult = pApp->Ver.nPatchLevel - pApp->iVer.nPatchLevel;
	if (nResult > 0) then return 1; endif;
	if (nResult < 0) then return -1; endif;

	// Patch levels are equal, move on to release type.
	if ((pApp->Ver.szReleaseType = RELEASE_TYPE_GA) && (pApp->iVer.szReleaseType = RELEASE_TYPE_BETA)) then return 1; endif;
	if ((pApp->Ver.szReleaseType = RELEASE_TYPE_BETA) && (pApp->iVer.szReleaseType = RELEASE_TYPE_GA)) then return -1; endif;
	
	// Release types are equal.  Special handling now required:
	
	// If this is a GA release, then there is no more info to look at, and the versions are equal.
	if (pApp->Ver.szReleaseType = RELEASE_TYPE_GA) then return 0; endif;

	// We have a Beta release, so we still have some info to check.  Compare the beta release levels.
	nResult = pApp->Ver.nBetaLevel - pApp->iVer.nBetaLevel;
	if (nResult > 0) then return 1; endif;
	if (nResult < 0) then return -1; endif;
	
	// Beta versions are equal.
	return 0;
end;
	

///////////////////////////////////////////////////////////////////////////////
//
// Function: GetInstallAction
//
//  Purpose: This function decides what install action to perform for a
//           component and sets the display name accordingly.
//
///////////////////////////////////////////////////////////////////////////////
function GetInstallAction(szCompName, bInstalled, szInstallMsg, szUpgradeMsg, szDowngradeMsg, szReinstallMsg, pApp)
	string szDisplayName;
	string szVerMsg;
	int nResult;
begin
	szVerMsg = "";

	if (bInstalled) then
		nResult = CompareVersions(pApp);

		if (nResult = 0) then
			szDisplayName = szReinstallMsg;
			pApp->nInstallAction = ACTION_REINSTALL;
	
		elseif (nResult > 0) then
			szDisplayName = szUpgradeMsg;
			pApp->nInstallAction = ACTION_UPGRADE;
			Sprintf(szVerMsg, " %s", pApp->iVer.szTitle);
			
		else // Trying to install old version on top of new version
			szDisplayName = szDowngradeMsg;
			pApp->nInstallAction = ACTION_DOWNGRADE;
			Sprintf(szVerMsg, " %s", pApp->iVer.szTitle);
		endif;
	else
		szDisplayName = szInstallMsg;
		pApp->nInstallAction = ACTION_INSTALL;
	endif;

	szDisplayName = szDisplayName + szVerMsg;

	ComponentSetData(MEDIA, szCompName, COMPONENT_FIELD_DISPLAYNAME, 0, szDisplayName);
	
	return TRUE;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: GetInstallActions
//
//  Purpose: This function decides what install action to perform for each
//           component and sets the display name accordingly.
//
///////////////////////////////////////////////////////////////////////////////
function GetInstallActions()
begin
	// Decide what to do for each component and then set the display names appropriately
	GetInstallAction(CLIENT_COMP_NAME, bClientInstalled, @INSTALL_CLIENT_MSG, @UPGRADE_CLIENT_MSG, @DOWNGRADE_CLIENT_MSG, @REINSTALL_CLIENT_MSG, &appFullClient);
	GetInstallAction(SERVER_COMP_NAME, bServerInstalled, @INSTALL_SERVER_MSG, @UPGRADE_SERVER_MSG, @DOWNGRADE_SERVER_MSG, @REINSTALL_SERVER_MSG, &appServer);
	GetInstallAction(CC_COMP_NAME, bCcInstalled, @INSTALL_CC_MSG, @UPGRADE_CC_MSG, @DOWNGRADE_CC_MSG, @REINSTALL_CC_MSG, &appControlCenter);
	GetInstallAction(LIGHT_CLIENT_COMP_NAME, bLightClientInstalled, @INSTALL_LIGHT_CLIENT_MSG, @UPGRADE_LIGHT_CLIENT_MSG, @DOWNGRADE_LIGHT_CLIENT_MSG, @REINSTALL_LIGHT_CLIENT_MSG, &appLightClient);
	GetInstallAction(DOCS_COMP_NAME, bDocsInstalled, @INSTALL_DOCS_MSG, @UPGRADE_DOCS_MSG, @DOWNGRADE_DOCS_MSG, @REINSTALL_DOCS_MSG, &appDocs);
end;

///////////////////////////////////////////////////////////////////////////////
//
// Function: ClientSelectedOrInstalled
//
//  Purpose: This function checks whether or not the client is selected or if
//           it is already installed.
//
///////////////////////////////////////////////////////////////////////////////
function ClientSelectedOrInstalled()
begin
	if (bFullClientSel || bClientInstalled) then
		return TRUE;
	endif;
		
	return FALSE;
end;

///////////////////////////////////////////////////////////////////////////////
//
// Function: AppVersionOk
//
//  Purpose: This function determines if a given app will have the correct
//           version after installation.
//
///////////////////////////////////////////////////////////////////////////////
function AppVersionOk(bInstalled, bSel, pApp)
	BOOL bOk;
begin
	// Here's the logic of this:
	//
	// 1)  If the app is not installed and
	//     a) it's not being installed, then this app doesn't affect the version
	//        restrictions, so return true
	//     b) it will be installed, then it will get the version which this
	//        installer installs, so the version will be ok
	//
	// 2)  If the app is installed and
	//     a) the app is selected for reinstall, upgrade, or downgrade, then the
	//        newly installed version will be the version this installer installs,
	//        and so the version will be ok
	//     b) the app is not selected for reinstall, upgrade, or downgrade, then
	//        the version is ok only if the app's current version matches the
	//        the version of this installer

	bOk = !bInstalled || bSel || (CompareVersions(pApp) = 0);

	return bOk;
end;
	

///////////////////////////////////////////////////////////////////////////////
//
// Function: DialogShowSdAskComponentDialog
//
//  Purpose: This function asks the user which components to install
//			 and what the destination folder should be.
//
///////////////////////////////////////////////////////////////////////////////
function DialogShowSdAskComponentDialog()
    NUMBER nResult;
    STRING szTitle, szMsg, szComponent, szFilename;
    BOOL bClient, bServer, bCc, bDocs, bVersionsOK;
begin

    szTitle = "";
    szMsg   = "";
	szComponent = "";

	// If running WinNT hide the Light Client component.  If the user wants to only
	// allow client installs, then hide all but the Client component as well.  If not
	// running on NT, then hide all but the Light Client component.
	if (bWinNT) then
		ComponentSetData(MEDIA, LIGHT_CLIENT_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
		ComponentSelectItem(MEDIA, LIGHT_CLIENT_COMP_NAME, FALSE);
	
		if (FindFile(SRCDIR, ALLOW_ONLY_CLIENT_FLAG_FILENAME, szFilename) = 0) then
			ComponentSetData(MEDIA, SERVER_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
			ComponentSetData(MEDIA, CC_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
			ComponentSetData(MEDIA, DOCS_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
		
			ComponentSelectItem(MEDIA, SERVER_COMP_NAME, FALSE);
			ComponentSelectItem(MEDIA, CC_COMP_NAME, FALSE);
			ComponentSelectItem(MEDIA, DOCS_COMP_NAME, FALSE);
		endif;
	else
		ComponentSetData(MEDIA, CLIENT_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
		ComponentSetData(MEDIA, SERVER_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
		ComponentSetData(MEDIA, CC_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
		ComponentSetData(MEDIA, DOCS_COMP_NAME, COMPONENT_FIELD_VISIBLE, FALSE, "");
		
		ComponentSelectItem(MEDIA, SERVER_COMP_NAME, FALSE);
		ComponentSelectItem(MEDIA, CLIENT_COMP_NAME, FALSE);
		ComponentSelectItem(MEDIA, CC_COMP_NAME, FALSE);
		ComponentSelectItem(MEDIA, DOCS_COMP_NAME, FALSE);
	endif;

retry:
	nResult = SdComponentDialog(szTitle, szMsg, svDir, szComponent);

	// Enforce selection rules - something must be selected and
	// the client must be selected if the server is.  However, take into
	// account if it is already installed.
	bFullClientSel = ComponentIsItemSelected(MEDIA , CLIENT_COMP_NAME);
	bLightClientSel = ComponentIsItemSelected(MEDIA , LIGHT_CLIENT_COMP_NAME);
	bServerSel = ComponentIsItemSelected(MEDIA , SERVER_COMP_NAME);
	bCcSel = ComponentIsItemSelected(MEDIA , CC_COMP_NAME);
	bDocsSel = ComponentIsItemSelected(MEDIA , DOCS_COMP_NAME);

	if (!bFullClientSel && !bLightClientSel && !bServerSel && !bCcSel && !bDocsSel) then
		ShowError(@NOTHING_SELECTED_MSG, SEVERE);
		goto retry;
	endif;

	if (bServerSel && !ClientSelectedOrInstalled()) then
		ShowError(@SERVER_NEEDS_CLIENT_MSG, SEVERE);
		goto retry;
	endif;	

	// Also, if upgrading or downgrading, all apps must be at the same version level.
	// We won't upgrade one app to a higer version than another, and we won't downgrade
	// one app to a lowel version than another.  All must be at the same level when the
	// install program is done.  This is necessary because the apps share some files.
	if (!bWinNT) then
		bVersionsOK = TRUE;
	else
		bClient = AppVersionOk(bClientInstalled, bFullClientSel, &appFullClient);
		bServer = AppVersionOk(bServerInstalled, bServerSel, &appServer);
		bCc = AppVersionOk(bCcInstalled, bCcSel, &appControlCenter);
		bDocs = AppVersionOk(bDocsInstalled, bDocsSel, &appDocs);
		bVersionsOK = bClient && bServer && bCc && bDocs;
	endif;
	
	if (!bVersionsOK) then
		ShowError(@APPS_NOT_SAME_VERSION, SEVERE);
		goto retry;
	endif;

    TARGETDIR = svDir;
    FixTargetPaths();

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: DialogShowSdSetupType
//
//  Purpose: This function displays the standard setup type dialog.
//
///////////////////////////////////////////////////////////////////////////////
function DialogShowSdSetupType()
    NUMBER nResult, nType;
    STRING szTitle, szMsg, szReserved;

begin

    if (svSetupType == "") then
    	svSetupType = "AFS Server";
	endif;
	
    szTitle = "";
    szMsg   = @SETUP_TYPE_MSG;
    szReserved = "";

	nResult = SdSetupTypeEx(szTitle, szMsg, szReserved, svSetupType, 0);

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: DialogShowSdSelectFolder
//
//  Purpose: This function displays the standard folder selection dialog.
//
//
///////////////////////////////////////////////////////////////////////////////
function DialogShowSdSelectFolder()
    NUMBER nResult;
    STRING szTitle, szMsg;
begin
    if (SHELL_OBJECT_FOLDER = "") then
       	SHELL_OBJECT_FOLDER = @FOLDER_NAME;
    endif;

	// We are going to force the user to use the default folder name.  This is
	// because we create the folder unlogged, and must therefore remove it
	// ourselves in our uninstall dll (which we do because IS doesn't handle
	// removing them correctly).  Therefore, we must know the name of the
	// folder so we can remove it during uninstall.  Also, the user can install
	// the AFS components during several executions of the install program,
	// and we don't want to allow them to pick a different folder each time.
	// We could solve these problems in code but we don't have time, so we
	// are going to force them to use our default folder name.
	return 0;

    szTitle    = "";
    szMsg      = "";
    nResult    = SdSelectFolder( szTitle, szMsg, SHELL_OBJECT_FOLDER);

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: DialogShowSdFinishReboot
//
//  Purpose: This function will show the last dialog of the product.
//           It will allow the user to reboot and/or show some readme text.
//
///////////////////////////////////////////////////////////////////////////////
function DialogShowSdFinishReboot()
    NUMBER nResult, nDefOptions;
    STRING szTitle, szMsg1, szMsg2, szOption1, szOption2;
    NUMBER bOpt1, bOpt2;
begin
    if (!BATCH_INSTALL) then
        bOpt1 = FALSE;
        bOpt2 = FALSE;
        szMsg1 = @SETUP_FINISHED_MSG;
        szMsg2 = "";
        szOption1 = "";
        szOption2 = "";
        szTitle = "";
        nResult = SdFinish(szTitle, szMsg1, szMsg2, szOption1, szOption2, bOpt1, bOpt2 );
        return 0;
    endif;

    nDefOptions = SYS_BOOTMACHINE;
    szTitle     = "";
    szMsg1      = "";
    szMsg2      = "";
    nResult     = SdFinishReboot( szTitle, szMsg1, nDefOptions, szMsg2, 0 );

    return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: ConfigServerService
//
//  Purpose: This function will configure the AFS Server bos service via the
//			 SCM.
//
///////////////////////////////////////////////////////////////////////////////
function ConfigServerService()
    STRING szMsg;
    BOOL bError;
    number nStatus;
begin
    bError = TRUE;

    if (!bWSPPInstall) then
    	szMsg = @CONFIGURING_AFS_SERVER_SERVICE;
    	SdShowMsg(szMsg, TRUE);
    endif;

    nStatus = RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
    if (nStatus < 0) goto error;

    nStatus = InstallServerService(TARGETDIR ^ "\\Server\\usr\\afs\\bin\\bosctlsvc.exe");
    if (nStatus < 0) goto error;

    bError = FALSE;

error:
    if (!bWSPPInstall) then
	    SdShowMsg(szMsg, FALSE);
	endif;

    if (bError) then
        Sprintf(szMsg, @AFS_SERVER_SERVICE_INSTALL_ERROR, nStatus);
        ShowError(szMsg, SEVERE);
        bInstallAborted = TRUE;
        return -1;
    endif;

    return 0;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: ConfigClientService
//
//  Purpose: This function will configure the AFS Client service via the SCM.
//
///////////////////////////////////////////////////////////////////////////////
function ConfigClientService()
    STRING szMsg;
    BOOL bError;
    number nStatus;
begin
    bError = TRUE;

	if (!bWSPPInstall) then
    	szMsg = @CONFIGURING_AFS_CLIENT_SERVICE;
    	SdShowMsg(szMsg, TRUE);
    endif;

    nStatus = RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
    if (nStatus < 0) goto error;

    nStatus = InstallClientService(TARGETDIR ^ "\\Client\\Program\\afsd_service.exe");
    if (nStatus < 0) goto error;

    bError = FALSE;

error:
    if (!bWSPPInstall) then
	    SdShowMsg(szMsg, FALSE);
	endif;

    if (bError) then
        Sprintf(szMsg, @AFS_CLIENT_SERVICE_INSTALL_ERROR, nStatus);
        ShowError(szMsg, SEVERE);
        bInstallAborted = TRUE;
        return -1;
    endif;

    return 0;
end;

///////////////////////////////////////////////////////////////////////////////
//
//  Function: AddPath
//
//  Purpose: This function will add the specified path to the system PATH
//			 environment variable.
//
///////////////////////////////////////////////////////////////////////////////
function AddPath(szPath)
begin
    LongPathToShortPath(szPath);

    if (AddToPath(szPath) < 0) then
        ShowError(@ADD_PATH_ERROR, WARNING);
    endif;

	return 0;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: ConfigUninstallDLL
//
//  Purpose: This function will modify the uninstall entry for these products
//			 to use an uninstall dll.
//
///////////////////////////////////////////////////////////////////////////////
function ConfigUninstallDLL(pApp)
    string svValue;
    NUMBER nStatus;
begin
	if (!pApp->bUseUninstDLL) then
		return 0;
	endif;

    nStatus = RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
    if (nStatus < 0) goto error;

    svValue = UNINST + " -c\"" + pApp->szBinPath ^ pApp->szUninstDllName + "\" -f\"" + TARGETDIR ^ pApp->szUninstLogfileName + "\"";

    nStatus = RegDBSetKeyValueEx(MSUNINSTALL_KEY ^ pApp->szProductName, "UninstallString", REGDB_STRING, svValue, -1);
    if (nStatus < 0) goto error;

    return 0;

error:
    return nStatus;
end;
		

///////////////////////////////////////////////////////////////////////////////
//
//  Function: CreateServerDirs
//
//  Purpose: This function will create directories that the server programs
//			 need.  The creation of these is not logged, and so they will be
//			 deleted by the uninstall dll.
//
///////////////////////////////////////////////////////////////////////////////
function CreateServerDirs()
begin
	CreateDir(TARGETDIR ^ "\\Server\\usr\\afs\\logs");
	CreateDir(TARGETDIR ^ "\\Server\\usr\\afs\\etc");
	CreateDir(TARGETDIR ^ "\\Server\\usr\\afs\\local");
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: GetDateTimeString()
//
//  Purpose: This function will return a string containing the current date
//           and time.
//
///////////////////////////////////////////////////////////////////////////////
function GetDateTimeString(szDateTime)
    STRING szDate, szTime;
    NUMBER nDate, nTime;
begin
	GetSystemInfo(DATE, nDate, szDate);
    GetSystemInfo(TIME, nTime, szTime);

    szDateTime = szDate + " " + szTime;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: SetUserVars()
//
//  Purpose: This function will set at runtime values for user defined variables
//			 we set up in the Registry Entries GUI tool.
//
///////////////////////////////////////////////////////////////////////////////
function SetUserVars()
    number nStatus;
    string szInstallDate;
    string szDesc;
    string szTitle;
    string szReleaseType;
begin
    // Set the install date variable's value
    GetDateTimeString(szInstallDate);

	ComponentSetTarget(MEDIA, "<INSTALL_DATE>", szInstallDate);

	// Set the proper client logon dll
	if (bFullClientSel) then
		ComponentSetTarget(MEDIA, "<LOGON_DLL>", TARGETDIR ^ FULL_CLIENT_LOGON_DLL);
	elseif (bLightClientSel) then
		ComponentSetTarget(MEDIA, "<LOGON_DLL>", TARGETDIR ^ LIGHT_CLIENT_LOGON_DLL);
	endif;

	// Set the client description and title values
	if (bFullClientSel || bLightClientSel) then

		if (bFullClientSel) then
			szDesc = @CLIENT_DESC;
			szTitle = @CLIENT_TITLE;
		else
			szDesc = @LIGHT_CLIENT_DESC;
			szTitle = @LIGHT_CLIENT_TITLE;
		endif;

		ComponentSetTarget(MEDIA, "<CLIENT_DESC>", szDesc);
		ComponentSetTarget(MEDIA, "<CLIENT_TITLE>", szTitle);
	endif;

	if (bServerSel) then
		ComponentSetTarget(MEDIA, "<EVENT_MSG_DLL>", TARGETDIR ^ "Common\\afseventmsg_" + szCodePage + ".dll");
	endif;

	szReleaseType = @PRODUCT_VERSION_RELEASE_TYPE;
	ComponentSetTarget(MEDIA, "<RELEASE_TYPE>", szReleaseType);

done:
	return nStatus;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: SetNetworkProviderOrder()
//
//  Purpose: This function will add the AFS Client service name to the
//			 ProviderOrder value of the
//			 HKLM\SYSTEM\CurrentControlSet\ControlNetworkProvider\Order key.
//			 It makes a call to the setup dll to do this.
///////////////////////////////////////////////////////////////////////////////
function SetNetworkProviderOrder()
	int nStatus;
begin
	nStatus = AddToNetworkProviderOrder(CLIENT_SERVICE_NAME);
	if (nStatus < 0) then
		ShowError(@SET_NETWORK_PROVIDER_ERROR, SEVERE);
	endif;

	return nStatus;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: CreateRegistrySets()
//
//  Purpose: This function will create the registry entries for each resgistry
//			 set in the passed in string.  The string must be a comma separated
//			 list of registry sets created using the IS Registry Entries GUI.
//
///////////////////////////////////////////////////////////////////////////////
function CreateRegistrySets(szRegistrySets)
	int nRegSetListID;
	int nResult;
	string szRegSet;
begin
   	nRegSetListID = ListCreate(STRINGLIST);
	
   	StrGetTokens(nRegSetListID, szRegistrySets, ",");

   	nResult = ListGetFirstString(nRegSetListID, szRegSet);
	while (nResult != END_OF_LIST)
//		MessageBox(szRegSet, INFORMATION);
		CreateRegistrySet(szRegSet);
		nResult = ListGetNextString(nRegSetListID, szRegSet);
	endwhile;
	
	ListDestroy(nRegSetListID);
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: ConfigAfsCreds()
//
//  Purpose: This function will config the AFS Client Credentials tool.
//
///////////////////////////////////////////////////////////////////////////////
function ConfigAfsCreds()
int nResult;
begin
	nResult = LaunchAppAndWait(TARGETDIR ^ "Client\\Program\\afscreds.exe", "/install", WAIT);
	if (nResult < 0) then
		ShowError(@CONFIGURING_AFS_CREDS_ERROR, INFORMATION);
	endif;
	
	return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: IsAppInstalled
//
//  Purpose: This function will determine if an app is installed, and if it is,
//           will set its iVer info.
//
///////////////////////////////////////////////////////////////////////////////
function IsAppInstalled(pApp)
int nResult;
int nSize;
int nType;
string szKey;
string szMajorVersion, szMinorVersion, szPatchLevel, szBetaLevel, szReleaseType, szTitle;
begin
    nResult = RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
    if (nResult < 0) goto error;

	szKey = COMPANY_REG_KEY ^ pApp->szProductName ^ "CurrentVersion";

	nResult = RegDBGetKeyValueEx(szKey, "MajorVersion", nType, szMajorVersion, nSize);
	if (nResult < 0) goto error;
	
	nResult = RegDBGetKeyValueEx(szKey, "MinorVersion", nType, szMinorVersion, nSize);
	if (nResult < 0) goto error;
	
	nResult = RegDBGetKeyValueEx(szKey, "PatchLevel", nType, szPatchLevel, nSize);
	if (nResult < 0) goto error;

	nResult = RegDBGetKeyValueEx(szKey, "BetaLevel", nType, szBetaLevel, nSize);
	// It's ok for this to fail.  Pre-3.5 3.32 releases did not use this.

	nResult = RegDBGetKeyValueEx(szKey, "VersionString", nType, szTitle, nSize);
	if (nResult < 0) goto error;

	nResult = RegDBGetKeyValueEx(szKey, "ReleaseType", nType, szReleaseType, nSize);
	if (nResult < 0) then
		if ((pApp->szProductName = "AFS Client") && (szTitle = "3.4a")) then
			// This value won't exist for 3.4a clients
			szReleaseType = RELEASE_TYPE_GA;
		else
			// This value won't exist for any 3.5 beta versions
			szReleaseType = RELEASE_TYPE_BETA;
		endif;
	endif;

	SetupVerInfo(pApp, szMajorVersion, szMinorVersion, szPatchLevel, szBetaLevel, szReleaseType, szTitle, TRUE);
	
	return TRUE;

error:
	return FALSE;
end;
	

///////////////////////////////////////////////////////////////////////////////
//
//  Function: LocalizeComponents()
//
//  Purpose: This function updates all components to use localized text
//           in their status fields as appropriate
//
///////////////////////////////////////////////////////////////////////////////
function LocalizeComponents()
begin
	LocalizeComponentStatusText ("Client\\Client_Common");
	LocalizeComponentStatusText ("Client\\Client_Program\\Client_Headers\\Client_Afs_Headers");
	LocalizeComponentStatusText ("Client\\Client_Program\\Client_Headers\\Client_Main_Headers");
	LocalizeComponentStatusText ("Client\\Client_Program\\Client_Headers\\Client_Rx_Headers");
	LocalizeComponentStatusText ("Client\\Client_Program\\Client_Headers");
	LocalizeComponentStatusText ("Client\\Client_Program\\Client_Sample");
	LocalizeComponentStatusText ("Client\\Client_Program");
	LocalizeComponentStatusText ("Client\\Client_System32");
	LocalizeComponentStatusText ("Client\\Client_WinDir");
	LocalizeComponentStatusText ("Client");
	LocalizeComponentStatusText ("Components");
	LocalizeComponentStatusText ("Control_Center\\Control_Center_Common");
	LocalizeComponentStatusText ("Control_Center\\Control_Center_Program");
	LocalizeComponentStatusText ("Control_Center\\Control_Center_System32");
	LocalizeComponentStatusText ("Control_Center\\Control_Center_WinDir");
	LocalizeComponentStatusText ("Control_Center");
	LocalizeComponentStatusText ("Documentation\\English\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\English\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\English\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\English");
	LocalizeComponentStatusText ("Documentation\\German\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\German\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\German\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\German");
	LocalizeComponentStatusText ("Documentation\\Japanese\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\Japanese\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\Japanese\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\Japanese");
	LocalizeComponentStatusText ("Documentation\\Korean\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\Korean\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\Korean\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\Korean");
	LocalizeComponentStatusText ("Documentation\\Portuguese\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\Portuguese\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\Portuguese\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\Portuguese");
	LocalizeComponentStatusText ("Documentation\\Simp_Chinese\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\Simp_Chinese\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\Simp_Chinese\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\Simp_Chinese");
	LocalizeComponentStatusText ("Documentation\\Spanish\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\Spanish\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\Spanish\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\Spanish");
	LocalizeComponentStatusText ("Documentation\\Trad_Chinese\\Cmd_Ref");
	LocalizeComponentStatusText ("Documentation\\Trad_Chinese\\Doc_Misc");
	LocalizeComponentStatusText ("Documentation\\Trad_Chinese\\Sys_Admin_Guide");
	LocalizeComponentStatusText ("Documentation\\Trad_Chinese");
	LocalizeComponentStatusText ("Documentation");
	LocalizeComponentStatusText ("Info");
	LocalizeComponentStatusText ("Languages\\Lang_English");
	LocalizeComponentStatusText ("Languages\\Lang_German");
	LocalizeComponentStatusText ("Languages\\Lang_Japanese");
	LocalizeComponentStatusText ("Languages\\Lang_Korean");
	LocalizeComponentStatusText ("Languages\\Lang_Portuguese");
	LocalizeComponentStatusText ("Languages\\Lang_Simp_Chinese");
	LocalizeComponentStatusText ("Languages\\Lang_Spanish");
	LocalizeComponentStatusText ("Languages\\Lang_Trad_Chinese");
	LocalizeComponentStatusText ("Languages");
	LocalizeComponentStatusText ("Light_Client\\Win95\\Light95_Client_Common");
	LocalizeComponentStatusText ("Light_Client\\Win95\\Light95_Client_Program");
	LocalizeComponentStatusText ("Light_Client\\Win95\\Light95_Client_System32");
	LocalizeComponentStatusText ("Light_Client\\Win95\\Light95_Client_WinDir");
	LocalizeComponentStatusText ("Light_Client\\Win95");
	LocalizeComponentStatusText ("Light_Client\\Win98\\Light_Client_Common");
	LocalizeComponentStatusText ("Light_Client\\Win98\\Light_Client_Program");
	LocalizeComponentStatusText ("Light_Client\\Win98\\Light_Client_System32");
	LocalizeComponentStatusText ("Light_Client\\Win98\\Light_Client_WinDir");
	LocalizeComponentStatusText ("Light_Client\\Win98");
	LocalizeComponentStatusText ("Light_Client");
	LocalizeComponentStatusText ("Readme\\English\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\English\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\English\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\English");
	LocalizeComponentStatusText ("Readme\\German\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\German\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\German\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\German");
	LocalizeComponentStatusText ("Readme\\Japanese\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\Japanese\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\Japanese\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\Japanese");
	LocalizeComponentStatusText ("Readme\\Korean\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\Korean\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\Korean\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\Korean");
	LocalizeComponentStatusText ("Readme\\Portuguese\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\Portuguese\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\Portuguese\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\Portuguese");
	LocalizeComponentStatusText ("Readme\\Simp_Chinese\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\Simp_Chinese\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\Simp_Chinese\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\Simp_Chinese");
	LocalizeComponentStatusText ("Readme\\Spanish\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\Spanish\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\Spanish\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\Spanish");
	LocalizeComponentStatusText ("Readme\\Trad_Chinese\\Doc_Misc");
	LocalizeComponentStatusText ("Readme\\Trad_Chinese\\Install_Guide");
	LocalizeComponentStatusText ("Readme\\Trad_Chinese\\Release_Notes");
	LocalizeComponentStatusText ("Readme\\Trad_Chinese");
	LocalizeComponentStatusText ("Readme");
	LocalizeComponentStatusText ("Server\\Server_Common");
	LocalizeComponentStatusText ("Server\\Server_Program");
	LocalizeComponentStatusText ("Server\\Server_System32");
	LocalizeComponentStatusText ("Server\\Server_WinDir");
	LocalizeComponentStatusText ("Server");
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: LocalizeComponentStatusText()
//
//  Purpose: This function updates one component to use localized text
//           in its status fields as appropriate
//
///////////////////////////////////////////////////////////////////////////////
function LocalizeComponentStatusText (svComponent)
	NUMBER dwResult;
	STRING svOriginal;
	STRING svLocalized;
begin
	ComponentGetData (MEDIA, svComponent, COMPONENT_FIELD_STATUS, dwResult, svOriginal);

	if (svOriginal == "CLIENT") then
		svLocalized = @STATUS_INSTALLING_CLIENT;
	elseif (svOriginal == "SERVER") then
		svLocalized = @STATUS_INSTALLING_SERVER;
	elseif (svOriginal == "CCENTER") then
		svLocalized = @STATUS_INSTALLING_CCENTER;
	elseif (svOriginal == "LIGHT") then
		svLocalized = @STATUS_INSTALLING_LIGHT;
	elseif (svOriginal == "HEADERS") then
		svLocalized = @STATUS_INSTALLING_HEADERS;
	elseif (svOriginal == "DOCS") then
		svLocalized = @STATUS_INSTALLING_DOCS;
	elseif (svOriginal == "LANGUAGE") then
		svLocalized = @STATUS_INSTALLING_LANGUAGE;
	else
		svLocalized = "";
	endif;

	ComponentSetData(MEDIA, svComponent, COMPONENT_FIELD_STATUS, 0, svLocalized);
end;


///////////////////////////////////////////////////////////////////////////////
//
//  Function: DetectInstalledComponents()
//
//  Purpose: This function will detect if any of our components is already
//           installed.
//
///////////////////////////////////////////////////////////////////////////////
function DetectInstalledComponents()
begin
	if (bWinNT) then
		bClientInstalled = IsAppInstalled(&appFullClient);
		bServerInstalled = IsAppInstalled(&appServer);
		bCcInstalled = IsAppInstalled(&appControlCenter);
		bDocsInstalled = IsAppInstalled(&appDocs);
	else
		// The light client uses the client's registry entries.  There is a light
		// client entry, but it contains nothing under it.  IS makes it but we
		// don't use it.
		bLightClientInstalled = IsAppInstalled(&appLightClient);
	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: ParseCommandLine
//
//  Purpose: This function parses the command line to set up a silent install
//
///////////////////////////////////////////////////////////////////////////////
function ParseCommandLine()
    NUMBER nResult;
    STRING szTemp, szResult[MAX_STRING_LEN], szParms;
    LIST listParms;
begin
    bWSPPInstall = FALSE;
	bPreinstallReplace = FALSE;

    szParms = CMDLINE;

    // Need a list to hold the parms
    listParms = ListCreate(STRINGLIST);

    // Break parms into semicolon separated tokens and put into list
    if (StrGetTokens(listParms, szParms, ";") = 0 ) then
       nResult = ListGetFirstString(listParms, szResult);

    	//  Do until the end of the list
    	while (nResult != END_OF_LIST)
			if (StrFind(szResult, "DIR=") > -1 ) then
            	nResult = StrSub(szTemp, szResult, 4, MAX_STRING_LEN);
            	TARGETDIR = szTemp + "\\AFS";
		
         	elseif (StrFind(szResult, "P=") > -1 ) then
            	nResult = StrSub(szTemp, szResult, StrFind(szResult, "P=") + 2, 1);
            	if (StrFind(szTemp, "Y") > -1 ) then
              		bPreinstallReplace = TRUE;
            	endif;

          	elseif (StrFind(szResult, "C=") > -1 ) then
          		ProcessCompsOnCmdLn(szResult);
          	endif;

			nResult = ListGetNextString(listParms, szResult);
		endwhile;
	endif;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: ProcessCompsOnCmdLn
//
//  Purpose: This function puts the components in a list and
//           checks for setup types on the command line.
//
//
///////////////////////////////////////////////////////////////////////////////
function ProcessCompsOnCmdLn(szParms)
     NUMBER nResult, nIndex;
     STRING szComp;
begin
    bWSPPInstall = TRUE;

	// Strip c= off cmdln string
	nResult = StrSub(szParms, szParms, StrFind(szParms, "C=") + 2, StrLength(szParms) - 2);

	// Get each component from the cmd line
    while (StrLength(szParms))
        nIndex = StrSub(szComp, szParms, 0, StrFind(szParms, ","));
        if (!nIndex) then  // if comma not found then last item in list
        	szComp = szParms;
        	nIndex = StrLength(szComp); // for extracting this string
		endif;

       	if (StrFind(szComp, "Client") > -1) then
        	bFullClientSel = TRUE;
       	elseif (StrFind(szComp, "Server") > -1) then
           	bServerSel = TRUE;
       	elseif (StrFind(szComp, "ControlCtr") > -1) then
        	bCcSel = TRUE;
        endif;

       // Delete component from the command line string
        StrSub(szParms, szParms, nIndex + 1, StrLength(szParms));
     endwhile;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: RemoveInstalledBits
//
//  Purpose: This function silently uninstalls a product.
//
///////////////////////////////////////////////////////////////////////////////
function RemoveInstalledBits(szProductName)
	int nStatus;
	int nType;
	string szValue[MAX_STRING_LEN];
	string szProgram[256];
	string szCmdLine[128];
	int nSize;
	int nIndex;
begin
	// Check for a special case - version 3.4a of the AFS Client
	if ((szProductName = "AFS Client") && (appFullClient.iVer.szTitle = "3.4a")) then
		return Eradicate34Client();
	endif;

    nStatus = RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);
    if (nStatus < 0) goto error;

	nStatus = RegDBGetKeyValueEx(MSUNINSTALL_KEY ^ szProductName, "UninstallString", nType, szValue, nSize);
    if (nStatus < 0) goto error;

	nIndex = StrFind(szValue, " ");
	if (nIndex < 0) goto error;
	
	StrSub(szProgram, szValue, 0, nIndex);
	StrSub(szCmdLine, szValue, nIndex + 1, 128);

	// Make the uninstall run silently
	szCmdLine = szCmdLine + " -a";

	nStatus = LaunchAppAndWait(szProgram, szCmdLine, WAIT);
	if (nStatus < 0) goto error;

	return 0;

error:
	return nStatus;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: AddShortCut
//
//  Purpose: Adds a short cut to the start menu.
//
///////////////////////////////////////////////////////////////////////////////
function AddShortCut(szFolder, szName, szCmdPath, szArgs)
	int result;
	string szCmd;
begin
	szCmdPath = TARGETDIR ^ szCmdPath;
	LongPathToQuote(szCmdPath, TRUE);
	
	szCmd = szCmdPath + " " + szArgs;
	
	nResult = AddFolderIcon(SHELL_OBJECT_FOLDER ^ szFolder, szName, szCmd, "", "", 0, "", REPLACE);
	
	return nResult;
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: CreateStartMenuEntries
//
//  Purpose: This function creates start menu entries for our apps.
//
///////////////////////////////////////////////////////////////////////////////
function CreateStartMenuEntries()
begin
	if (bServerSel) then
		AddShortCut(@SERVER_GROUP_NAME, @CFG_WIZARD_ITEM_NAME, CFG_WIZARD_CMD_PATH, CFG_WIZARD_ARGS);
	endif;
	
	if (bFullClientSel) then
		AddShortCut(@CLIENT_GROUP_NAME, @CREDS_TOOL_ITEM_NAME, CREDS_TOOL_CMD_PATH, CREDS_TOOL_ARGS);
		AddShortCut(@CLIENT_GROUP_NAME, @CLIENT_HELP_ITEM_NAME, CLIENT_HELP_CMD_PATH, CLIENT_HELP_ARGS);
	endif;
	
	if (bCcSel) then
		AddShortCut(@CCENTER_GROUP_NAME, @SERVER_MANAGER_ITEM_NAME, SERVER_MANAGER_CMD_PATH, SERVER_MANAGER_ARGS);
		AddShortCut(@CCENTER_GROUP_NAME, @ACCT_MANAGER_ITEM_NAME, ACCT_MANAGER_CMD_PATH, ACCT_MANAGER_ARGS);
	endif;
	
	if (bLightClientSel) then
		AddShortCut(@LIGHT_GROUP_NAME, @CREDS_TOOL_ITEM_NAME, CREDS_TOOL_CMD_PATH, CREDS_TOOL_ARGS);
		AddShortCut(@LIGHT_GROUP_NAME, @LIGHT_CLIENT_HELP_ITEM_NAME, LIGHT_CLIENT_HELP_CMD_PATH, LIGHT_CLIENT_HELP_ARGS);
	endif;

	AddShortCut("", @DOCUMENTATION_ITEM_NAME, DOCUMENTATION_CMD_PATH, DOCUMENTATION_ARGS);
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: ShowError
//
//  Purpose: This function shows an error to the user when not in silent mode,
//           and will log an error to a file when in silent mode.
//
///////////////////////////////////////////////////////////////////////////////
function ShowError(szMsg, nType)
    string szType;
    int nFileID;
begin
    if (!bWSPPInstall) then
        MessageBox(szMsg, nType);
        return;
    endif;

    switch (nType)
        case WARNING:       szType = "Warning: ";
        case SEVERE:        szType = "Error: ";
        case INFORMATION:   szType = "Note: ";
        default:            szType = "Unknown error type: ";
    endswitch;

    WriteToInstallErrorLog(szType + szMsg);
end;


///////////////////////////////////////////////////////////////////////////////
//
// Function: CheckCopyCellServDB
//
//  Purpose: During install the CellServDB file is copied into the Common dir.
//			 If the user does not already have this file, we will copy it for
//           them into the windows directory.  If they already do have the
//           file, then we will do nothing.
///////////////////////////////////////////////////////////////////////////////
function CheckCopyCellServDB()
	int nResult;
	string szResult;
begin
	nResult = FindFile(WINDIR, CELLSERVDB_FILENAME, szResult);
	if (nResult < 0) then
		VarSave(SRCTARGETDIR);
		SRCDIR = TARGETDIR ^ "Common";
		TARGETDIR = WINDIR;
		CopyFile(CELLSERVDB_FILENAME, CELLSERVDB_FILENAME);
		VarRestore(SRCTARGETDIR);
	endif;
end;


// --- include script file section ---
#include "sddialog.rul"



