vspacer
 
vspacer
 

InstallShield

 

 
 * *************************************************************
 *
 *    File Name:  SETUP.RUL
 *
 *  Description:  Is3iWest installation script.
 *
 *       Author:  Dave Goodall
 *
 *     Comments:  This template script installs components for the:
 *
 *                - Application Program.
 *                - Alarm Relay Contacts Feature.
 *                - Map Feature.
 *
 *     Building:  Before running BUILDx for release build set DEBUG FALSE!
 *        CDrom:  Before running BUILDC/BUILDALL set CDROM_INSTALL TRUE!
 *      Diskset:  Before running BUILDD set CDROM_INSTALL FALSE!
 *
 * InstallSHIELD: Professional Edition 32-bit Version 3.0
 *                NOTE! This is the second setup to use 32-Bit Installshield.
 *                The move was made because we need to update the Win95 and
 *                Windows registries, and 16-Bit IS does not support 32-Bit
 *                functionality ie no registry entries are made on 32-bit
 *                platforms. See on-line help topic  'When to use 16- or
 *                32-bit Installshield'
 *
 *      Limited:  If the IS 32-bit compiler fails to compile this file with
 *      Warranty  'memory allocation err-ex' or 1627 errors you have overrun
 *                an internal compiler limit and must reduce the number of
 *                #defines to get a successful compile.
 *                Put some more #ifdef SETUP_RUL exclusions in consdefs.hpp
 * Dire Warning:  InstallShield 3 is VERY buggy. Before trying to use it
 *                review files GOTCHAS.IS3 and WANTCHAS.IS3!
 *                To find IS3 bug fixes and workararounds in this
 *                file grep for IS3.
 *
 *      Syncing:  It is VERY desirable that as far as possible changes
 * Setup to the   in the application get reflected in setup.
 *  application
 *
 *             1. One way this is done is to drive setup from the
 *                consdefs.hpp file. Do NOT hard code constants.
 *                ALWAYS use the consdefs values, and if you need new
 *                ones, then add them to consdefs.hpp and back
 *                propagate changes to the app source code to ensure
 *                that the app and setup stay in sync.
 *
 *                Screw this up, and YOU get to fix the subsequent
 *                maintenance problems!
 *
 *             2. WARNING! In this version of setup the script is NOT
 *                sensitive to the NETMAN.MAK choices of static or dll
 *                library bindings, and implicitly assumes that the
 *                release build will be built with:
 *
 *                DEBUG=OFF
 *
 *                BCDLL=OFF
 *                RWDLL=OFF
 *                ZDLL=OFF
 *                CBDLL=OFF
 *
 *                GFDLL=OFF
 *                CBOARDDLL=ON
 *                SHDLL=ON
 *
 *                And the dll's included or not included in the
 *                distribution and uncmprss.lst are based on this
 *                assumption.
 *
 *                Build the release any other way, and you must
 *                check what dll's should be included/discarded.
 *
 *  Programming: -Don't use the InstallShield 'NUMBER' data type.
 *       Wisdom   Use LONG instead and hungarian lPrefix.
 *
 *               -Use the ^ operator ONLY for FULLY qualified paths (c:\temp)
 *                To assemble sub-directory paths use + "\\"
 *                eg   CBDIR + "\\" + ASK_DOT_ASK = cb\*.*
 *                but  CBDIR ^ ASK_DOT_ASK        = .cb\*.*
 *
 *               -Don't use the vanilla CopyFile, XCopyFile, and
 *                CompressGet functions.
 *                ALWAYS use the 'wrapped' GaCopyFile, GaXCopyFile
 *                gaCompressGet functions.
 *                EXCEPT if you're going to fiddle with them after
 *                you've copied them (see UpdateOrReplaceAppIni() for example).
 *
 *                Installshield has this trifling problem of truncating files
 *                on the target disk when copying from Warren fried Cdrom
 *                drives, but reports no errors.
 *
 *                The wrapped functions support immediate error and post
 *                transfer file size checks.
 *
 *               -Be aware that some functions take fully qualified
 *                drive+path+filename arguments, and some filename only
 *                arguments. The second flavor generally require that
 *                you set SRCDIR to the drive+path BEFORE you use them!
 *
 *  All CDRom's   8x Cdrom readers will not reliably read back from
 *  are not       Re-writable CDroms with blue/green polymer recording
 *  created equal surfaces.  Use the gold polymer type!
 *
\*----------------------------------------------------------------------------*/
// Build control declarations
//#define DEBUG            TRUE     // Activate Debug logic
#define DEBUG              FALSE    // Release
#define CDROM_INSTALL      TRUE     // BUILDALL.BAT CDROM Release Install
                                    // BUILDC.BAT   CDROM English-Only Install
//#define CDROM_INSTALL    FALSE    // BUILDD.BAT   Diskette Install
// Set externally by parm to buildc.bat
//#define LANG             0        // English
//#define LANG             1        // Spanish
//#define LANG             2        // French
//#define LANG             3        // German
//------------------------------------------------------------------------------
// IS3 BUGFIX!
// The following define has two major functions:
// - It ensures that the IS3 compiler will not try to include win.hpp in
//   consdefs.hpp and blow up.
// - It limits the number of #defined constants picked up from consdefs.hpp
//   and _isuser.hpp to only those that setup requires.
//   If too many defined constants are included the is3iwest compiler 3.00.096
//   will overwrite itself internally and fail with error 1627 or 1355.
//   Best guess is that the limit is 1024.  See Gotcha #40.
#define SETUP_RUL TRUE               // Don't include win.hpp in consdefs.hpp
//------------------------------------------------------------------------------
// Externalized Glenayre custom bitmap-dialog-string resources
// These bitmaps and icons exist as resources in _isuser.dll
// but cannot be accessed with IS 3.0 (gotcha #38).
// For the moment, use these definitions to access external
// bitmap and icon files in the SUPPORTDIR.
#define NEWINST_DOT_BMP           "newinst.bmp"
#define UPDINST_DOT_BMP           "updinst.bmp"
#define ENGLISH_DOT_BMP           "english.bmp"
#define SPANISH_DOT_BMP           "spanish.bmp"
#define FRENCH_DOT_BMP            "french.bmp"
#define GERMAN_DOT_BMP            "german.bmp"
#define NTADMIN_DOT_DLL           "ntadmin.dll"
//------------------------------------------------------------------------------
// Included Declarations
// Ini parms obsoleted with Falcon (3.0)
#define ALARM_MGR_SECTION          "Alarm Manager"
//------------------------------------------------------------------------------
// Glenayre Constant declarations.
#define ENUM_ENGLISH                0
#define ENUM_SPANISH                1
#define ENUM_FRENCH                 2
#define ENUM_GERMAN                 3
#define ENGLISH                     "english"
#define SPANISH                     "spanish"
#define FRENCH                      "french"
#define GERMAN                      "german"
// Network Manager RECOMMENDED and MINIMUM requirements
#define HDW_30_REC_MEM_MB             32    // RAM memory recommended
#define HDW_30_MIN_MEM_WITH_MAP_MB    16    // RAM memory minimum
#define HDW_30_MIN_MEM_BASE_APP_MB    12
#define HDW_30_REC_CPU            IS_PENTIUM    // CPU recommended
#define HDW_30_REC_CPU_STR        "166 MHz Pentium or Pentium Pro 200"
#define HDW_30_MIN_CPU_STR        "100MHz Pentium"
// wls magic cookie values.
#define MIN_PAGEFILE_SIZE_MB      125   // MB
#define MIN_FREE_DISC_SPACE_MB    50    // MB
// When increasing the pagefile size, this amount of space must be left free.
#define MIN_BUFFERS               30    // Config.sys } if less forces
#define MIN_FILES                 60    // Config.sys } reboot user machine
#define COMPANY_NAME              "Glenayre Technologies"
#define PRODUCT_NAME              "GL-N2000 OMC (Netman)"
#define PRODUCT_VERSION           "3.01"
#define PRODUCT_KEY               "n2000.exe"
#define DEINSTALL_KEY             "N2000DeinstKey"
#define OLD_APP_NAME              "Network Manager"
#define PROGRAM_FOLDER_NAME       '"Glenayre Solutions"'  //Pgm Mgr Group Title
#define NETMAN                    "NETMAN"  // old default dir / startup pgm
#define N2000                     "N2000"   // new default dir / startup pgm
#define CBDIR                     "CB"      // ComputerBoards Cfg directory
#define PDIS08DIR                 "PDIS08"  // ComputerBoards Utility directory
#define ENUM_EUROPE        1
#define ENUM_NORTHA        2
#define ENUM_SOUTHA        3
#define ENUM_SASIA         4
#define ENUM_ERR_CREATING_LIST    66
#define ENUM_ERR_OPENING_FILE     67
#define ENUM_ERR_PARSING_FILE     68
#define ENUM_ERR_READING_FILE     69
#define ENUM_ERR_WRITING_FILE     70
#define ENUM_ERR_OPENING_DLL      71
#define ENUM_MATCHED             777
#define CSTR_ARC_SUPPORT          "ARC Support"
#define CSTR_DASH_LINE            "-------------------------------------"
#define CSTR_REM                  "rem: "
#define CSTR_ADDED_BY             " added by "
#define CSTR_SET                  "set"     // Batch file cmd to set env vars
#define CSTR_SCANDISK             "scandisk"
#define CSTR_WIN                  "win"
#define CSTR_REG_SZ               "REG_SZ"
#define CSTR_REG_MULTI_SZ         "REG_MULTI_SZ"
#define CSTR_REG_BINARY           "REG_BINARY"
#define CSTR_REG_DWORD            "REG_DWORD"
#define CSTR_REG_EXPAND_SZ        "REG_EXPAND_SZ"
#define CSTR_REG_AUTO_LOGON_KEY
                   "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"
#define CSTR_REG_AUTO_ADMIN_LOGON    "AutoAdminLogon"
#define CSTR_REG_DEFAULT_USER_NAME   "DefaultUserName"
#define CSTR_REG_DEFAULT_DOMAIN_NAME "DefaultDomainName"
#define CSTR_REG_DEFAULT_PASSWORD    "DefaultPassword"
#define STR_DEFTAB                "        " // Default indent in Info Boxes
#define MAX_SIZE                  250       // String length limit. Max is 300.
#define MAX_MODE_STR             1024       // The max for Win NT & Win95
                                            // See comment in WriteFileToLog()
#ifdef LOCKED_OUT_FOR_30
// From DDK winnet.h : wfw detect constants
#define WNNC_NET_TYPE             0x0002
#define WNNC_NET_NONE             0x0000
#define WNNC_NET_MultiNet         0x8000
#define WNNC_SUBNET_WinWorkgroups 0x0004
#endif
#define GA_PBUT_EXIT_SETUP         9        // See sdrc.h SD_PBUT_EXIT_SETUP
// icomp data.z -l > output file added to _setup.lib by buildd.bat
#define DATAZ_DOT_CMP             "dataz.cmp"
#define ASK_DOT_ASK               "*.*"
#define DOT_DEF                   ".def"    // Default .ini file extension
#define DOT_EXE                   ".exe"
#define DOT_HLP                   ".hlp"
#define DATA_DOT_Z                "data.z"  // Working file in SUPPORTDIR
#define DATA_DOT_1                "data.1"  // Split compressed library
// Database Backups
#define AST_DOT_DBF               "*.dbf"
#define AST_DOT_CDX               "*.cdx"
#define AST_DOT_FPT               "*.fpt"
#define AST_DOT_DAT               "*.dat"
#define UNINST_DOT_EXE            "uninst.exe"
#define WINHELP_DOT_EXE           "winhelp.exe"
#define NOTEPAD_DOT_EXE           "notepad.exe"
#define CONTROL_DOT_EXE           "control.exe"   // ControlPanel
#define TIMEDATE_DOT_CPL          "timedate.cpl"  // ControlPanel applet name
#define README_DOT_TXT            "readme.txt"
#define NTDRIVER_DOT_EXE          "ntdriver.exe"
#define DISPMGR_DOT_EXE           "dispmgr.exe"
#define FORWARD_DOT_EXE           "forward.exe"
#define UI_DOT_EXE                "ui.exe"
#define NTUI_DOT_EXE              "ntui.exe"
#define N2000_DOT_EXE             "n2000.exe"
#define FILTCONV_DOT_EXE          "filtconv.exe"
#define DBCONVW_DOT_EXE           "dbconvw.exe"
#define DBFIX_DOT_EXE             "dbfix.exe"
#define SPEAKER_DOT_DRV           "speaker.drv"
#define DRIVERS                   "drivers"      // WINNT/SYSTEM32/DRIVERS
#define GIVEIO_DOT_SYS            "giveio.sys"   // ARC - NT kernel driver
#define SYSTEM_DOT_INI            "system.ini"
#define WIN_DOT_INI               "win.ini"
#define CONFIG_DOT_SYS            "config.sys"
#define AUTOEXEC_DOT_BAT          "autoexec.bat"
#define AUTOEXEC_DOT_BAK          "autoexec.ga" //Backup of user's autoexec.bat
#define ARC16_DOT_DLL             "arc16.dll"
#define ARC32_DOT_DLL             "arc32.dll"
#define ARC16_95_DOT_DLL          "arc16_95.dll"
#define ARC32_95_DOT_DLL          "arc32_95.dll"
#define ARC32_NT_DOT_DLL          "arc32_NT.dll"
#define CBW_NT_DOT_DLL            "cbw_nt.dll"
#define CBW_95_DOT_DLL            "cbw_95.dll"
#define CBW_DOT_DLL               "cbw.dll"
#define CBI_DIO_DOT_DLL           "cbi_dio.dll"
#define CBI_CTR_DOT_DLL           "cbi_ctr.dll"
#define BC450RTL_DOT_DLL          "bc450rtl.dll"
#define SETUP_DOT_LOG             "setup.log"
#define CSTR_386ENH               "386Enh"
#define REDO                      6666      // Hopefully unique from IS consts
#define LOCALE_SENGLANGUAGE       0x00001001
#define LANGUAGE_LEN              30
//  Note : SetStatusWindow() does NOT update the status bar.
//         ONLY the file transfer can update the bar.
//         So we can't stop the progress bar at say 85%
//         after the file xfer and then increment it as we
//         complete each of the next functions.
#define PERCENT_PROGRESS_AT_END_FILE_XFER 99
declare
        // IS script dialog defs. sddialog.h includes SDINT.H with prototypes
        // for the WIN API's USER.LoadString and KERNEL.GetModuleHandle
        // Global variable declarations.
        STRING ITEM_APP;                    // A constant!
        STRING svUninstLogFile, szRegKey, szAppSharedDir;
        STRING szComponentList;             // App installable components list
        STRING szFileSet;                   // Controls Component Transfer
        LONG   lTmp;                        // Used in main section only
        STRING szTmp;                       // Used in main section only
        STRING szBuf;                       // Used in main section only
        STRING szBuf1;                      // Used in main section only
        STRING szFileXferDest;              // Setup -> Perform FileTransfer()
        LIST   listCopiedFiles;             // List of copied files
        LONG   hLogFile;                    // Log file handle
        HWND   hIsUserHandle;               // Handle to ISUSER.DLL
        // Glenayre data declarations
        // program set flags
        BOOL   bLogFile;                    // Setup log available
                                            // Note! For execute step only!
        BOOL   bSpaceOk;
        BOOL   bReboot;                     // Set if changes require reboot
        BOOL   bRestartWin;                 // Set if changes require restart
        BOOL   bEnuffRAMForMap;             // Set if >= minimum required RAM
        BOOL   bMapSelected;
        BOOL   bMapsSelected;               // More than one Map selected
        BOOL   bMapEuropeSelected;
        BOOL   bMapNorthaSelected;
        BOOL   bMapSouthaSelected;
        BOOL   bMapSasiaSelected;
        LONG   lSetDefaultsForMap;          // ENUM_EUROPE/NORTHA/SOUTHA/SASIA
        // User set flags
        BOOL   bNewInstall;
        BOOL   bExistingApp;
        BOOL   bAppSelected;
        BOOL   bRunMapsFromCdrom;
        BOOL   bMapUnitsKm;
        BOOL   bAlarmRelayContactsSelected;
        BOOL   bSetIpProductKey;
        BOOL   bEnableIpSupport;
        BOOL   bEnableQT1000Support;
        BOOL   bAddAppToStartupGrp;
        BOOL   bAutoLogon;
        BOOL   bInstallPCSoundDriver;
        BOOL   bEnableAlarmForwarding;
        BOOL   bCopyOldDBFiles;             // From old \netman for an update
        BOOL   bCommit;                     // User committed to setup phase 2
        BOOL   bOfferAutoLogon;             // Offer Auto Logon Configuration
                                            // dialog and set registry.
                                            // setup : enabled/disabled
        STRING szDefaultAutoAdminLogon;     // Autologon value on entry
        STRING szDefaultUserName;           // Autologon value on entry
        STRING szDefaultDomainName;         // Autologon value on entry
        STRING szDefaultPassword;           // Autologon value on entry
        STRING szNewUserName;               // Autologon value on entry
        STRING szNewDomainName;             // Autologon value on entry
        STRING szNewPassword;               // Autologon value on entry
        LONG   lRebootRestartExit;          // SYS_BOOTWIN - Restart (W31 only)
                                            // SYS_BOOTMACHINE - Reboot machine
                                            // 0 - do not reboot or restart Win
        STRING szSrcPath [ MAX_SIZE ];      // Fully qualified path to src root
                                            // ie E:\ usually but could be
                                            // a network drive+path if network
                                            // install. Set once at startup.
                                            // Treat as READ ONLY!
        STRING szAppPath [ MAX_SIZE ];      // Fully qualified path to app
        STRING szOldAppPath [ MAX_SIZE ]    // Fully qualified path to old
                                            // Netman directory if there is one
                                            // Empty "" if not.
        typedef SYSINFOSTRUCT
           begin
              BOOL   bCdrom;                // TRUE/FALSE - CDRom installed
              LONG   lCpu;                  // IS_486,IS_PENTIUM etc
              STRING szCpu[10];
              LONG   lBaseMemoryKb;         // raw buggy GetSystemInfo Output
              LONG   lExtendedMemoryKb;     // raw buggy GetSystemInfo Output
              LONG   lSystemMemoryMb;       // calculated
              LONG   lOS;                   // IS_WINDOWS, etc
              LONG   lOSMajor;              // Major version of OS
              LONG   lOSMinor;              // Minor version of OS
              BOOL   bUserIsMemberOfAdminGroup;   // For Windows NT
              BOOL   bShare;                // SHARE or VSHARE(WFW) is loaded.
              LONG   lWinMajor;             // major version eg 3
              LONG   lWinMinor;             // minor version eg 10
              BOOL   bWFWInstalled;         // TRUE/FALSE
              BOOL   bWin32sInstalled;      // TRUE/FALSE
              LONG   lWin32sMajor;          // 0 not installed or Ver digit 1-2
              LONG   lWin32sMinor;          // Ver decimals nn 00-30
              STRING szLanguage[LANGUAGE_LEN]; // English name current language
           end;
        SYSINFOSTRUCT SysInfo;              // Declare a SYSINFOSTRUCT
        // Workaround buggy IS3 GetSystemInfo function
        // which can't determine lSystemMemoryMb properly.
        typedef MEMMANINFO
           begin
              LONG   dwSize;                // sizeof MEMMANINFO
              LONG   dwLargestFreeBlock;    // Not used
              LONG   dwMaxPagesAvailable;   // Not used
              LONG   dwMaxPagesLockable;    // Not used
              LONG   dwTotalLinearSpace;    // Not used
              LONG   dwTotalUnlockedPages;  // Not used
              LONG   dwFreePages;           // Not used
              LONG   dwTotalPages;          // *4 = Total Memory Kb
              LONG   dwFreeLinearSpace;     // Not used
              LONG   dwSwapFilePages;       // Not used
              SHORT  wPageSize;             // WORD still 16-bits in Win32
           end;
        MEMMANINFO stMemManInfo;
        POINTER pstMemManInfo;
        // Glenayre Function declarations.
        // Language support
        prototype KERNEL32.GetLocaleInfoA(LONG, LONG, BYREF STRING, INT);
        prototype KERNEL32.GetSystemDefaultLCID();
        prototype GetLanguage( BYREF SYSINFOSTRUCT);
        prototype ResetSRCDIR();
        prototype SetupScreen();
        prototype ExitHandler();
        prototype CloseLogFile();
        prototype GetSysInfo( BYREF SYSINFOSTRUCT, BYREF MEMMANINFO);
        prototype BOOL TOOLHELP.MemManInfo( POINTER ); // TOOLHELP function
#ifdef LOCKED_OUT_FOR_30
        prototype WFWDetect();                 // Detect wfw driver&networking
        prototype INT USER.WNetGetCaps( INT ); // WIN API USER.EXE function
#endif // LOCKED_OUT_FOR_30
        prototype SelectLanguage();
        // Glenayre defined support ntadmin.dll functions
        prototype BOOL ntadmin.RUNNINGASADMINISTRATOR();
        prototype ShowSysInfo( SYSINFOSTRUCT, MEMMANINFO, BOOL );
        prototype CheckCdromInstallFromCdrom();
        prototype CheckOsIs32Bit();
        prototype CheckNTServicePack();
        prototype CheckMinRequirements( SYSINFOSTRUCT );
        prototype DisplayWelcomeDialog();
        prototype CaptureNewUpdateOption();
        prototype AddComponentsToList( STRING );
        prototype DetermineCmpComponentSize( STRING, STRING );
        prototype DetermineUnCmpComponentSize( STRING, STRING );
        prototype CaptureComponentsAndDrive();
        prototype CreateDirectory( STRING, STRING );
        prototype LocateOldNetmanDirectory (BYREF STRING);
        prototype SearchDriveList ( LIST, BYREF STRING);
        prototype CheckUserComponentSelections();
        prototype AskIfOkToDo( STRING, LONG, LONG );
        prototype WarnDirectoryError( STRING, LONG, STRING );
        prototype EnoughSpace( STRING, STRING, LONG );
        prototype SelectMapOptions();
        prototype SelectAppOptions();
        prototype CaptureIpProductKey();
        prototype IsIpProductKeyInstalled();
        prototype IsTcpIpInstalled();
        prototype IsRASInstalled();
        prototype RASSetup();
        prototype GetAutoLogonDefaults();
        prototype AutoLogon();
        prototype ConfirmSelections();
        prototype UpdateRegistry();
        prototype UpdateRegistryAppPath();
        prototype UpdateRegistryRelayDriver();
        prototype UpdateRegistryAutoLogon();
        prototype UpdateRegistryPagefile();
        prototype RegistryError( STRING, STRING );
        prototype CreateRegistryKey( STRING, STRING, LONG, STRING, LONG);
        prototype LogRegistryKey( STRING, STRING, LONG, STRING, LONG);
        prototype DeleteOldFiles();
        prototype CreateArcDirectories();              // redundant?
        prototype SetupFileTransfer( STRING, STRING, STRING );
        prototype PerformFileTransfer( STRING, STRING );
        prototype FileSetError( LONG, STRING );
        prototype MoveAppIniToAppDir();
        prototype FilterConvert();
        prototype UpdateOrReplaceAppIni();
        prototype WriteIniEntry( STRING, STRING, STRING, STRING );
        prototype WriteNumComment( STRING, STRING, STRING, LONG);
        prototype WriteStrComment( STRING, STRING, STRING, STRING);
        prototype WriteNumDefault( STRING, STRING, STRING, LONG);
        prototype WriteStrDefault( STRING, STRING, STRING, STRING);
        prototype UpdateMapDefaults( STRING );
        prototype WriteMapRegionDefaults( STRING, STRING,
                                          STRING, STRING, STRING,
                                          LONG, LONG, LONG, LONG );
        prototype RelocateNumNonDefault( STRING, STRING, STRING, LONG, STRING);
        prototype UpdateWinIni();
        prototype UpdateSystemIni();
        prototype UpdateAutoexecBat();
        prototype UpdateConfigSys();
        prototype SetupArcDLLsEXEs( SYSINFOSTRUCT );
        prototype CreateProgramFolderAndIcons();
        prototype AddIcon( STRING, STRING, STRING, STRING, STRING);
        prototype SetAppIconInStartupFolder( BOOL );
        prototype LaunchDateTimeApplet();
        prototype BackupDatabaseFiles();
        prototype CopyDbFiles();
        prototype ConvertDatabaseFiles();
        prototype RunDBFix();
        prototype CheckFileTransfer();
        prototype ParseDatazCmpToList( LIST );
        prototype CheckSrcTgtDirectories( LIST, STRING, STRING );
        prototype CheckCmpTgtDirectories( LIST, LIST, STRING );
        prototype CheckSrcEqTgtFileSize( LIST, STRING , STRING );
        prototype BugUserToReadTheReadmeFile();
        prototype RebootRestartExit();
        prototype ExecuteFinishReboot( LONG );
        prototype GaCopyFile( STRING, STRING, BOOL, BOOL, BOOL);
        prototype GaXCopyFile( STRING, STRING, STRING, LONG);
        prototype GaCompressGet( STRING, STRING, LONG);
        prototype CopyError( STRING , LONG, BOOL, BOOL, BOOL);
        prototype SetupError( STRING );
        prototype WriteLogMsg( STRING );
        prototype WriteFileToLog ( STRING, STRING );
        prototype LoadString128( LONG, BYREF STRING);
program
   StartHere:
   // Set and treat as constants!
        ITEM_APP = PRODUCT_NAME + " V" + PRODUCT_VERSION;
        szSrcPath = SRCDIR;            // Save src path to global
   // Other inititializations
        Disable( BACKGROUND );
        Enable (BITMAP256COLORS);
        bReboot = FALSE;
        bRestartWin = FALSE;
        lRebootRestartExit = 0;
   // Load the custom support DLLs from the support directory.
   // This assumes that buildc/d.bat have compressed them into _SETUP.LIB
   // NOTE! _isuser.dll and _isres.dll are automatically loaded by setup
   //       and useDLL is NOT needed for them. See gotcha #36.
   // Get module handle to _isuser.dll.  This is used throughout
   // the program to access string resources in this dll.
        szTmp = ISUSER;      // Unique session permuted _ISUSER name
        hIsUserHandle = GetModuleHandle( szTmp );
        if ( hIsUserHandle = 0 ) then
            SprintfBox(SEVERE, "Main()", "Failed to getModuleHandle to %s.",
                                     szTmp);
            ExitHandler();
        endif;
   // Resource strings available
   // Setup for N2000 V3.0:
   //   - is internationalized, but localization has only been done by machine
   //     translation for _isres.dll strings, and not at all for the bitmaps.
   //   - When invoked as setup.exe without command line parameters does not
   //     display the SelectLanguage() dialog and defaults to English.
   //   - Accepts these (case insensitive) command line parameters:
   //       english
   //       spanish
   //       french
   //       german
   //       select   (display the SelectLanguage() dialog)
   // Let the user select the installation language
   // Language support for CDROM only right now
        if ( LANG = 0 && CDROM_INSTALL ) then
            if (CMDLINE % ENGLISH) then
                lTmp = ENUM_ENGLISH;
            endif;
            if (CMDLINE % SPANISH) then
                lTmp = ENUM_SPANISH;
            endif;
            if (CMDLINE % FRENCH) then
                lTmp = ENUM_FRENCH;
            endif;
            if (CMDLINE % GERMAN) then
                lTmp = ENUM_GERMAN;
            endif;
            if (CMDLINE % "select") then
                lTmp = SelectLanguage();
            endif;
            switch ( lTmp )
                case 0:
                    goto Run;
                case 1:
                    Enable (HOURGLASS);
                    DoInstall( SRCDIR ^ SPANISH + "\\setup.ins", "", NOWAIT);
                    ExitHandler();
                case 2:
                    Enable (HOURGLASS);
                    DoInstall( SRCDIR ^ FRENCH  + "\\setup.ins", "", NOWAIT);
                    ExitHandler();
                case 3:
                    Enable (HOURGLASS);
                    DoInstall( SRCDIR ^ GERMAN  + "\\setup.ins", "", NOWAIT);
                    ExitHandler();
                default:
                    SprintfBox(SEVERE,"Main()",
                                      "Unsuppported language [%ld]", LANG);
                    ExitHandler();
            endswitch;
        endif;
   // The SRCDIR will be one directory level down for non-english installs
   // and must be adjusted up one for the copies to work properly.
        if (LANG != 0 && CDROM_INSTALL ) then
              ResetSRCDIR();
        endif;
   Run:
        szTmp = SUPPORTDIR ^ NTADMIN_DOT_DLL;
        if ( UseDLL(szTmp) < 0 ) then
            LoadString128(SETUP_FAILED_TO_LOAD_DLL, szBuf);
            SprintfBox(SEVERE, "Main()", szBuf, szTmp);
            ExitHandler();
        endif;
   // Set installation info., which is required for registry entries.
        InstallationInfo( COMPANY_NAME, PRODUCT_NAME, PRODUCT_VERSION,
                          PRODUCT_KEY );
   // Set up the installation screen.
        SetupScreen();
        Enable( DIALOGCACHE );
   // Fill SysInfo struct with the system's current settings
        GetSysInfo(SysInfo, stMemManInfo);
   // Make sure that the user is not trying to run the setup.exe
   // in the /DISK1 directory (ie the diskette install) off of
   // the CDROM. Yep, this really happened!
        if (!CheckCdromInstallFromCdrom()) then
            ExitHandler();
        endif;
   // Make sure the OS is 32-Bit : Win95, NT or better
        if (!CheckOsIs32Bit()) then
            ExitHandler();
        endif;
   // Check that the OS has the latest service pack applied
       if (!CheckNTServicePack()) then
            ExitHandler();
       endif;
   // The user must have administrator privileges to run Setup under NT.
   // Refer Kbase article Q10122 'How Do Windows NT Default Security
   // Permissions Affect an Installation'
   // Without this, Setup cannot carry out:
   // - Alarm Relay Contacts registry updates.
   // - win.ini access for (needed?) updates :
   //   [Windows] spooler=yes [Spooler] netspool=yes
   // - WinLogon Registry updates.
   // - Create a Common program 'Glenayre Solutions' group or
   //   add icons to the Common program group.
   // - Execute the Windows NT Time Zone editor applet.
   // - Enable-disable autologon
        if ( SysInfo.lOS = IS_WINDOWSNT &&
             !SysInfo.bUserIsMemberOfAdminGroup) then
            LoadString128(SETUP_YOU_MUST_BE_IN_ADMIN, szBuf);
            LoadString128(SETUP_ADD_YOURSELF_TO_ADMIN, szBuf1);
            LoadString128(SETUP_RUN_SETUP_AGAIN, szTmp);
            SprintfBox(SEVERE, "Main()", "%s\n%s\n%s", szBuf, szBuf1, szTmp);
            ExitHandler();
        endif;
   // Read registry to retrieve the current AutoLogon settings.
        GetAutoLogonDefaults();
   // Check that the user pc meets minimum Memory & CPU requirements
   // At User's discretion exit setup if less than the minima.
   // Set flag to not show Map Components if not enough memory.
        CheckMinRequirements(SysInfo);
   // Create a Welcome dialog.
   // This includes a warning  that they should not be running N2000
   // or any other app for that matter, some legal threats, and a
   // chance to exit.
        Disable( BACKBUTTON );
        if (DisplayWelcomeDialog() = CANCEL) then
           ExitHandler();
        endif;
   // Capture New/Update Installation Option
   CaptureNewUpdate:
        CaptureNewUpdateOption();
   // Setup and fill a list of [component + size + selected]
        szAppPath       = TARGETDISK ^ N2000 + "\\";
        szComponentList = "AppComponents";
        AddComponentsToList( szComponentList );
   // Capture components to install and the fully qualified path to the
   // directory in which the user wants the app installed (szAppPath).
   CaptureComponentsAndTargetDrive:
        if ( CaptureComponentsAndDrive() = BACK )
            goto CaptureNewUpdate;
        lTmp = CheckUserComponentSelections();
        switch (lTmp)
            case REDO:
                goto CaptureComponentsAndTargetDrive;
            case BACK:
                goto CaptureNewUpdate;
        endswitch;
   // If the user has selected more than one Map component
   // find out which one he wants to use.
   CaptureMapOptions:
        if (bMapsSelected) then
            if ( SelectMapOptions() = BACK )
                goto CaptureComponentsAndTargetDrive;
        endif;
   // Capture options from the User:
   // For QT1000 Support, RunMapsFromCDrom, Startup Group, AutoStartWindows,
   // and PC Sound Driver etc.
   CaptureAppOptions:
        if ( SelectAppOptions() = BACK )
            goto CaptureComponentsAndTargetDrive;
   // If the user hasn't already installed a valid IP product key
   // and want's an IP based option, ask for the IP product key.
        bSetIpProductKey = FALSE;
        lTmp = IsIpProductKeyInstalled();
        // IS3 BUG : The preceding expression is evaluated separately
        //           as the IS parser wrongly evaluates the following
        //           if expression if it is included in the if statement)
        if ( ( bEnableIpSupport || bEnableAlarmForwarding ) && (!lTmp) ) then
            if ( CaptureIpProductKey() != TRUE) then
                goto CaptureAppOptions;
            else
                bSetIpProductKey = TRUE;
            endif;
        endif;
#ifdef LOCKED_OUT_FOR_30
   // LOCKOUT FOR 3.0 If user selected Enable Alarm Forwarding check to see
   // LOCKOUT FOR 3.0 if he needs RAS, and if so tell him he's on his own.
   // LOCKOUT FOR 3.0     if ( bEnableAlarmForwarding) then
   // LOCKOUT FOR 3.0         if ( RASSetup() = BACK ) then
   // LOCKOUT FOR 3.0            goto CaptureAppOptions;
   // LOCKOUT FOR 3.0         endif;
   // LOCKOUT FOR 3.0     endif;
#endif  // LOCKED_OUT_FOR_30
// If user was offered and selected AutoLogon capture the registry entries.
        if ( bOfferAutoLogon && bAutoLogon ) then
           if ( AutoLogon() = BACK ) then
               goto CaptureAppOptions;
           endif;
        endif;
   // Confirm back to the user the choices he has made and confirm
   // that it's ok to go ahead with the file transfer operation.
        if ( ConfirmSelections() = BACK )
            goto CaptureAppOptions;
   // -- EXECUTE THE SETUP --
        bCommit = TRUE;
   // Create Setup.log file in the SUPPORT directory.
        if ( OpenFileMode(FILE_MODE_APPEND) = 0  &&
            CreateFile( hLogFile, SUPPORTDIR, SETUP_DOT_LOG ) = 0 ) then
            bLogFile = TRUE;
            WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE );
            GetSystemInfo(DATE, lTmp, szBuf);
            GetSystemInfo(TIME, lTmp, szTmp);
            Sprintf( szBuf1, "Setup Started : %s %s", szBuf, szTmp);
            WriteLogMsg( szBuf1 );
        endif;
   // Log the destination directory
        WriteLogMsg(" ");
        WriteLogMsg("AppPath: " + szAppPath);
        WriteLogMsg(" ");
   // Log (and if DEBUG display) current System Info
        ShowSysInfo(SysInfo, stMemManInfo, DEBUG);
   // Update the Registry
        if (!UpdateRegistry()) then
            ExitHandler();
        endif;
   // Because InstallShield can truncate copied files without returning
   // an error, create a list to hold the source and target names of files
   // we're copying.  The Ga wrapped file transfer functions will update it,
   // and CheckFileTransfer() will check this list when copying is complete.
      listCopiedFiles = ListCreate(STRINGLIST);
   // Clean out old files
        if ( !bNewInstall || ( bNewInstall && bExistingApp ) ) then
            DeleteOldFiles();
        endif;
   // Create the Alarm Relay Contact Directories
        if ( bAlarmRelayContactsSelected ) then
            CreateArcDirectories();
        endif;
   // Setup for the file transfer
        Enable ( STATUSDLG );               // Enable Progress Bar Indicator
        if ( CDROM_INSTALL ) then
            szFileSet = "cdrom";            // Note : IS will present default
        else
            szFileSet = "diskette";         // bbrdn billboards (one file set)
        endif;
        SetupFileTransfer( szComponentList, szFileSet, szFileXferDest );
   // Prepare InstallSHIELD to record deinstallation information.
        DeinstallStart( szAppPath, svUninstLogFile, DEINSTALL_KEY, 0 );
        RegDBSetItem( REGDB_UNINSTALL_NAME, PRODUCT_NAME );
   // Transfer files to the target system.
        PerformFileTransfer( szFileSet, szFileXferDest );
        Delay(2);
   // Relocate old /NETMAN/netman.ini file -> new /N2000/n2000.ini and
   // copy database files from old /NETMAN to new /N2000 if necessary
        if(bCopyOldDBFiles && szOldAppPath != "") then
           MoveAppIniToAppDir();
           VarSave( SRCTARGETDIR );
           SRCDIR = szOldAppPath;
           TARGETDIR = szAppPath;
           CopyDbFiles();
           VarRestore (SRCTARGETDIR);
        endif;
   // Run the Filter Conversion program
        FilterConvert();
   // Change or overwrite an existing app.INI file
        UpdateOrReplaceAppIni();
   // Update the Windows WIN.INI file
        UpdateWinIni();
   // Update the Windows SYSTEM.INI file
   // and copy speaker.drv to the WINSYSDIR if necessary
        UpdateSystemIni();
   // Update the AUTOEXEC.BAT file
        UpdateAutoexecBat();
   // Update the CONFIG.SYS file
        UpdateConfigSys();
   // Check that files were transferred correctly
        CheckFileTransfer();
        ListDestroy( listCopiedFiles );
   // Setup the Alarm Relay Contacts dll's and exe's for 95 or NT
   // NOTE! We do this AFTER CheckFileTransfer() because otherwise
   // the cbw_nt.dll->cbw.dll copy would generate a spurious error.
        SetupArcDLLsEXEs( SysInfo );
   // Create App Program Folder and Icons
        CreateProgramFolderAndIcons();
   // Add-Delete Icons in startup folder
        SetAppIconInStartupFolder(bAddAppToStartupGrp);
   // Backup-Convert databases only for update installs.
   // Only if the user has selected the application component.
   // Note dbconvw blows the Alarm.* alarmdis.* and alarmses.*
   // databases away UNLESS it detects that alarm.dbf is in Eagle Format.
   BackupDatabases:
        if(bNewInstall = FALSE && bAppSelected ) then
            if ( BackupDatabaseFiles() = BACK ) then
               goto BackupDatabases;
            else
               ConvertDatabaseFiles();
            endif;
        endif;
   // Remove the status window
        Disable( STATUSDLG );               //Remove the Progress Bar
   // Run DBfix for both new and Update installs
   // NOTE! We only do this for New Installs at this time because
   //       the app won't start up right unless Dbfix is run to create
   //       null serial.dbf and .cdx files.  When this bug gets fixed, move
   //       the following function up inside the if-endif above.
   //       AND take the -QUIET option off!
        RunDBFix();
   // Last chance to torment the user
   ReadTheReadme:
        BugUserToReadTheReadmeFile();
   // Set the Time Zone for new Installs
   // Note, we're doing it here, as if the user doesn't respond
   // before the launch times out, then on NT systems, setup.rul
   // gets back control and starts to run again.
   // 'IS3 limitation' aka bug.
   // Putting it here is a bit less visually disconcerting.
      if( bNewInstall = TRUE ) then
          LaunchDateTimeApplet();
      endif;
   // Reboot Restart Processing
   // Find out if we need to reboot-restart.
   // If so give the user some last minute advice
   RebootRestartXit:
        lRebootRestartExit = RebootRestartExit();
        switch (lRebootRestartExit)
            case 0:
                goto OutaHere;
            case BACK:
                goto ReadTheReadme;
            default:
                lTmp = ExecuteFinishReboot( lRebootRestartExit );
                if (lTmp = BACK)
                   goto RebootRestartXit;
        endswitch;
   // Didn't need to reboot or restart Windows
   OutaHere:
        ExitHandler();
/*---------------------------------------------------------------------------*\
 *
 * Function:  GetLanguage( BYREF SYSINFOSTRUCT)
 *
 *  Purpose:  Access the registry to retrieve the English name of the current
 *            language. Update it to the SysInfoStruct.
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function GetLanguage( SysInfo)
LONG lResult, lCLSID;
STRING szResult, szFunction, szLanguage
begin
        lCLSID = GetSystemDefaultLCID();
        lResult = GetLocaleInfoA(lCLSID, LOCALE_SENGLANGUAGE, szLanguage, 1024);
        if (lResult = 0) then
           SysInfo.szLanguage = "";
        else
            if (lResult <= LANGUAGE_LEN) then
                SysInfo.szLanguage = szLanguage;
            else
                StrSub(szResult, szLanguage, 0, LANGUAGE_LEN);
                SysInfo.szLanguage = szResult;
            endif;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  ResetSRCDIR
 *
 *  Purpose:  Reset SRCDIR for non-english installs back up one directory
 *            level so the file copy functions will find the \N2000, \MAPDATA
 *            etc etc directories.
 *
 *    Input:  LANG
 *
 *   Output:  SRCDIR
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function ResetSRCDIR()
STRING szFn, szLang;
LONG lLen;
begin
        szFn = "ResetSRCDIR()";
        switch (LANG)
            case 1:
                szLang = SPANISH;
            case 2:
                 szLang = FRENCH;
            case 3:
                 szLang = GERMAN;
            default:
                 SprintfBox(SEVERE, szFn, "Unsuppported language [%ld]", LANG);
                 ExitHandler();
        endswitch;
        szBuf = SRCDIR;
        lLen = StrFind(szBuf, szLang) - 1;
        if (lLen < 0) then
            SprintfBox( SEVERE, szFn,
                        "ASSERT: LANG=%ld but directory is not %s",
                        LANG, szLang );
            ExitHandler();
        endif;
        StrSub(SRCDIR, szBuf, 0, lLen);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SetupScreen
 *
 *  Purpose:  This function will set up the screen look.  This includes
 *            colors, fonts, text to be displayed, etc.
 *
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function SetupScreen()
string szBitmap;
LONG   lDx, lDy;
begin
        GetExtents( lDx, lDy );
        Enable( FULLWINDOWMODE );
        Enable( INDVFILESTATUS );
        Enable( BITMAP256COLORS );
        Enable( DIALOGCACHE );
        SetFont( FONT_TITLE, STYLE_BOLD, "MS Sans Serif" );
        SetColor( BACKGROUND, BK_BLUE ); // Gradient blue background.
        SetColor( STATUSBAR, BLUE );
        SetTitle( "Setup", 0, BACKGROUNDCAPTION ); // Caption bar text.
        Enable( BACKGROUND );
        Delay( 1 );
        // Show the bitmap.
        Enable( BITMAPFADE );
        PlaceBitmap( SUPPORTDIR ^ "TITLE.BMP", 1, 10, 10,
                                               UPPER_LEFT | BITMAPICON );
        Disable( BITMAPFADE );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  ExitHandler()
 *
 *  Purpose:  This function do any processing required before calling
 *            exit.  All functions should exit through it to ensure
 *            proper shutdown and later restart of setup.
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function ExitHandler()
begin
        UnUseDLL( SUPPORTDIR ^ NTADMIN_DOT_DLL );
        CloseLogFile();
        exit;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CloseLogFile()
 *
 *  Purpose:  Close and flush the logfile
 *
 *    Input:  bCommit
 *            bLogFile
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function CloseLogFile()
STRING szBuf, szBuf1;
LONG   lTmp;
begin
        if (bCommit && bLogFile) then
            WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
            WriteLogMsg( " " );
            GetSystemInfo(DATE, lTmp, szBuf);
            GetSystemInfo(TIME, lTmp, szTmp);
            Sprintf( szBuf1, "Setup Ended : %s %s", szBuf, szTmp);
            WriteLogMsg( szBuf1);
            CloseFile(hLogFile);
            SRCDIR = SUPPORTDIR;
            TARGETDIR = szAppPath;
            CopyFile( SETUP_DOT_LOG, SETUP_DOT_LOG);
            bLogFile = FALSE;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  GetSysInfo(SysInfo, stMemManInfo)
 *
 *  Purpose:  This function will retrieve current system info
 *            into the SysInfoStruct.
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
 *
 * Gotcha!    IS3BUG: COMPILE finds no problem with functions taking
 *            members of structs as return arguments, but will NOT
 *            return valid information into them!
 *
 *            This is really rather vicious, as you are liable
 *            to use the first form automatically. It appears
 *            the Installshield parser needs more work!
 *
 *            Pro tem do not try the one-step :
 *            GetSystemInfo(CDROM, SysInfo.bCdrom, szResult);
 *
 *            Do the two step :
 *            GetSystemInfo(CDROM, nResult, szResult);
 *            SysInfo.bCdrom = nResult;
 *
 *            If InstallShield clean up their act, our code can be
 *            simplified (to the commented out lines).
 *
\*---------------------------------------------------------------------------*/
function GetSysInfo(SysInfo, stMemManInfo)
STRING  szResult, szFileName, szVersionNumber;
LONG lResult, lMemMB, lTmp1, lTmp2;
begin
        // GetSystemInfo(CDROM,          SysInfo.bCdrom, szResult);
        GetSystemInfo(CDROM,             lResult, szResult);
        SysInfo.bCdrom = lResult;
        // GetSystemInfo(CPU,            SysInfo.nCpu,   szResult);
        GetSystemInfo(CPU,               lResult,   szResult);
        SysInfo.lCpu = lResult;
        switch (SysInfo.lCpu)
          case IS_286:
              SysInfo.szCpu = "286";
          case IS_386:
              SysInfo.szCpu = "386";
          case IS_486:
              SysInfo.szCpu = "486";
          case IS_PENTIUM:
              SysInfo.szCpu = "Pentium";
          case IS_UNKNOWN:
              LoadString128(SETUP_UNKNOWN, szBuf);
              SysInfo.szCpu = szBuf;
        endswitch;
        //GetSystemInfo(BASEMEMORY,      SysInfo.lBaseMemoryKb,     szResult);
        GetSystemInfo(BASEMEMORY,        lResult,                   szResult);
        SysInfo.lBaseMemoryKb = lResult;
        //GetSystemInfo(EXTENDEDMEMORY,  SysInfo.lExtendedMemoryKb, szResult);
        GetSystemInfo(EXTENDEDMEMORY,    lResult,                   szResult);
        SysInfo.lExtendedMemoryKb = lResult;
        lTmp1 = SysInfo.lBaseMemoryKb + SysInfo.lExtendedMemoryKb;
        lMemMB = lTmp1 / 1024;              // MB
        // Round up to nearest even MB
        lTmp1 = lMemMB / 2;
        lTmp2 = lTmp1 * 2;
        lTmp1 = lMemMB - lTmp2;
        lTmp2 = lMemMB + lTmp1;
        SysInfo.lSystemMemoryMb = lTmp2;
        //GetSystemInfo(OS,              SysInfo.lOS,               szResult);
        GetSystemInfo(OS,                lResult,                   szResult);
        SysInfo.lOS = lResult;
        if ( SysInfo.lOS = IS_WINDOWSNT ) then
            SysInfo.bUserIsMemberOfAdminGroup = RUNNINGASADMINISTRATOR();
        endif;
        //GetSystemInfo(OSMAJOR,         SysInfo.lOSMajor,          szResult);
        GetSystemInfo(OSMAJOR,           lResult,                   szResult);
        SysInfo.lOSMajor = lResult;
        //GetSystemInfo(OSMINOR,         SysInfo.lOSMinor,          szResult);
        GetSystemInfo(OSMINOR,           lResult,                   szResult);
        SysInfo.lOSMinor = lResult;
        //GetSystemInfo(SHARE,           SysInfo.bShare,            szResult);
        GetSystemInfo(SHARE,             lResult,                   szResult);
        SysInfo.bShare = lResult;
        //GetSystemInfo(WINMAJOR,        SysInfo.lWinMajor,         szResult);
        GetSystemInfo(WINMAJOR,          lResult,                   szResult);
        SysInfo.lWinMajor = lResult;
        //GetSystemInfo(WINMINOR,        SysInfo.lWinMinor,         szResult);
        GetSystemInfo(WINMINOR,          lResult,                   szResult);
        SysInfo.lWinMinor = lResult;
        // Fixup 'compatibility' 'feature' in GetSystemInfo which
        // returns 3.10 even if it's 3.11! Grrrr...
        // Get the Windows version string from USER.EXE
        szFileName = WINSYSDIR ^ "USER.EXE";
        lResult = VerGetFileVersion(szFileName, szVersionNumber);
        StrSub(szVersionNumber, szVersionNumber, 0, 4);
        // Fixup nWinMinor if required
        if (lResult >= 0 ) then
            if ( szVersionNumber = "3.11" ) then
               SysInfo.lWinMinor = 11;
            endif;
        endif;
        //GetSystemInfo(WIN32SINSTALLED, SysInfo.bWin32sInstalled,  szResult);
        GetSystemInfo(WIN32SINSTALLED,   lResult,                   szResult);
        SysInfo.bWin32sInstalled = lResult;
        //GetSystemInfo(WIN32SMAJOR,     SysInfo.lWin32sMajor,      szResult);
        GetSystemInfo(WIN32SMAJOR,       lResult,                   szResult);
        SysInfo.lWin32sMajor = lResult;
        //GetSystemInfo(WIN32SMINOR,     SysInfo.lWin32sMinor,      szResult);
        GetSystemInfo(WIN32SMINOR,       lResult,                   szResult);
        SysInfo.lWin32sMinor = lResult;
        if ( SysInfo.lWin32sMajor = -1 || SysInfo.lWin32sMinor = -1) then
            SysInfo.bWin32sInstalled = FALSE;
        else
            //GetSystemInfo(WIN32SINSTALLED,SysInfo.bWin32sInstalled,szResult);
            GetSystemInfo(WIN32SINSTALLED,lResult,szResult);
            SysInfo.bWin32sInstalled = lResult;
        endif;
#ifdef LOCKED_OUT_FOR_30
        // Determine if we are running WFW
        SysInfo.bWFWInstalled = WFWDetect();
#endif // LOCKED_OUT_FOR_30
        return TRUE;
end;
#ifdef LOCKED_OUT_FOR_30
/*-----------------------------------------------------------------------
   Name    : WFWDetect
   Purpose : Function to determine whether the Windows for Workgroups
             Multinet driver AND Windows for Workgroups networking is
             present by calling WNetGetCaps with nIndex set to WNNC_NET_TYPE.
             When operating under the WFWNET driver umbrella, WNetGetCaps
             will return with the high bit set in the high (Nwtwork Type)
             byte, and bit 3 of the low byte will also be set indicating
             the presence of Windows for Workgroups.
   Returns : TRUE if present, FALSE if not present
   Notes   : Converted from MSDNCD sample code - Search for WFWDetect
             WNetGetCaps is exported from USER but is not in the LIBW.LIB
             import library, so must be explicitly imported in the DEF file:
             IMPORTS   USER.WNETGETCAPS
             This routine only works for a 16-bit system ie one running
             Windows 3.x or Windows for Workgroups.
             If it was run on a 32-bit system eg Win95 it would incorrectly
             return that the system was running Windows for Workgroups.
             This limitation is Ok, since the only reason for this routine
             existing in the first place is that :
             a. IS3 GetSystemInfo with the WINMINOR option returns a bogus
                10 instead of the correct 11 if Windows for Workgroups 3.11
                is installed (Verified with Installshield Tech Support
                who allege this is for 'Compatibility reasons' !)
             b. There is a Windows for Workgroups 3.10, and a Windows plain
                3.10 so that in this case there is still no way of telling
                which flavor of Windows is present.
             For this reason we test the major version of the OS and
             return false if not 3.
-------------------------------------------------------------------------*/
function WFWDetect()
STRING szResult;
LONG   lResult;
INT    iNetType;
BOOL   bWFWNet;
begin
        bWFWNet = FALSE;
        GetSystemInfo(WINMAJOR, lResult, szResult);
        if (lResult = 3 ) then
            // Get the network version type
            iNetType = WNetGetCaps(WNNC_NET_TYPE);
            // AND nNetType with WNNC_NT_Multinet to see if the 0x8000 bit
            // (hibyte) is set.  If it is, a multinet driver is installed.
            if (iNetType && WNNC_NET_MultiNet) then
               // A multinet driver is installed. If 0x0004 bit in nNetType
               // (lobyte) is set, Windows for WorkGroups is installed
               if (iNetType & WNNC_SUBNET_WinWorkgroups) then
                   bWFWNet = TRUE;
               endif;
            endif;
        endif;
        return ( bWFWNet );
end;
#endif  // LOCKED_OUT_FOR_30
/*---------------------------------------------------------------------------*\
 *
 * Function:  SelectLanguage()
 *
 *  Purpose:  This function will capture from the User
 *            the language he wants setup to use.
 *
 *    Input:
 *
 *   Output:
 *
 *  Returns:  lReturn   0=English
 *                      1=Spanish
 *                      2=French
 *                      3=German
 *
 *     Pre-:  The _isuser resource dll must have been successfully loaded
 * Requisite
 *
 * Comments:  Until the toads at InstallShield let us get at bitmaps in
 *            _ISUSER.DLL and not just _ISRES.DLL, this function relies
 *            on external bitmaps being present in the SUPPORTDIR.
 *            As such it is tightly coupled to BUILDC/D.BAT which
 *            must compress the bitmaps into _SETUP.LIB.
\*---------------------------------------------------------------------------*/
function SelectLanguage()
STRING szMsg, szTitle, szTmp, szBuf;
LIST listButtons, listDescrip;
LONG lResult;
begin
        szTitle  = "Setup Language";
        szMsg    = " ";
        // Disable the BACK-NEXT buttons, since we do not want to use them.
        // The dialog terminates after selection.
        Disable( BACKBUTTON );
        Disable( NEXTBUTTON );
        listButtons = ListCreate( STRINGLIST );
        listDescrip = ListCreate( STRINGLIST );
        // set button and description lists
        // NOTE! Undocumented  format - see gotcha #38
        szTmp = SUPPORTDIR ^ ENGLISH_DOT_BMP;
        Sprintf(szBuf, "@%s;0;255,0,255", szTmp);
        ListAddString( listButtons, szBuf, AFTER );
        // Always load English irrespective of the current language setting
        LoadString(hIsUserHandle, SETUP_CHOOSE_LANGUAGE + ENUM_ENGLISH,
                   szBuf, MAX_SIZE);
        ListAddString( listDescrip, szBuf, AFTER);
        // NOTE! Undocumented  format - see gotcha #38
        szTmp = SUPPORTDIR ^ SPANISH_DOT_BMP;
        Sprintf(szBuf, "@%s;0;255,0,255", szTmp);
        ListAddString( listButtons, szBuf, AFTER );
        // Always load Spanish irrespective of the current language setting
        LoadString(hIsUserHandle, SETUP_CHOOSE_LANGUAGE + ENUM_SPANISH,
                   szBuf, MAX_SIZE);
        ListAddString( listDescrip, szBuf, AFTER);
        // NOTE! Undocumented  format - see gotcha #38
        szTmp = SUPPORTDIR ^ FRENCH_DOT_BMP;
        Sprintf(szBuf, "@%s;0;255,0,255", szTmp);
        ListAddString( listButtons, szBuf, AFTER );
        // Always load French irrespective of the current language setting
        LoadString(hIsUserHandle, SETUP_CHOOSE_LANGUAGE + ENUM_FRENCH,
                   szBuf, MAX_SIZE);
        ListAddString( listDescrip, szBuf, AFTER);
        // NOTE! Undocumented  format - see gotcha #38
        szTmp = SUPPORTDIR ^ GERMAN_DOT_BMP;
        Sprintf(szBuf, "@%s;0;255,0,255", szTmp);
        ListAddString( listButtons, szBuf, AFTER );
        // Always load German irrespective of the current language setting
        LoadString(hIsUserHandle, SETUP_CHOOSE_LANGUAGE + ENUM_GERMAN,
                   szBuf, MAX_SIZE);
        ListAddString( listDescrip, szBuf, AFTER);
        // Display the dialog
        lResult = SdOptionsButtons( szTitle, szMsg, listButtons, listDescrip );
        switch(lResult)
          case 101:
                     lResult = ENUM_ENGLISH;
          case 102:
                     lResult = ENUM_SPANISH;
          case 103:
                     lResult = ENUM_FRENCH;
          case 104:
                     lResult = ENUM_GERMAN;
        endswitch;
        ListDestroy( listButtons );
        ListDestroy( listDescrip );
        Enable( NEXTBUTTON );
        Enable( BACKBUTTON );
        return lResult;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  ShowSysInfo(SysInfo, SwapInfo, MemManInfo, bDisplay)
 *
 *  Purpose:  Log information from SysInfoStruct,
 *            and MemManInfo to the setup log to support trouble
 *            shooting by the 1-800-CALL-WLS service.
 *
 *            Optionally display the information in an SdShowInfoList
 *            dialog to aid debugging.
 *
 *    Input:  SysInfoStruct, MemManInfo
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function ShowSysInfo(SysInfo, MemManInfo, bDisplay)
STRING szTitle, szMsg, szInfo, szBuf, szTmp, szYN;
LONG   lTmp;
LIST   listInfo;
begin
        szTitle = "System Information";
        szMsg   = "The following is a diagnostic list of target " +
                  "system settings, setup states, and user selections.";
        // Create a string list and add lines to it
        listInfo = ListCreate(STRINGLIST);
        szInfo = "System:";
        ListAddString(listInfo, szInfo, AFTER);
        // -- CDROM info ---
        switch (SysInfo.bCdrom)
            case TRUE:
                szBuf = "has";
            case FALSE:
                szBuf = "does not have";
        endswitch;
        Sprintf(szInfo, "%sYour machine %s a CDROM drive.",
                  STR_DEFTAB,szBuf);
        ListAddString(listInfo, szInfo, AFTER);
        // --- CPU info ---
        Sprintf(szInfo, "%sYour machine has a %s CPU",
                  STR_DEFTAB,SysInfo.szCpu);
        ListAddString(listInfo, szInfo, AFTER);
        // --- Memory info ---
        Sprintf(szInfo, "%sBase Memory     = %ld Kb",
                  STR_DEFTAB,SysInfo.lBaseMemoryKb);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sExtended Memory = %ld Kb",
                  STR_DEFTAB,SysInfo.lExtendedMemoryKb);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sTotal Memory = %ld MB",
                  STR_DEFTAB,SysInfo.lSystemMemoryMb);
        ListAddString(listInfo, szInfo, AFTER);
        // --- OS info ---
        // Fixup GetSysInfo bug - returns 'Windows 6.22'
        switch (SysInfo.lOS)
            case IS_WINDOWS:
                szBuf = "DOS";               // Yes, DOS NOT Windows!
            case IS_WINDOWSNT:
                szBuf = "Windows NT";
            case IS_WINDOWS95:
                szBuf = "Windows 95";
            case IS_WIN32S:
                szBuf = "Win32s";
            case IS_UNKNOWN:
                szBuf = "UNKNOWN";
        endswitch;
        Sprintf(szInfo, "%sThe Operating System is : %s", STR_DEFTAB,szBuf);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sOS Major.Minor Versions : %ld.%ld",
                         STR_DEFTAB, SysInfo.lOSMajor, SysInfo.lOSMinor);
        ListAddString(listInfo, szInfo, AFTER);
        // --- Windows info ---
        Sprintf(szInfo, "%sWindows Version is : %ld.%ld.",
                         STR_DEFTAB,SysInfo.lWinMajor, SysInfo.lWinMinor);
        ListAddString(listInfo, szInfo, AFTER);
        if (SysInfo.bWFWInstalled) then
            szInfo = STR_DEFTAB + "Windows For WorkGroups is installed.";
            ListAddString(listInfo, szInfo, AFTER);
        endif;
        // User Info
        if ( SysInfo.lOS = IS_WINDOWSNT ) then
            switch (SysInfo.bUserIsMemberOfAdminGroup)
                case TRUE:
                    szBuf = " ";
                case FALSE:
                    szBuf = " not ";
            endswitch;
            Sprintf(szInfo, "%sUser is%sa member of the Adminstrators Group.",
                            STR_DEFTAB,szBuf);
            ListAddString(listInfo, szInfo, AFTER);
        endif;
        // --- SHARE info ---
        switch (SysInfo.bShare)
            case TRUE:
                szBuf = " ";
            case FALSE:
                szBuf = " not ";
        endswitch;
        Sprintf(szInfo, "%sSHARE is%sloaded.", STR_DEFTAB,szBuf);
        ListAddString(listInfo, szInfo, AFTER);
        // --- Win32s info ---
        if (SysInfo.bWin32sInstalled) then
            Sprintf(szInfo, "%sWin32s %ld.%ld is installed.",
                            STR_DEFTAB,SysInfo.lWin32sMajor,
                                       SysInfo.lWin32sMinor);
        else
            szInfo = STR_DEFTAB + "Win32s is not installed.";
        endif;
        ListAddString(listInfo, szInfo, AFTER);
        // --- Language Info ----
        szInfo = "Locale";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sLanguage= [%s]", STR_DEFTAB, SysInfo.szLanguage);
        ListAddString(listInfo, szInfo, AFTER);
        // --- User Selections ---
        szInfo = "User Selection Flags:";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sNew Install=[%d]",
                               STR_DEFTAB,bNewInstall);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sApp Selected=[%d]",
                               STR_DEFTAB,bAppSelected);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sAlarm Relay Contacts Selected=[%d]",
                               STR_DEFTAB,bAlarmRelayContactsSelected);
        ListAddString(listInfo, szInfo, AFTER);
        szInfo = "Map Flags:";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sMap Selected=[%d]",
                               STR_DEFTAB,bMapSelected);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sMaps Selected=[%d]",
                               STR_DEFTAB,bMapsSelected);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_MAP_EUROPE, szBuf);
        Sprintf(szInfo, "%s%s Selected=[%d]", STR_DEFTAB, szBuf,
                                              bMapEuropeSelected);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_MAP_NORTHA, szBuf);
        Sprintf(szInfo, "%s%s Selected=[%d]", STR_DEFTAB, szBuf,
                                              bMapNorthaSelected);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_MAP_SOUTHA, szBuf);
        Sprintf(szInfo, "%s%s Selected=[%d]", STR_DEFTAB, szBuf,
                                              bMapSouthaSelected);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_MAP_SASIA, szBuf);
        Sprintf(szInfo, "%s%s Selected=[%d]", STR_DEFTAB, szBuf,
                                              bMapSasiaSelected);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_MAP_ALL_FROM_CDROM, szBuf);
        Sprintf(szInfo, "%s%s Selected=[%d]", STR_DEFTAB, szBuf,
                                              bRunMapsFromCdrom);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sEnuffRAMForMap=[%d]",
                               STR_DEFTAB,bEnuffRAMForMap);
        ListAddString(listInfo, szInfo, AFTER);
        szInfo = "User Option Flags:";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_ENABLE_IP, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bEnableIpSupport);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_ENABLE_QT1000, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bEnableQT1000Support);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_ADD_TO_STARTUP_GRP, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bAddAppToStartupGrp);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_AUTO_LOGON, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bAutoLogon);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_INSTALL_SPEAKERDRV, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bInstallPCSoundDriver);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_MAP_UNITS_KM, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bMapUnitsKm);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_ENABLE_ALARM_FORWARDING, szBuf);
        Sprintf(szInfo, "%s%s=[%d]", STR_DEFTAB, szBuf, bEnableAlarmForwarding);
        ListAddString(listInfo, szInfo, AFTER);
        szInfo = "Program Set Flags:";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sSetIpProductKey=[%d]",
                               STR_DEFTAB,bSetIpProductKey);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sSpaceOk=[%d]",
                               STR_DEFTAB,bSpaceOk);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sReboot=[%d]",
                               STR_DEFTAB,bReboot);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sRestartWin=[%d]",
                               STR_DEFTAB,bRestartWin);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sDefaultAutoAdminLogon=[%s]",
                               STR_DEFTAB,szDefaultAutoAdminLogon);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sOfferAutoLogon=[%d]", STR_DEFTAB, bOfferAutoLogon);
        ListAddString(listInfo, szInfo, AFTER);
        // Show Memory Status info
        szInfo = "Memory Status Info:";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sTotalPages=[%d]",
                               STR_DEFTAB, SysInfo.lSystemMemoryMb);
        ListAddString(listInfo, szInfo, AFTER);
        // Show flags relating to handling the existing system
        szInfo = "Existing System:";
        ListAddString(listInfo, " ", AFTER);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sExistingApp=[%d]", STR_DEFTAB,bExistingApp);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sCopyOldDBFiles=[%d]", STR_DEFTAB,bCopyOldDBFiles);
        ListAddString(listInfo, szInfo, AFTER);
        Sprintf(szInfo, "%sOldAppPath=[%s]", STR_DEFTAB, szOldAppPath);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, " ", AFTER);
        // Optional (DEBUG) display
        if (bDisplay) then
            // Display the dialog
            SdShowInfoList (szTitle, szMsg, listInfo);
        endif;
        // Always log the information
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE );
        lTmp = ListGetFirstString(listInfo, szInfo);
        while (lTmp = 0)
            WriteLogMsg ( szInfo );
            lTmp = ListGetNextString(listInfo, szInfo);
        endwhile;
        WriteLogMsg ( " ");
        ListDestroy( listInfo );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CheckCdromInstallFromCdrom()
 *
 *  Purpose:  Check that the user is not trying to run the setup.exe in
 *            the \disk1 directory from the Cdrom.  The \disk1 -> \disk5
 *            directories are there to be copied by the user to diskettes
 *            for installs on portable computers without CDrom's.
 *
 *  Returns:  BOOL  TRUE - Running \setup.exe from the cdrom.
 *                  FALSE- Running \disk1\setup.exe from the Cdrom.
 *
 * Comments:  IS_CDROM = 6
 *
\*---------------------------------------------------------------------------*/
function CheckCdromInstallFromCdrom()
LONG lDriveType;
STRING szRoot;
begin
        // Get the drive type of the drive setup is running from.
        GetSystemInfo(DRIVE, lDriveType, SRCDISK);
        if (lDriveType = IS_CDROM && !CDROM_INSTALL ) then
            ParsePath(szRoot, szSrcPath, DISK);
            LoadString128(SETUP_USE_ROOT_SETUP, szBuf);
            SprintfBox( SEVERE, "CheckCdromInstallFromCdrom()", szBuf, szRoot);
            return FALSE;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CheckOsIs32Bit()
 *
 *  Purpose:  The OS must be pure 32-Bit ie 95m, NT or higher
 *
 *  Returns:  BOOL  TRUE
 *                  FALSE
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function CheckOsIs32Bit()
STRING szTitle;
begin
        switch (SysInfo.lOS)
            case IS_WINDOWSNT:
                return TRUE;
            case IS_WINDOWS95:
                return TRUE;
        endswitch;
        LoadString128(SETUP_OS_REQ_DLG_TITLE, szTitle);
        LoadString128(SETUP_N2000_REQUIRES_OS, szBuf);
        SprintfBox( SEVERE, szTitle, szBuf);
        return FALSE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CheckNTServicePack()
 *
 *  Purpose:  Warn the user if the target machine has NT 4.0 installed and
 *            either has
 *            - no service packs installed or
 *            - less than service pack 3 installed.
 *    Input:
 *
 *   Output:
 *
 *  Returns:  BOOL - TRUE Continue with setup, FALSE Exit setup
 *
 * Comments:  HKEY_LOCAL_MACHINE
 *             SOFTWARE
 *              Microsoft
 *               Windows NT
 *                 CurrentVersion    CurrentVersion:REG_SZ:3.51
 *                                   CurrentBuildNumber:REG_SZ:1057
 *                                   CSDVersion:REG_SZ:Service Pack n
 *                                   (Not present if no svc packs applied)
 *
\*---------------------------------------------------------------------------*/
function CheckNTServicePack()
STRING szTitle, szKey, szMsg, szInfo, szBuf, szFn, szVer, szBuild, szPack;
BOOL   bReturn;
LIST   listInfo;
LONG   lSize, lType, lResult, lAsciiValue;
begin
        szFn = "CheckNTServicePack()";
        bReturn = TRUE;
        if ( SysInfo.lOS = IS_WINDOWSNT ) then
            if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) = 0 ) then
                szKey = "\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
                // Check to see if the key exists
                // IS3 TRAP : RegDbKeyExists returns 1 not 0 if key found
                if ( RegDBKeyExist( szKey ) = 1) then
                    // Get the Version
                    if (RegDBGetKeyValueEx(szKey, "CurrentVersion",
                                   lType, szVer, lSize) != 0 ) then
                        szVer = "";
                    else
                        GetByte(lAsciiValue, szVer, 0);
                        if ( lAsciiValue < 52) then
                            szVer = "";    //  '4' = Ascii 52
                        endif;
                    endif;
                    // Get the Build number
                    if (RegDBGetKeyValueEx(szKey, "CurrentBuildNumber",
                                   lType, szBuild, lSize) != 0 ) then
                        szBuild = "";
                    endif;
                    // Get the service pack
                    if (RegDBGetKeyValueEx(szKey, "CSDVersion",
                                   lType, szPack, lSize) != 0 ) then
                        szPack = "";
                    endif;
                    if ( szVer != "" ) then
                        if ( szPack = "" ||
                             StrCompare(szPack, "Service Pack 3") < 0 ) then
                             // Create a string list.
                             listInfo = ListCreate(STRINGLIST);
                             LoadString128(SETUP_YOU_ARE_RUNNING, szInfo);
                             ListAddString(listInfo, szInfo, AFTER);
                             ListAddString(listInfo, " ", AFTER);
                             LoadString128(SETUP_NT_BUILD_INFO, szBuf);
                             Sprintf( szInfo, szBuf, szVer, szBuild, szPack);
                             ListAddString(listInfo, "    " + szInfo, AFTER);
                             ListAddString(listInfo, " ", AFTER);
                             LoadString128(SETUP_APPLY_NT_SVC_PACK, szInfo);
                             ListAddString(listInfo, szInfo, AFTER);
                             ListAddString(listInfo, " ", AFTER);
                             LoadString128(SETUP_SEE_X_FOR_MORE_INFO, szBuf);
                             Sprintf(szInfo, szBuf, README_DOT_TXT);
                             ListAddString(listInfo, szInfo, AFTER);
                             ListAddString(listInfo, " ", AFTER);
                             LoadString128(SETUP_CLICK_CANCEL_TO_EXIT, szInfo);
                             ListAddString(listInfo, szInfo, AFTER);
                             LoadString128(SETUP_CLICK_NEXT_TO_CONTINUE, szInfo);
                             ListAddString(listInfo, szInfo, AFTER);
                             ListAddString(listInfo, " ", AFTER);
                             Disable (BACKBUTTON);
                             Enable  (NEXTBUTTON);
                             Enable  (CANCELBUTTON);
                             // Display the dialog
                             LoadString128(SETUP_OS_REQ_DLG_TITLE, szTitle);
                             LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
                             lResult = SdShowInfoList (szTitle, szMsg, listInfo);
                             ListDestroy( listInfo );
                             if (lResult = CANCEL) then
                                 bReturn = FALSE;
                             endif;
                        endif;
                    endif;
               endif;
            endif;
        endif;
        return (bReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CheckMinRequirements ( SysInfo )
 *
 *  Purpose:  Warn the user if the target machine:
 *            - falls below the minimum Memory and CPU requirements for
 *              the basic application (without Map).
 *            - Has less memory than is needed for the Map feature.
 *            - Specs fall between the required and recommended criteria.
 *
 *    Input:
 *
 *    Output: BOOL   bEnuffRAMForMap;       // Set always TRUE;
 *
 *  Returns:  NEXT
 *
 * Comments:  This function is critically dependent on the target machine's
 *            Memory being determined correctly. Although this is believed
 *            to be accurate as of 3.0, we let the user keep going even if
 *            the machine falls below Minimum requirements.
 *
 *
\*---------------------------------------------------------------------------*/
function CheckMinRequirements( SysInfo )
STRING szTitle, szMsg, szInfo, szBuf;
BOOL   bWarning;
LIST   listInfo;
LONG   lResult;
begin
        // Whether we have enough memory for map or not
        bEnuffRAMForMap = TRUE;
        if ( SysInfo.lCpu            >= HDW_30_REC_CPU &&
             SysInfo.lSystemMemoryMb >= HDW_30_REC_MEM_MB ) then
            // Target machine meets or exceeds the Map recommended spec.
            return NEXT;
        endif;
        // Create a string list.
        listInfo = ListCreate(STRINGLIST);
        if ( SysInfo.lSystemMemoryMb < HDW_30_MIN_MEM_BASE_APP_MB ||
             SysInfo.lCpu < HDW_30_REC_CPU  ) then
            // Target machine below either basic Mem or CPU minima
            LoadString128(SETUP_MIN_REQ_FOR_N2000, szBuf);
            Sprintf( szInfo, szBuf, N2000);
            ListAddString(listInfo, szInfo, AFTER);
            LoadString128(SETUP_MIN_CPU, szBuf);
            Sprintf( szInfo, szBuf, HDW_30_MIN_CPU_STR,
                                    HDW_30_MIN_MEM_BASE_APP_MB);
            ListAddString(listInfo, szInfo, AFTER);
            if ( CDROM_INSTALL ) then
                LoadString128(SETUP_MIN_RAM_WITH_MAP, szBuf);
                Sprintf(szInfo, szBuf, HDW_30_MIN_MEM_WITH_MAP_MB);
                ListAddString(listInfo, szInfo, AFTER);
            endif;
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_TARGET_CPU_RAM, szBuf);
            Sprintf(szInfo, szBuf, SysInfo.szCpu, SysInfo.lSystemMemoryMb);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_UPGRADE, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
        else
            // Target machine below RECOMMENDED requirements?
            LoadString128(SETUP_RECOMMENDED_HDW, szBuf);
            Sprintf(szInfo, szBuf, N2000);
            ListAddString(listInfo, szInfo, AFTER);
            LoadString128(SETUP_RECOMMENDED_HDW, szBuf);
            LoadString128(SETUP_MIN_CPU, szBuf);
            Sprintf( szInfo, szBuf, HDW_30_REC_CPU_STR,
                                    HDW_30_REC_MEM_MB);
            ListAddString(listInfo, szInfo, AFTER);
            if ( CDROM_INSTALL ) then
                LoadString128(SETUP_MIN_RAM_WITH_MAP, szBuf);
                Sprintf(szInfo, szBuf, HDW_30_REC_MEM_MB);
                ListAddString(listInfo, szInfo, AFTER);
            endif;
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_TARGET_CPU_RAM, szBuf);
            Sprintf(szInfo, szBuf, SysInfo.szCpu, SysInfo.lSystemMemoryMb);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            if ( CDROM_INSTALL ) then
                if ( SysInfo.lSystemMemoryMb < HDW_30_MIN_MEM_WITH_MAP_MB ) then
                    LoadString128(SETUP_NOT_ENOUGH_RAM_FOR_MAP, szInfo);
                    ListAddString(listInfo, szInfo, AFTER);
                    LoadString128(SETUP_CONTINUE, szInfo);
                    ListAddString(listInfo, szInfo, AFTER);
                    bWarning = TRUE;
                endif;
            endif;
            if ( (SysInfo.lSystemMemoryMb >= HDW_30_MIN_MEM_BASE_APP_MB &&
                  SysInfo.lSystemMemoryMb < HDW_30_MIN_MEM_WITH_MAP_MB )
                 ||
                 (SysInfo.lCpu < HDW_30_REC_CPU )
               ) then
                bWarning = TRUE;
            endif;
            if (bWarning) then
                LoadString128(SETUP_PERFORMANCE_WARNING, szBuf);
                Sprintf(szInfo, szBuf, N2000);
                ListAddString(listInfo, szInfo, AFTER);
            endif;
        endif;
        ListAddString(listInfo, " ", AFTER);
        LoadString128(SETUP_CLICK_CANCEL_TO_EXIT, szInfo);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_CLICK_NEXT_TO_CONTINUE, szInfo);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, " ", AFTER);
        Disable (BACKBUTTON);
        Enable  (NEXTBUTTON);
        Enable  (CANCELBUTTON);
        // Display the dialog
        LoadString128(SETUP_MIN_REQ_DLG_TITLE, szBuf);
        Sprintf(szTitle, szBuf, N2000);
                LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
        lResult = SdShowInfoList (szTitle, szMsg, listInfo);
        ListDestroy( listInfo );
        if (lResult = CANCEL) then
          ExitHandler();
        endif;
        return lResult;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  DisplayWelcomeDialog
 *
 *  Purpose:  Display operating and legal threats
 *
 *    Input:
 *
 *   Output:
 *
 *  Returns:  sdWelcome return value  - may be CANCEL
 *
 * Comments:  You cannot put more than 512 bytes of text in szMsg or
 *            very strange things will happen.
\*---------------------------------------------------------------------------*/
function DisplayWelcomeDialog()
BOOL bDone;
LONG lCmdValue;
STRING szPath, szDialog, szFn, szMsg, szMsg1, szMsg2;
begin
        szFn = "DisplayWelcomeDialog()";
        WriteLogMsg( " ");
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        szDialog = "WelcomeDialog";
        szPath = SUPPORTDIR ^ ISUSER;    // Unique session permuted DLL name
        if ( EzDefineDialog(szDialog, szPath, "", GA_WELCOME_DLG + LANG) != 0)
        then
            LoadString128(SETUP_ERROR, szMsg1);
            LoadString128(SETUP_UNABLE_TO_LOAD_WELCOME_DLG, szMsg2);
            szMsg = szMsg1 + szMsg2;
            SprintfBox( SEVERE, szFn, szMsg );
            return (ENUM_ERR_OPENING_DLL);
        endif;
        bDone = FALSE;
        while (bDone = FALSE)
            lCmdValue = WaitOnDialog(szDialog);
            switch(lCmdValue)
            case DLG_INIT:
                // Set the Product+Version Number static text field
                LoadString128(SETUP_WELCOME_TEXT1, szMsg1);
                // Replace the %s with the App Name + Version No
                Sprintf(szMsg, szMsg1, ITEM_APP);
                CtrlSetText(szDialog, SETUP_WELCOME_ID1 + LANG, szMsg);
            case OK:
                bDone = TRUE;
            case BACK:
                bDone = TRUE;
            case GA_PBUT_EXIT_SETUP:
                ExitHandler();
            case DLG_CLOSE:
                bDone = TRUE;
            case DLG_ERR:
                LoadString128(SETUP_ERROR, szMsg1);
                LoadString128(SETUP_INTERNAL_DIALOG_BOX_ERROR, szMsg2);
                szMsg = szMsg1 + szMsg2;
                SprintfBox( SEVERE, szFn, szMsg);
                bDone = TRUE;
            endswitch;
        endwhile;
        EndDialog(szDialog);
        ReleaseDialog(szDialog);
        return lCmdValue;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CaptureNewUpdateOption
 *
 *  Purpose:  This function will capture from the User
 *            whether he wants a New or Update Installation
 *
 *    Input:  bAppSelected  (if backtracking)
 *
 *   Output:  bAppSelected
 *
 *  Returns:  bNewInstall
 *
 * Comments:  Until the toads at InstallShield let us get at bitmaps in
 *            _ISUSER.DLL and not just _ISRES.DLL, this function relies
 *            on external bitmaps being present in the SUPPORTDIR.
 *            As such it is tightly coupled to BUILDC/D.BAT which
 *            must compress the bitmaps into _SETUP.LIB.
\*---------------------------------------------------------------------------*/
function CaptureNewUpdateOption()
STRING szMsg, szTitle, szTmp, szTmp1, szBuf;
LIST listButtons, listDescrip;
LONG lResult;
begin
        LoadString128(SETUP_TYPE, szTitle);
        LoadString128(SETUP_SELECT_TYPE, szMsg);
        // Disable the BACK-NEXT buttons, since we do not want to use them.
        // The dialog terminates after selection.
        Disable( BACKBUTTON );
        Disable( NEXTBUTTON );
        listButtons = ListCreate( STRINGLIST );
        listDescrip = ListCreate( STRINGLIST );
        // set button list
        // NOTE! Undocumented  format - see gotcha #38
        szTmp = SUPPORTDIR ^ NEWINST_DOT_BMP;
        Sprintf(szBuf, "@%s;0;255,0,255", szTmp);
        ListAddString( listButtons, szBuf, AFTER );
        // NOTE! Undocumented  format - see gotcha #38
        szTmp = SUPPORTDIR ^ UPDINST_DOT_BMP;
        Sprintf(szBuf, "@%s;0;255,0,255", szTmp);
        ListAddString( listButtons, szBuf, AFTER );
        // set description list
        LoadString128(SETUP_NEW_INSTALL, szTmp);
        LoadString128(SETUP_NEW_INSTALL_VER, szBuf);
        Sprintf(szTmp1, szBuf, ITEM_APP);
        Sprintf(szBuf,"%s\n%s", szTmp, szTmp1);
        ListAddString( listDescrip, szBuf, AFTER );
        LoadString128(SETUP_UPDATE_INSTALL, szTmp);
        LoadString128(SETUP_UPDATE_INSTALL_VER, szBuf);
        Sprintf(szTmp1, szBuf, ITEM_APP);
        Sprintf(szBuf,"%s\n%s", szTmp, szTmp1);
        ListAddString( listDescrip, szBuf, AFTER );
        lResult = SdOptionsButtons( szTitle, szMsg, listButtons, listDescrip );
        switch(lResult)
          case 101:
                     bNewInstall=TRUE;
                     bAppSelected = TRUE;
          case 102:
                     bNewInstall=FALSE;
        endswitch;
        ListDestroy( listButtons );
        ListDestroy( listDescrip );
        Enable( NEXTBUTTON );
        Enable( BACKBUTTON );
        return bNewInstall;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  AddComponentsToList ( szComponentList)
 *
 *  Purpose:  This function will:
 *            - place all the separately installable file components
 *              into a component list.
 *            - set the size the files in the component.
 *            - All components are initialized as not selected
 *              except for the App component which is pre-selected
 *              for a New install.
 *
 *            The 'Map' option is ONLY offered for a CDROM install.
 *            The diskette install does not offer the 'map' option.
 *
 *    Input:  szComponentList;             // App installable components list
 *            bNewInstall
 *            bDontShowMapComponents       // Installed RAM < required for Map
 *            bEnuffRAMForMap;             // Set if >= minimum required RAM
 *   Output:
 *
 *  Returns:
 *
 * Comments:  The data.z layout for compressed Diskette installation is
 *
 *            DATA.Z File
 *             :.....N2000        ; contains all arcxx_nn.dll flavors
 *             :                              & speaker.drv
 *             :                              n2000.def (default.ini)
 *             :.....CB           ; contains cbul.386
 *             :.....PDIS08
 *
 *            For un-compressed CDROM installation the layout is
 *
 *            DATA.Z File
 *             :.....N2000        ; contains all arcxx_nn.dll flavors
 *             :                              & speaker.drv
 *             :                              n2000.def (default.ini)
 *             :.....MAPDATA
 *             :.....NORTHA
 *             :.....SOUTHA
 *             :.....EUROPE
 *             :.....SASIA
 *             :
 *             :.....CB           ; contains cbul.386
 *             :.....PDIS08
 *
 *   NOTE!:   This module is strongly coupled with BUILDx.BAT.
 *            The code here MUST line up with BUILDx.BAT icomp statements
 *
\*---------------------------------------------------------------------------*/
function AddComponentsToList( szComponentList )
string szLibrary, szSearchFile, szBuf;
LONG  lTmp1, lTmp2, nResult;
BOOL  bFlag;
begin
        if ( bNewInstall = TRUE ) then
            bFlag = TRUE;
        else
            bFlag = bAppSelected;
        endif;
        if (CDROM_INSTALL = TRUE) then
            // Application component
            lTmp1 = DetermineUnCmpComponentSize(SRCDIR ^ N2000, ASK_DOT_ASK);
            // The .VIX file is generic overhead no matter how many regions
            lTmp2 = DetermineUnCmpComponentSize(SRCDIR ^ N2000 ^ MDV_MAPDATA,
                                                                ASK_DOT_ASK);
            ComponentAddItem( szComponentList, ITEM_APP, lTmp1 +lTmp2, bFlag);
            // Only show Map components if the target machine has
            // enough RAM to support Map.
            if ( bEnuffRAMForMap ) then
                lTmp1 = DetermineUnCmpComponentSize(SRCDIR ^ MDV_EUROPE,
                                                    ASK_DOT_ASK);
                LoadString128(SETUP_MAP_EUROPE, szBuf);
                ComponentAddItem( szComponentList, szBuf, lTmp1, FALSE);
                lTmp1 = DetermineUnCmpComponentSize(SRCDIR ^ MDV_NORTHA,
                                                    ASK_DOT_ASK);
                LoadString128(SETUP_MAP_NORTHA, szBuf);
                ComponentAddItem( szComponentList, szBuf, lTmp1, FALSE);
                lTmp1 = DetermineUnCmpComponentSize(SRCDIR ^ MDV_SOUTHA,
                                                    ASK_DOT_ASK);
                LoadString128(SETUP_MAP_SOUTHA, szBuf);
                ComponentAddItem( szComponentList, szBuf, lTmp1, FALSE);
                lTmp1 = DetermineUnCmpComponentSize(SRCDIR ^ MDV_SASIA,
                                                    ASK_DOT_ASK);
                LoadString128(SETUP_MAP_SASIA, szBuf);
                ComponentAddItem( szComponentList, szBuf, lTmp1, FALSE);
                LoadString128(SETUP_MAP_ALL_FROM_CDROM, szBuf);
                ComponentAddItem( szComponentList, szBuf, 0, FALSE);
            endif;
            // Relay Contacts is here for Windows NT with release 2.6!
            lTmp1 = DetermineUnCmpComponentSize(SRCDIR ^ CBDIR, ASK_DOT_ASK);
            lTmp2 = DetermineUnCmpComponentSize(SRCDIR ^ PDIS08DIR,
                                                ASK_DOT_ASK);
            LoadString128(SETUP_ARC, szBuf);
            ComponentAddItem( szComponentList, szBuf, lTmp1+lTmp2, FALSE);
        else
            szLibrary = DATA_DOT_1;
            // Application component
            lTmp1 = DetermineCmpComponentSize(szLibrary,
                                              N2000 + "\\" + ASK_DOT_ASK);
            ComponentAddItem( szComponentList, ITEM_APP, lTmp1, bFlag);
            // Relay Contacts is here for Windows NT with Release 2.6
            lTmp1 = DetermineCmpComponentSize(szLibrary,
                                              CBDIR +"\\"+ ASK_DOT_ASK);
            lTmp2 = DetermineCmpComponentSize(szLibrary,
                                              PDIS08DIR +"\\"+ ASK_DOT_ASK);
            LoadString128(SETUP_ARC, szBuf);
            ComponentAddItem( szComponentList, szBuf, lTmp1+lTmp2, FALSE);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  DetermineUnCmpComponentSize (szDir, szFile )
 *
 *  Purpose:  This function will return the size of the directory
 *            specified. - It does NOT recurse downwards!
 *
 *    Input:  szDir
 *            szFile
 *
 *  Returns:  lSize
 *
 *  Assumes:  UnCompressed directory
 *
\*---------------------------------------------------------------------------*/
function DetermineUnCmpComponentSize( szDir, szFile )
LONG  lSize, lReturn, lResult;
STRING szFileName, szResult;
begin
        lSize = 0;
        // If directory is empty, don't continue (see below)
        lReturn = FindFile (szDir, szFile, szFileName);
        if (lReturn = 0) then
           lReturn = FindAllFiles( szDir, szFile, szFileName, RESET);
           if (lReturn != 0) then
              return -1;
           endif;
        else
           //The top level directory was empty
           return -1;
        endif;
        while (lReturn = 0 )
           GetFileInfo (szFileName, FILE_SIZE, lResult, szResult);
           lSize = lSize + lResult;
           lReturn = FindAllFiles ( szDir, szFile, szFileName, CONTINUE);
        endwhile;
        return lSize;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  DetermineCmpComponentSize (szLibrary, szSearchFile )
 *
 *  Purpose:  This function will return the size of the library
 *            component specified.
 *
 *    Input:  szLibrary
 *            szSearchFile
 *
 *  Returns:  lSize
 *
 *  Assumes:  Compressed library
 *
\*---------------------------------------------------------------------------*/
function DetermineCmpComponentSize( szLibrary, szSearchFile )
LONG  lSize, lResult, lErr;
string szSize, szError, szErrCode, szErrMsg;
begin
        lResult = CompressInfo( szLibrary, szSearchFile,
                                COMP_INFO_ORIGSIZE | COMP_NORMAL,
                                lSize, szSize );
        switch ( lResult )
            case 0 :
                     return lSize;
            case COMP_ERR_INCOMPATIBLE:   //-21
                     lErr = SETUP_ERR_LIB_FILE_NOT_COMP;
            case COMP_ERR_MEMORY:         //-6
                     lErr = SETUP_ERR_CANT_ALLOC_MEMORY;
            case COMP_ERR_OPENINPUT:      //-2
                     lErr = SETUP_ERR_CANT_FIND_LIBFILE;
            case COMP_ERR_OPTIONS:        //-25
                     lErr = SETUP_ERR_NOPTIONS_INVALID;
            default:
                     lErr = SETUP_ERR_UNKNOWN;
            // Note: -1  if you use "data.z" instead of "data.1" on a split lib
            // Note: -42 if the library does not contain the path specified
            //           in szSearchFile
        endswitch;
        NumToStr( szErrCode, lResult);
        LoadString128(lErr, szErrMsg);
        // Note: If you get an Unknown Error -1 and szSearchFile=C2000\*.*
        //       check CDROM_INSTALL value!
        LoadString128(SETUP_ERROR, szError);
        SprintfBox( SEVERE, "DetermineComponentSize()",
                    "%s %s %s\n szLibrary=[%s] szSearchFile=[%s]",
                    szError, szErrCode, szErrMsg, szLibrary, szSearchFile);
        ExitHandler();
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CaptureComponentsAndDrive
 *
 *  Purpose:  This function will capture from the User
 *            a list of the components he wants to install, and a fully
 *            qualified path to the directory he wants to put the App in.
 *
 *            The user is offered N2000 on TARGETDISK as the
 *            default directory in which to install the application.
 *
 *            The user may change both the default drive and the application
 *            directory.
 *
 *            If he has selected 'Update' then the \n2000 application
 *            directory  must exist on the specified drive and be writable
 *            (Unless he's updating from a x:\netman to a c:\n2000).
 *
 *
 *    Input:  szAppPath [ MAX_SIZE ];      // Fully qualified path to app
 *            szComponentList;             // App installable components list
 *
 *   Output:  szAppPath [ MAX_SIZE ];
 *            szComponentList;             // App installable components list
 *            bMapSelected
 *            bMapREGIONSelected flags
 *            lSetDefaultsForMap;          // ENUM_region
 *
 *  Returns:
 *
 * Comments:
 *            If bRunMapsFromCdrom is set, Map data will NOT be copied from the
 *            Cdrom to the user's disk, but the n2000.ini
 *
 *            [Datafiles]
 *            VoronoiTable=
 *            IndexFile=
 *            DataFile0=
 *
 *            parms will be set to point at the Cdrom.
 *
\*---------------------------------------------------------------------------*/
function CaptureComponentsAndDrive()
STRING szMsg, szTitle, szTmp1, szTmp2;
LONG lTmp, lReturn;
begin
        LoadString128(SETUP_SELECT_COMPONENTS, szTitle);
        LoadString128(SETUP_SELECT_COMPONENT_MSG1, szTmp1);
        LoadString128(SETUP_SELECT_COMPONENT_MSG2, szTmp2);
        szMsg = szTmp1 + " " + szTmp2;
        // Display required file sizes in even MB units
        DialogSetInfo( DLG_INFO_KUNITS, "", FALSE);
        DialogSetInfo( DLG_INFO_USEDECIMAL, "", FALSE);
        // Display the component selection dialog
        Enable( BACKBUTTON );
        Enable( NEXTBUTTON );
        lReturn = SdComponentDialog(szTitle, szMsg, szAppPath, szComponentList);
        // Set flags depending on selection
        bAppSelected = ComponentIsItemSelected(szComponentList, ITEM_APP);
        LoadString128(SETUP_ARC, szMsg);
        bAlarmRelayContactsSelected = ComponentIsItemSelected(szComponentList,
                                                              szMsg);
        LoadString128(SETUP_MAP_EUROPE, szBuf);
        bMapEuropeSelected = ComponentIsItemSelected(szComponentList, szBuf);
        LoadString128(SETUP_MAP_NORTHA, szBuf);
        bMapNorthaSelected = ComponentIsItemSelected(szComponentList, szBuf);
        LoadString128(SETUP_MAP_SOUTHA, szBuf);
        bMapSouthaSelected = ComponentIsItemSelected(szComponentList, szBuf);
        LoadString128(SETUP_MAP_SASIA, szBuf);
        bMapSasiaSelected  = ComponentIsItemSelected(szComponentList, szBuf);
        LoadString128(SETUP_MAP_ALL_FROM_CDROM, szBuf);
        bRunMapsFromCdrom = FALSE;
        bRunMapsFromCdrom =  ComponentIsItemSelected(szComponentList, szBuf);
        if ( bRunMapsFromCdrom = TRUE) then
            bMapEuropeSelected = TRUE;
            bMapNorthaSelected = TRUE;
            bMapSouthaSelected = TRUE;
            bMapSasiaSelected  = TRUE;
        endif;
        if (bMapEuropeSelected ||
            bMapNorthaSelected ||
            bMapSouthaSelected ||
            bMapSasiaSelected ) then
            bMapSelected = TRUE;
        else
            bMapSelected = FALSE;
        endif;
        lTmp = bMapEuropeSelected + bMapNorthaSelected +
               bMapSouthaSelected + bMapSasiaSelected;
        if (lTmp > 1) then
            // Multiple maps selected.
            bMapsSelected = TRUE;
            // Let the user decide which one to set in SelectMapOptions
            lSetDefaultsForMap = 0;
        else
            // Single Map selected
            bMapsSelected = FALSE;
            // Set the map to use
            if (bMapEuropeSelected) then
                lSetDefaultsForMap = ENUM_EUROPE;
            endif;
            if (bMapNorthaSelected) then
                lSetDefaultsForMap = ENUM_NORTHA;
            endif;
            if (bMapSouthaSelected) then
                lSetDefaultsForMap = ENUM_SOUTHA;
            endif;
            if (bMapSasiaSelected) then
                lSetDefaultsForMap = ENUM_SASIA;
            endif;
        endif;
        return (lReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CreateDirectory( szDirectory, szFn)
 *
 *  Purpose:  This function will create the specified directory and
 *            if the log is open at this point, log failure to do so.
 *
 *    Input:  szDirPath                    // Fully qualified directory path
 *            szFn                         // Calling Function Id
 *
 *   Output:
 *
 *  Returns:  TRUE                         // Directory created or exists.
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function CreateDirectory( szDirPath, szFn )
BOOL bResult;
begin
        bResult = TRUE;
        if ( ExistsDir (szDirPath) != EXISTS ) then
           CreateDir( szDirPath );
           if ( LAST_RESULT != 0 ) then
               MessageBeep(0);
               LoadString128(SETUP_ERR_CREATE_DIR, szBuf);
               Sprintf(szBuf1, szBuf, szDirPath);
               LoadString128(SETUP_ERROR, szBuf);
               LoadString128(SETUP_CHECK_WRITE_ACCESS, szTmp);
               SprintfBox( SEVERE, szFn, "%s%s\n%s", szBuf, szBuf1, szTmp);
               if (bLogFile) then
                   WriteLogMsg( szFn + szBuf1);
               endif;
               bResult = FALSE;
           endif;
        endif;
        return (bResult);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  LocateOldNetmanDirectory (BYREF STRING)
 *
 *  Purpose:  Determine the fully qualified path to the old netman directory.
 *
 *    Input:  szOldAppPath                 // Fully qualified directory path
 *   Output:  szOldAppPath
 *
 *  Returns:  TRUE                         // Valid path found to old netman
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function LocateOldNetmanDirectory( szOldAppPath )
LIST listDrives;
begin
        // Search fixed drives first
        listDrives = ListCreate( STRINGLIST );
        if ( GetValidDrivesList(listDrives, FIXED_DRIVE, -1) = 0 ) then
            if ( SearchDriveList(listDrives, szOldAppPath) = TRUE ) then
                return TRUE;
            endif;
        endif;
        ListDestroy(listDrives);
        // Search Remote drives next
        listDrives = ListCreate( STRINGLIST );
        if ( GetValidDrivesList(listDrives, REMOTE_DRIVE, -1) = 0 ) then
            if ( SearchDriveList(listDrives, szOldAppPath) = TRUE ) then
                return TRUE;
            endif;
        endif;
        ListDestroy(listDrives);
        return FALSE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SearchDriveList (listDrives, szOldAppPath)
 *
 *  Purpose:  Check each drive in the list for a \NETMAN directory
 *            off the root of the drive.
 *
 *    Input:  szOldAppPath                 // Fully qualified directory path
 *   Output:  szOldAppPath
 *
 *  Returns:  TRUE                         // Valid path found to old netman
 *            FALSE                        // NETMAN dir not on any drive.
 * Comments:
\*---------------------------------------------------------------------------*/
function SearchDriveList( listDrives, szOldAppPath )
STRING szDrive, szPath, szWork;
LONG lResult;
begin
        szOldAppPath = "";
        lResult = ListGetFirstString( listDrives, szDrive );
        while (lResult = 0)
           szWork = szDrive + ":" ^ NETMAN;
           if ( ExistsDir (szWork) = EXISTS ) then
               // May have found a 'NETMAN' directory but does it
               // have old (pre 2.0) Database files in it?
               if (  Is( FILE_EXISTS, szWork ^ "SITES.DBF" ) &&
                     Is( FILE_EXISTS, szWork ^ NETMAN + DOT_EXE ) ) then
                   szOldAppPath = szWork;
                   return TRUE;
               endif;
           endif;
           lResult = ListGetNextString( listDrives, szDrive );
        endwhile;
        return FALSE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CheckUserComponentSelections
 *
 *  Purpose:  This function will validate the choices made by the
 *            user in CaptureComponentsAndDrive()
 *
 *
 *    Input:  szAppPath [ MAX_SIZE ];      // Fully qualified path to app
 *            szComponentList;             // App installable components list
 *
 *   Output:  szOldAppPath [MAX_SIZE];     // Non-empty if we have to transfer
 *                                            his old DB files to the new dir.
 *            bCopyOldDBFiles              // TRUE if we have to transfer DBF
 *            bExistingApp                 // DBfix pacifier.
 *
 *  Returns:  REDO                         // To CaptureComponentsAndDrive()
 *            BACK                         // To CaptureNewUpdateOption()
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function CheckUserComponentSelections()
STRING szTitle, szConfirmTitle, szMsg, szMsg1, szDir, szReason, szAction;
LONG lReturn;
begin
        // [NEXT] checks!
        LoadString128(SETUP_COMPONENT_SELECTION, szTitle);
        bExistingApp = FALSE;
        szOldAppPath = "";
        bCopyOldDBFiles = FALSE;
        if (bNewInstall) then
            // NEW checks
            // Application component must be selected
            if (bAppSelected = FALSE) then
                LoadString128(SETUP_YOU_MUST_SELECT_APP, szMsg1);
                Sprintf(szMsg, szMsg1, ITEM_APP);
                SprintfBox( WARNING, szTitle, szMsg);
                return (REDO);
            endif;
            if ( ExistsDir( szAppPath ) = EXISTS ) then
                if ( Is( DIR_WRITEABLE, szAppPath) = FALSE) then
                    LoadString128(SETUP_DIR_NOT_WRITABLE, szReason);
                    WarnDirectoryError(szAppPath,
                                       SETUP_NEW_INSTALL_TO_DIR,
                                       szReason);
                    return (REDO);
                endif;
                //    If the directory has a netman.exe or n2000.exe in it,
                //    warn the user that he's trying to do a new install over
                //    an existing application directory , and make sure
                //    that he really wants to wipe the current .INI out,
                //    losing his current settings, and start all over with a
                //    new default .INI.
                //    If he's not, return BACK to send him back to the
                //    NEW/UPDATE dialog
                if ( Is( FILE_EXISTS, szAppPath ^ NETMAN + DOT_EXE) ||
                     Is( FILE_EXISTS, szAppPath ^ N2000  + DOT_EXE) ) then
                     if ( AskIfOkToDo( szAppPath,
                                      SETUP_NEW_INSTALL_TO_DIR,
                                      SETUP_N2000_DIR) ) = NO then
                        return (BACK);
                    endif;
                    bExistingApp = TRUE;
                endif;
            endif;
        else
            // UPDATE checks
            //  User must select at least ONE component.
            if (bAppSelected = FALSE &&
                bAlarmRelayContactsSelected = FALSE &&
                bMapSelected = FALSE) then
                LoadString128(SETUP_PLEASE_SELECT_UPDATE, szMsg);
                SprintfBox( WARNING, szTitle, szMsg);
                return (REDO);
            endif;
            if ( ExistsDir( szAppPath ) = EXISTS ) then
                // The Application directory exists.
                if ( Is( DIR_WRITEABLE, szAppPath) = FALSE) then
                    LoadString128(SETUP_DIR_NOT_WRITABLE, szReason);
                    WarnDirectoryError(szAppPath,
                                       SETUP_UPD_INSTALL_TO_DIR,
                                       szReason);
                    return (REDO);
                endif;
                // Has it got N2000 ie post Release 2.0 files in it?
                if ( Is( FILE_EXISTS, szAppPath ^ N2000  + DOT_EXE) ) then
                    bExistingApp = TRUE;
                endif;
            endif;
            if (bExistingApp = FALSE ) then
                // We're updating from a previous Version 2.0 or before
                // where the old app was located in \netman.
                // OR
                // We're updating from a 2.6.1 version where the old
                // app was located in x:\something\N2000!
                // Start by trying to locate his old \netman directory
                // on the assumption that he used the default \netman name.
                LocateOldNetmanDirectory (szOldAppPath);
                // Ask him to confirm the old directory if we found it
                // or supply the path if he called it something different
                // or located it off the root of a drive.
                LoadString128(SETUP_CONFIRMATION_REQUESTED, szConfirmTitle);
                LoadString128(SETUP_CONFIRM_NETMAN_DIR, szMsg1);
                Sprintf(szMsg, szMsg1, NETMAN, N2000);
                szDir = szOldAppPath;
                // Check the path to the old netman directory with the user.
                SelectDir(szConfirmTitle, szMsg, szDir, FALSE);
                if ( ExistsDir( szDir ) != EXISTS ) then
                    LoadString128(SETUP_DIR_DOES_NOT_EXIST, szReason);
                    WarnDirectoryError(szDir,
                                       SETUP_SPECIFIED_NETMAN_DIR,
                                       szReason);
                    return (REDO);
                endif;
                if ( Is( FILE_EXISTS,  szDir ^ "SITES.DBF") &&
                     Is( FILE_EXISTS,  szDir ^ NETMAN + DOT_EXE) ) then
                    bCopyOldDBFiles = TRUE;
                    bExistingApp = TRUE;
                    szOldAppPath = szDir;
                else
                    if ( Is( FILE_EXISTS,  szDir ^ "SITES.DBF") &&
                        Is( FILE_EXISTS,  szDir ^ N2000 + DOT_EXE) ) then
                        bCopyOldDBFiles = FALSE;
                        bExistingApp = TRUE;
                        szAppPath = szDir;
                        szOldAppPath = "";
                    else
                        LoadString128(SETUP_NON_N2000_DIR, szMsg);
                        Sprintf(szReason, szMsg, N2000);
                        WarnDirectoryError(szDir,
                                           SETUP_SPECIFIED_NETMAN_DIR,
                                           szReason);
                        return (BACK);
                    endif;
                endif;
            endif;
        endif;
        // Always check the apps are valid
        szDir = "";
        if ( Is(VALID_PATH, szAppPath) = FALSE ) then
            szDir = szAppPath;
        endif;
         if ( szOldAppPath != "") then
            if ( Is(VALID_PATH, szOldAppPath) = FALSE ) then
                szDir = szOldAppPath;
            endif;
        endif;
        if (szDir != "") then
            LoadString128(SETUP_INVALID_PATH, szMsg);
            LoadString128(SETUP_CHECK_ILLEGAL_CHARS, szMsg1);
            SprintfBox( SEVERE, szTitle, szMsg + "\n" + szDir, "\n\n" + szMsg1);
            return (REDO);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  AskIfOkToDo ( szDir, lMsg1Id, lMsg2Id )
 *
 *  Purpose:  Display a message box of the format:
 *            'You have asked Setup to do a new install to directory %s'
 *            'This directory [contains existing/does not contain] n2000 files"
 *            'Is this what you want to do?'
 *            Service Routine to CheckUserComponentSelections()
 *    Input:  szDir
 *            lMsg1Id
 *            lMsg2Id
 *
 *  Returns:  YES or NO
 *
 * Comments:  Assumes that the resource strings:
 *            1. Contains a %s to be replaced with the supplied directory path
 *            2. Contains a %s to be replaced with N2000.
\*---------------------------------------------------------------------------*/
function AskIfOkToDo( szDir, lMsg1Id, lMsg2Id )
STRING szMsg, szTmp, szExpand, szWant;
LONG lResult;
begin
         LoadString128(lMsg1Id, szExpand);
         Sprintf(szMsg, szExpand, szDir);
         if (lMsg2Id) then
             LoadString128(lMsg2Id, szExpand);
             Sprintf(szTmp, szExpand, N2000);
         else
             szTmp = "";
         endif;
         LoadString128(SETUP_IS_THIS_WHAT_U_WANT, szWant);
         SetDialogTitle(DLG_ASK_YESNO, "Confirm selection");
         lResult = AskYesNo( szMsg + "\n" + szTmp + "\n\n" + szWant, YES);
         return lResult;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WarnDirectoryError( szDir, szExpand, szReason)
 *
 *  Purpose:  Display a error message box in the format:
 *            'You asked Setup to do an [New/Update] install to directory %s'
 *            'directory path'
 *            'This directory [is not writable] or
 *                            [does not exist] or
 *                            [does not contain n2000 files]'
 *            'Please check the destination directory and drive."
 *            Service Routine to CheckUserComponentSelections()
 *    Input:  szDir
 *            lReasonId
 *  Returns:
 *
 * Comments:  Assumes that the input resource ID string contains a
 *            %s to be replaced with the input Directory name.
\*---------------------------------------------------------------------------*/
function WarnDirectoryError( szDir, lIdExpand, szReason)
STRING szTitle, szMsg, szExpand, szAction;
LONG lTmp;
begin
         LoadString128(SETUP_ERROR, szTitle);
         LoadString128(lIdExpand, szExpand);
         Sprintf(szMsg, szExpand, szDir);
         LoadString128(SETUP_CHK_DIR_DR, szAction);
         SprintfBox( SEVERE, szTitle,
                             szMsg + "\n" + szReason + "\n\n" + szAction);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  EnoughSpace
 *
 *  Purpose:  This function will determine if enough space exists on
 *            the target drive based on the selections in the component
 *            list.
 *
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function  EnoughSpace( szAppPath, szComponentList, lExtraSpace )
LIST    listInfo;
LONG    lTotal, lSize, lFreeSpace, lResult;
STRING  szMsg, szTitle, svComponent, szTmp1, szTmp2;
begin
        // Get total size of all selected components.
        listInfo   = ListCreate( STRINGLIST );
        lTotal = lExtraSpace;
        ComponentListItems( szComponentList, listInfo );
        lResult = ListGetFirstString( listInfo, svComponent );
        while (lResult = 0)
           ComponentGetItemSize( szComponentList, svComponent, lSize );
           if (ComponentIsItemSelected( szComponentList, svComponent )) then
              lTotal = lTotal + lSize;
           endif;
           lResult = ListGetNextString( listInfo, svComponent );
        endwhile;
        ListDestroy( listInfo );
        // Determine if target disk has enough space.
        lFreeSpace = GetDiskSpace( szAppPath );
        if (lFreeSpace < lTotal) then
           LoadString128(SETUP_DISK_SPACE, szTitle);
           LoadString128(SETUP_DISK_SPACE_MSG1, szTmp1);
           LoadString128(SETUP_DISK_SPACE_MSG2, szTmp2);
           szMsg = szTmp1 + "\n" + szTmp2;
           SprintfBox( WARNING, szTitle, szMsg, lTotal, szAppPath );
           return FALSE;
        endif;
        return TRUE; // Enough space.
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SelectMapOptions()
 *
 *  Purpose:  If the user has selected multiple map components,
 *            this function will offer radiobutton choices to
 *            the user of all the map components he selected
 *            so he can elect which one to start up the application
 *            with.
 *
 *            If not, it just returns displaying nothing.
 *
 *    Input:  bMapsSelected
 *
 *   Output:  lSetDefaultsForMap (global)    // ENUM_region
 *
 *  Returns:  Back Possibly
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function SelectMapOptions()
LONG lReturn;
BOOL bSelect;
STRING szTitle, szMsg1, szMsg2, szComponents, szId, szBuf;
begin
        if (DEBUG) then
           if (bMapsSelected = FALSE) then
               SprintfBox( SEVERE, "SelectMapOptions()",
               "ASSERT: bMapsSelected=[%d] should be TRUE", bMapsSelected);
           endif;
        endif;
        LoadString128(SETUP_SHOW_WHICH_MAP, szTitle);
        LoadString128(SETUP_MAP_OPTION_MSG, szBuf);
        Sprintf(szMsg1, szBuf, ITEM_APP);
        szMsg2 = " ";
        szId = "";
        szComponents = szTitle;
        // Arrange for the first radiobutton to be highlighted.
        bSelect = TRUE;
        if (bRunMapsFromCdrom) then
            // Setup to choose active map from all the maps
            LoadString128(SETUP_MAP_EUROPE, szBuf);
            ComponentAddItem(szComponents, szBuf, 1, bSelect);
            bSelect = FALSE;
            LoadString128(SETUP_MAP_NORTHA, szBuf);
            ComponentAddItem(szComponents, szBuf, 1, bSelect);
            bSelect = FALSE;
            LoadString128(SETUP_MAP_SOUTHA, szBuf);
            ComponentAddItem(szComponents, szBuf, 1, bSelect);
            bSelect = FALSE;
            LoadString128(SETUP_MAP_SASIA, szBuf);
            ComponentAddItem(szComponents, szBuf, 1, bSelect);
            bSelect = FALSE;
        else
            // Setup to choose from the maps the user selected.
            LoadString128(SETUP_MAP_EUROPE, szBuf);
            if (ComponentIsItemSelected(szComponentList, szBuf)) then
                ComponentAddItem(szComponents, szBuf, 1, bSelect);
                bSelect = FALSE;
            endif;
            LoadString128(SETUP_MAP_NORTHA, szBuf);
            if (ComponentIsItemSelected(szComponentList, szBuf)) then
                ComponentAddItem(szComponents, szBuf, 1, bSelect);
                bSelect = FALSE;
            endif;
           LoadString128(SETUP_MAP_SOUTHA, szBuf);
           if (ComponentIsItemSelected(szComponentList, szBuf)) then
                ComponentAddItem(szComponents, szBuf, 1, bSelect);
                bSelect = FALSE;
            endif;
            LoadString128(SETUP_MAP_SASIA, szBuf);
            if (ComponentIsItemSelected(szComponentList, szBuf)) then
                ComponentAddItem(szComponents, szBuf,  1, bSelect);
                bSelect = FALSE;
            endif;
        endif;
        // Display the dialog
        lReturn = SdAskOptions(szTitle, szMsg1, szMsg2, szId, szComponents,
                               EXCLUSIVE);
        // Get selection, Set lSetDataFilesToMap
        lSetDefaultsForMap = 0;
        LoadString128(SETUP_MAP_EUROPE, szBuf);
        if ( ComponentIsItemSelected (szComponents, szBuf) ) then
            lSetDefaultsForMap = ENUM_EUROPE;
        endif;
        LoadString128(SETUP_MAP_NORTHA, szBuf);
        if ( ComponentIsItemSelected (szComponents, szBuf) ) then
            lSetDefaultsForMap = ENUM_NORTHA;
        endif;
        LoadString128(SETUP_MAP_SOUTHA, szBuf);
        if ( ComponentIsItemSelected (szComponents, szBuf) ) then
            lSetDefaultsForMap = ENUM_SOUTHA;
        endif;
        LoadString128(SETUP_MAP_SASIA, szBuf);
        if ( ComponentIsItemSelected (szComponents, szBuf) )  then
            lSetDefaultsForMap = ENUM_SASIA;
        endif;
        if (DEBUG) then
           if (lSetDefaultsForMap = 0 && lReturn != BACK) then
               SprintfBox( SEVERE, "SelectMapOptions()",
               "ASSERT: lSetDefaultsForMap=[%d] should not be zero",
                        lSetDefaultsForMap);
           endif;
        endif;
        return (lReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SelectAppOptions
 *
 *  Purpose:  This function will capture as many of these options from
 *            from the User as are required.
 *
 *            [ ] Enable 2010 and IP Alarm Stream Support
 *            [ ] Enable Alarm Forwarding to other OMC's
 *            [ ] Enable QT1000 Support
 *            [ ] Add N2000 to the Startup Group
 *            [ ] Automatically log you on whenever Windows starts up
 *            [ ] Install PC Speaker Driver to provide sound
 *            [ ] Show Map Scale in Km Units
 *
 *            Only options that are applicable will be displayed.
 *
 *    Input:  sysInfo
 *            bNewInstall
 *            bOfferAutoLogon
 *
 *  Returns:  BACK possibly
 *
 * Comments:  AskOptions can handle up to 15 checkboxes
 *
 *            The 'Show Map Scale in Km Units' option is
 *            offered even if the user has chosen no maps.
 *
\*---------------------------------------------------------------------------*/
function SelectAppOptions()
BOOL bRet;
LONG lReturn, lValue, lCount, lTmp;
STRING szWaveParm, szNonExclMessage, szText, szTmp, szTitle,
       szEnableIpSupport, szEnableQT1000Support, szAddAppToStartupGrp,
       szAutoLogon,  szInstallPCSoundDriver,
       szMapUnitsKm, szEnableAlarmForwarding;
begin
        // Initialize flags
        // Note! Every single user except Warren on which setup was tested
        // wanted AddAppToStartupGroup UNchecked like the rest.
        // The majority rule!
        bEnableQT1000Support        = FALSE;
        bAddAppToStartupGrp         = FALSE;
        bAutoLogon                  = FALSE;
        bInstallPCSoundDriver       = FALSE;
        bEnableIpSupport            = FALSE;
        bEnableAlarmForwarding      = FALSE;
        LoadString128(SETUP_ENABLE_IP,             szEnableIpSupport);
        LoadString128(SETUP_ENABLE_ALARM_FORWARDING, szEnableAlarmForwarding);
        LoadString128(SETUP_ENABLE_QT1000,         szEnableQT1000Support);
        LoadString128(SETUP_ADD_TO_STARTUP_GRP,    szAddAppToStartupGrp);
        LoadString128(SETUP_AUTO_LOGON,            szAutoLogon);
        LoadString128(SETUP_INSTALL_SPEAKERDRV,    szInstallPCSoundDriver);
        LoadString128(SETUP_MAP_UNITS_KM,          szMapUnitsKm);
        // If doing an update install and the IP Product Key is set
        // and Alarm Forwarding is active,
        // check the AlarmForwarding checkbox if:
        // [Programs] ForwardManager=forward.exe  entry exists and
        // [N2000 Config] enableForwarding=0      entry doesn't exist
        bRet = IsIpProductKeyInstalled();
        szText = szAppPath ^ CONS_CONFIG_FILE_NAME;
        // I33 BUGFIX - The parentheses around the first operand are required
        //              (refer gotcha #30)
        if ( (!bNewInstall) && bRet ) then
            if ( GetProfString(szText, CONS_STARTUP_SECTION,
                                  CONS_FORWARD_MANAGER, szTmp) = 0 ) then
                 if (szTmp = FORWARD_DOT_EXE ) then
                    // IS3BUGFIX : GetProfInt returns 0 (success) for a non-
                    //             existing szKeyName. Use getProfString()
                    //             instead. This does work and correctly
                    //             returns -1 if there's no ini entry.
                    if ( GetProfString(szText, CONS_NETCFG_SECTION,
                                   CONS_ENABLE_FORWARD, szTmp) < 0) then
                        if ( CONS_ENABLE_FORWARD_DEF) then
                            bEnableAlarmForwarding = TRUE;
                        endif;
                    else
                        StrToNum(lTmp, szTmp);
                        bEnableAlarmForwarding = lTmp;
                    endif;
                 endif;
            endif;
        endif;
        // Set the Map Units Km Checkbox
        // Start by assuming North America is the only non-metric region.
        if ( ( bMapNorthaSelected && !bMapsSelected ) ||
            lSetDefaultsForMap = ENUM_NORTHA ) then
            bMapUnitsKm             = FALSE;  // Default
        else
            bMapUnitsKm             = TRUE;   // Default
        endif;
        // If doing an update check the N2000 Ini for an explicit
        // [Map] MapUnitsMiles entry. Reset bMapUnitsKm if there is one.
        szText = szAppPath ^ CONS_CONFIG_FILE_NAME;
        if ( !bNewInstall ) then
            if ( GetProfString(szText, MAP_SECTION,
                               MAP_UNITS_MILES, szTmp) = 0 ) then
                StrToNum(lTmp, szTmp);
                if (lTmp) then
                    bMapUnitsKm = FALSE;
                else
                    bMapUnitsKm = TRUE;
                endif;
            endif;
        endif;
        // Setup the dialog title and user informational messages
        LoadString128(SETUP_OPTIONS, szBuf);
        SetDialogTitle(DLG_ASK_OPTIONS, ITEM_APP + " " + szBuf);
        LoadString128(SETUP_CHECKBOX_MSG1, szText);
        LoadString128(SETUP_CHECKBOX_MSG2, szTmp);
        szNonExclMessage = szText + " " + szTmp;
        // Display the dialog
        // - The Alarm Forwarding option is not offered under W95 or if
        //   TcPIp is not installed.
        // Optionally offer to install a pc sound driver if
        // - the system is a Win95 system and
        // - the user does not have a sound driver installed or
        //   has a wave=speaker.drv entry in system.ini but the
        //   speaker.drv file is missing from the windows system directory.
        // Note results of testing on wls's '95 machine:
        // - Deleted the PC sound driver registry entries,
        //   and checked  that there was alive wave=speaker.drv entry
        //   pointing to the old Win3.1 speaker.drv.
        // - Rebooted.
        // - Windows 95 re-creates the registry entries from the system.ini
        //   and N2000 does beep using the old 3.1 speaker driver, driven
        //   as before by the Win16 sub-system and using 100% cpu.
        szText = WINDIR ^ SYSTEM_DOT_INI;
        GetProfString(szText, "drivers", "Wave", szWaveParm);
        lValue = NONEXCLUSIVE;
        switch (SysInfo.lOS)
            case IS_WINDOWSNT:
                if ( IsTcpIpInstalled() ) then
                    if ( bOfferAutoLogon ) then
                        lReturn = AskOptions( lValue, szNonExclMessage,
                                             "&" + szEnableIpSupport,
                                                   bEnableIpSupport,
                                             "&" + szEnableAlarmForwarding,
                                                   bEnableAlarmForwarding,
                                             "&" + szEnableQT1000Support,
                                                   bEnableQT1000Support,
                                             "&" + szAddAppToStartupGrp,
                                                   bAddAppToStartupGrp,
                                             "&" + szAutoLogon,
                                                   bAutoLogon,
                                             "&" + szMapUnitsKm,
                                                   bMapUnitsKm);
                    else
                        lReturn = AskOptions( lValue, szNonExclMessage,
                                             "&" + szEnableIpSupport,
                                                   bEnableIpSupport,
                                             "&" + szEnableAlarmForwarding,
                                                   bEnableAlarmForwarding,
                                             "&" + szEnableQT1000Support,
                                                   bEnableQT1000Support,
                                             "&" + szAddAppToStartupGrp,
                                                   bAddAppToStartupGrp,
                                             "&" + szMapUnitsKm,
                                                   bMapUnitsKm);
                    endif;
                else
                    if ( bOfferAutoLogon ) then
                        lReturn = AskOptions( lValue, szNonExclMessage,
                                             "&" + szEnableIpSupport,
                                                   bEnableIpSupport,
                                             "&" + szEnableQT1000Support,
                                                   bEnableQT1000Support,
                                             "&" + szAddAppToStartupGrp,
                                                   bAddAppToStartupGrp,
                                             "&" + szAutoLogon,
                                                   bAutoLogon,
                                             "&" + szMapUnitsKm,
                                                   bMapUnitsKm);
                    else
                        lReturn = AskOptions( lValue, szNonExclMessage,
                                             "&" + szEnableIpSupport,
                                                   bEnableIpSupport,
                                             "&" + szEnableQT1000Support,
                                                   bEnableQT1000Support,
                                             "&" + szAddAppToStartupGrp,
                                                   bAddAppToStartupGrp,
                                             "&" + szMapUnitsKm,
                                                   bMapUnitsKm);
                    endif;
                endif;
            case IS_WINDOWS95:
                if (   ( szWaveParm = "" ||
                       ( StrFind(szWaveParm,SPEAKER_DOT_DRV) >= 0 &&
                         Is( FILE_EXISTS, WINSYSDIR ^ SPEAKER_DOT_DRV) = FALSE )
                       )
                   )  then
                    // Sound Driver is not installed
                    lReturn = AskOptions( lValue, szNonExclMessage,
                                         "&" + szEnableIpSupport,
                                               bEnableIpSupport,
                                         "&" + szEnableQT1000Support,
                                               bEnableQT1000Support,
                                         "&" + szAddAppToStartupGrp,
                                               bAddAppToStartupGrp,
                                         "&" + szInstallPCSoundDriver,
                                               bInstallPCSoundDriver,
                                         "&" + szMapUnitsKm,
                                               bMapUnitsKm);
                else
                    // Sound driver is already installed
                    lReturn = AskOptions( lValue, szNonExclMessage,
                                         "&" + szEnableIpSupport,
                                               bEnableIpSupport,
                                         "&" + szEnableQT1000Support,
                                               bEnableQT1000Support,
                                         "&" + szAddAppToStartupGrp,
                                               bAddAppToStartupGrp,
                                         "&" + szMapUnitsKm,
                                               bMapUnitsKm);
                endif;
            default:
                // Error;
        endswitch;
        return (lReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CaptureIpProductKey()
 *
 *  Purpose:  Capture a valid product Key from the user.
 *
 *    Input:
 *  Returns:  BOOL
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function CaptureIpProductKey()
STRING szTitle, szTmp, szText;
BOOL bReturn;
begin
        bReturn = FALSE;
        LoadString128(SETUP_ENTER_IP_PROD_KEY, szTitle);
        SetDialogTitle(DLG_ASK_TEXT, szTitle);
        LoadString128(SETUP_IP_PROD_KEY_REQUIRED, szTmp);
        if ( AskText(szTmp, "", szText)  != BACK ) then
            if (szText = CONS_IP_PRODUCT_KEY_DEF) then
                bReturn = TRUE;
            else
                if (szText != "" ) then
                    LoadString128(SETUP_IP_PROD_KEY_INVALID, szText);
                    SprintfBox( WARNING, szTitle, szText);
                endif;
            endif;
        endif;
        return bReturn;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  IsIpProductKeyInstalled()
 *
 *  Purpose:  Determine if the user has a valid IP Product Key in N2000.INI
 *
 *    Input:
 *  Returns:  BOOL
 *
 * Comments:  The function looks up the N2000.INI file
 *
 *            [N2000 Config]
 *            ipInstalled=nnn-nnnnnnn
 *
\*---------------------------------------------------------------------------*/
function IsIpProductKeyInstalled()
STRING szFn, szIni, szTmp;
BOOL bReturn;
begin
        szFn = "IsIpProductKeyInstalled()";
        bReturn = FALSE;
        if ( !bNewInstall ) then
            // Set the fully qualified path to the app .INI file.
            szIni = szAppPath ^ CONS_CONFIG_FILE_NAME;
            if ( Is( FILE_EXISTS, szIni ) ) then
                if ( GetProfString(szIni, CONS_NETCFG_SECTION,
                                   CONS_IP_INSTALLED, szTmp) = 0) then
                    if (szTmp = CONS_IP_PRODUCT_KEY_DEF) then
                            bReturn = TRUE;
                    endif;
                endif;
            endif;
        endif;
        return (bReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  IsTcpIpInstalled()
 *
 *  Purpose:  Determine if the target machine has TCP/IP protocol installed.
 *
 *    Input:
 *  Returns:  BOOL
 *
 * Comments:  The function looks up the registry key:
 *
 *            HKEY_LOCAL_MACHINE
 *              System
 *                CurrentControlSet
 *                   Services
 *                     Tcpip
 *                        Linkage
 *
 *            The existence of this key is not necessarily an indicator
 *            that the user has a permanent network connection. When a
 *            RAS installation is done from the 'My Computer' 'Dial-Up
 *            Networking' icon, one of the dialogs offers TCP/IP as one
 *            of several protocol choices, and installs the above entry
 *            in the registry along with the RAS stuff.
 *
\*---------------------------------------------------------------------------*/
function IsTcpIpInstalled()
STRING szFn, szKey;
BOOL bReturn;
begin
        szFn = "IsTcpIpInstalled()";
        bReturn = TRUE;
        if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) < 0 ) then
            //szMsg = "Failed to set root key to HKEY_LOCAL_MACHINE";
            bReturn = FALSE;
        else
            szKey = "\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Linkage";
            // Check to see if the key exists
            if ( RegDBKeyExist(szKey) < 0) then
                bReturn = FALSE;
            endif;
        endif;
        return (bReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  IsRASInstalled()
 *
 *  Purpose:  Determine if RAS is installed on the target machine.
 *
 *    Input:
 *  Returns:  BOOL
 *
 * Comments:  The function looks up the registry key:
 *
 *            HKEY_LOCAL_MACHINE
 *              System
 *                CurrentControlSet
 *                   Services
 *                     RemoteAccess
 *
 *            and if this key exists, takes this as indicating that RAS
 *            is installed.
 *
 *            Other entries that may exist:
 *                    RasAcd
 *                    RasArp
 *                    RasAuto
 *                    RasMan
 *
 *            There is also a SYSTEM32\RAS directory and
 *            numerous ras*.* files in \SYSTEM32
\*---------------------------------------------------------------------------*/
function IsRASInstalled()
STRING szFn, szKey;
BOOL bReturn;
begin
        szFn = "IsRASInstalled()";
        bReturn = TRUE;
        if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) < 0 ) then
            //szMsg = "Failed to set root key to HKEY_LOCAL_MACHINE";
            bReturn = FALSE;
        else
            szKey = "\\SYSTEM\\CurrentControlSet\\Services\\RemoteAccess";
           // Check to see if the key exists
           if ( RegDBKeyExist(szKey) < 0) then
               bReturn = FALSE;
           endif;
        endif;
        return (bReturn);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  RASSetup()
 *
 *  Purpose:  If the user has requested the Alarm Forwarding feature,
 *            - Check if TCP/IP is installed
 *              If so we're done. We don't want to give the user the
 *              option to use RAS dial-up forwarding if he's got a
 *              permanent IP link he can use.
 *
 *              If not, check if RAS is installed.
 *              If it is we're done
 *              If not tell the user he'll have to install RAS for himself
 *              and point him to the readme for the details.
 *
 *    Input:
 *  Returns:  BACK possibly
 *
 * Comments:  Related n2000.ini entries
 *
 *            [Programs]
 *            ForwardManager=forward.exe  ; may be commented out disabling AF
 *
 *            [N2000 Config]]
 *            enableForwarding=1
 *            ipInstalled=0
 *
 *            [ForwardManager]
 *            RasAllowed=0        ; 1=User wants to use RAS dial-up forwarding
 *
 *            [AlarmStream]
 *            ConnectTCPIP=1
 *
\*---------------------------------------------------------------------------*/
#ifdef LOCKED_OUT_FOR_30
function RASSetup()
STRING szTitle, szMsg, szInfo;
LIST  listInfo;
LONG lResult;
begin
        LoadString128(SETUP_RAS_DLG_TITLE, szTitle);
        lResult = NEXT;
        if ( !IsTcpIpInstalled() ) then
            if ( !IsRASInstalled() && bEnableAlarmForwarding ) then
               // Create a string list.
               listInfo = ListCreate(STRINGLIST);
               LoadString128(SETUP_RAS_SETUP_MSG1, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               ListAddString(listInfo, " ", AFTER);
               LoadString128(SETUP_RAS_SETUP_MSG2, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               ListAddString(listInfo, " ", AFTER);
               LoadString128(SETUP_RAS_SETUP_MSG3, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               LoadString128(SETUP_RAS_SETUP_MSG4, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               ListAddString(listInfo, " ", AFTER);
               ListAddString(listInfo, " ", AFTER);
               LoadString128(SETUP_RAS_SETUP_MSG5, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               ListAddString(listInfo, " ", AFTER);
               LoadString128(SETUP_RAS_SETUP_MSG6, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               LoadString128(SETUP_RAS_SETUP_MSG7, szInfo);
               ListAddString(listInfo, szInfo, AFTER);
               ListAddString(listInfo, " ", AFTER);
               ListAddString(listInfo, " ", AFTER);
               LoadString128(SETUP_SEE_X_FOR_MORE_INFO, szMsg);
               Sprintf( szInfo,szMsg, README_DOT_TXT);
               ListAddString(listInfo, szInfo, AFTER);
               ListAddString(listInfo, " ", AFTER);
               Enable (BACKBUTTON);
               Enable  (NEXTBUTTON);
               Enable  (CANCELBUTTON);
               // Display the dialog
               LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
               lResult = SdShowInfoList (szTitle, szMsg, listInfo);
               ListDestroy( listInfo );
            endif;
        endif;
        return (lResult);
end;
#endif // LOCKED_OUT_FOR_30
/*---------------------------------------------------------------------------*\
 *
 * Function:  GetAutoLogonDefaults()
 *
 *  Purpose:  Get and save the autologon registry defaults.
 *            Set bOfferAutoLogon TRUE if :
 *            - Running under NT
 *            - Defaults are good
 *            - AutoLogon isn't already installed:
 *              AutoAdminLogon value missing or 0.
 *
 *    Input:
 *   Output:  Globals:
 *            BOOL   bOfferAutoLogon
 *            STRING szDefaultUserName             Autologon value on entry
 *            STRING szDefaultDomainName           Autologon value on entry
 *            STRING szDefaultPassword             Autologon value on entry
 *
 *  Returns:
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function GetAutoLogonDefaults()
STRING szKey, szName, szValue;
LONG lType, lSize;
begin
        bOfferAutoLogon = FALSE;
        szKey = CSTR_REG_AUTO_LOGON_KEY;
        if ( SysInfo.lOS = IS_WINDOWSNT ) then
          if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) = 0 ) then
            // IS3 TRAP : RegDbKeyExists returns 1 not 0 if key found
            if ( RegDBKeyExist(szKey) = 1 ) then
                RegDBGetKeyValueEx(szKey, CSTR_REG_AUTO_ADMIN_LOGON,
                                   lType, szDefaultAutoAdminLogon, lSize);
                if (szDefaultAutoAdminLogon != "1") then
                    if (RegDBGetKeyValueEx(szKey, CSTR_REG_DEFAULT_USER_NAME,
                                   lType, szDefaultUserName, lSize) != 0 ) then
                        szDefaultUserName = "";
                    endif;
                    if (RegDBGetKeyValueEx(szKey, CSTR_REG_DEFAULT_DOMAIN_NAME,
                                 lType, szDefaultDomainName, lSize) != 0 ) then
                        szDefaultDomainName = "";
                    endif;
                    if (RegDBGetKeyValueEx(szKey, CSTR_REG_DEFAULT_PASSWORD,
                                   lType, szDefaultPassword, lSize) != 0 ) then
                        szDefaultPassword = "";
                    endif;
                    bOfferAutoLogon = TRUE;
                endif;
            endif;
          endif;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  AutoLogon()
 *
 *  Purpose:  If the user has requested AutoLogon
 *            display the AutoLogon configuration dialog with the
 *            current registry defaults, and capture new values.
 *
 *    Input:
 *   Output:  bAutoLogon
 *            szNew autologon fields
 *  Returns:  BACK possibly
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function AutoLogon()
STRING szTitle, szKey, szMsg, szInfo, szLabel1, szLabel2, szLabel3;
LIST  listInfo;
LONG lResult;
begin
        if (!bOfferAutoLogon) then
            return;
        endif;
        if (SysInfo.lOS = IS_WINDOWSNT) then
            Enable (BACKBUTTON);
            Enable (NEXTBUTTON);
            Enable (CANCELBUTTON);
            LoadString128(SETUP_LOGIN_NAME, szLabel1);
            LoadString128(SETUP_DOMAIN, szLabel2);
            LoadString128(SETUP_PASSWORD, szLabel3);
            szNewUserName   = szDefaultUserName;
            szNewDomainName = szDefaultDomainName;
            szNewPassword   = "";
            // Display the dialog
            LoadString128(SETUP_AUTO_LOGON_DLG_TITLE, szTitle);
            LoadString128(SETUP_AUTO_LOGON, szMsg);
            lResult = SdShowDlgEdit3 (szTitle, szMsg,
                                      szLabel1, szLabel2, szLabel3,
                                      szNewUserName, szNewDomainName,
                                      szNewPassword);
            // If user changed a key, write the registry to
            // make sure we can do this and then reset them
            // back again.  The user can still cancel out.
            WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
            WriteLogMsg( " " );
            WriteLogMsg( szTitle );
            WriteLogMsg( " ");
            szKey = CSTR_REG_AUTO_LOGON_KEY;
            if (szNewUserName != szDefaultUserName) then
                 if (!CreateRegistryKey(szKey, CSTR_REG_DEFAULT_USER_NAME,
                                  REGDB_STRING, szNewUserName, -1) ) then
                    bAutoLogon = FALSE;
                 else
                    if (!CreateRegistryKey(szKey, CSTR_REG_DEFAULT_USER_NAME,
                                  REGDB_STRING, szDefaultUserName, -1) ) then
                        bAutoLogon = FALSE;
                    endif;
                 endif;
            endif;
            if (szNewDomainName != szDefaultDomainName) then
                 if (!CreateRegistryKey(szKey, CSTR_REG_DEFAULT_DOMAIN_NAME,
                                  REGDB_STRING, szNewDomainName, -1) ) then
                    bAutoLogon = FALSE;
                 else
                    if (!CreateRegistryKey(szKey, CSTR_REG_DEFAULT_DOMAIN_NAME,
                                  REGDB_STRING, szDefaultDomainName, -1) ) then
                        bAutoLogon = FALSE;
                    endif;
                 endif;
            endif;
            if (szNewPassword != "" ) then
                 if (!CreateRegistryKey(szKey, CSTR_REG_DEFAULT_PASSWORD,
                                  REGDB_STRING, szNewPassword, -1) ) then
                    bAutoLogon = FALSE;
                 else
                    if (!CreateRegistryKey(szKey, CSTR_REG_DEFAULT_PASSWORD,
                                  REGDB_STRING, szDefaultPassword, -1) ) then
                        bAutoLogon = FALSE;
                    endif;
                 endif;
            else
                 szNewPassword = szDefaultPassword;
            endif;
            if (bAutoLogon) then
                 if (!CreateRegistryKey(szKey, CSTR_REG_AUTO_ADMIN_LOGON,
                                  REGDB_STRING, "1", -1) ) then
                    bAutoLogon = FALSE;
                 else
                    if (!CreateRegistryKey(szKey, CSTR_REG_AUTO_ADMIN_LOGON,
                                  REGDB_STRING, "0", -1) ) then
                        bAutoLogon = FALSE;
                    endif;
                 endif;
            endif;
        endif;
        return (lResult);
end;
/*-----------------------------------------------------------------------
 *
 *  Name   :  ConfirmSelections( SwapInfo )
 *
 *  Purpose:  This function will construct a info list for showing the
 *            SdStartCopy dialog to confirm the file transfer operation.
 *
 *  Returns:  BACK  (possibly)
 *
\*----------------------------------------------------------------------*/
function ConfirmSelections()
STRING szItem, szYN, szMsg, szBuf;
LIST   listInfo;
LONG   lResult, lId;
begin
        // Set the message above the listbox
        LoadString128(SETUP_CONFIRM_SELNS_MSG1, szItem);
        LoadString128(SETUP_CONFIRM_SELNS_MSG1, szYN);
        LoadString128(SETUP_CONFIRM_SELNS_MSG1, szBuf);
        Sprintf( szMsg,"%s\n%s\n%s", szItem, N2000, szYN, szBuf);
        listInfo = ListCreate( STRINGLIST );
        LoadString128(SETUP_TYPE, szBuf);
        ListAddString( listInfo, szBuf, AFTER );
        if (bNewInstall) then
             lId = SETUP_NEW_INSTALL;
        else
             lId = SETUP_UPDATE_INSTALL;
        endif;
        LoadString128(lId, szBuf);
        szItem = STR_DEFTAB + szBuf;
        ListAddString( listInfo, szItem, AFTER );
       if (bCopyOldDBFiles) then
            LoadString128(SETUP_COPY_DB_FILES, szTmp);
            Sprintf(szBuf, szTmp, szOldAppPath);
            szItem = STR_DEFTAB + szBuf;
            ListAddString( listInfo, szItem, AFTER );
        endif;
        ListAddString( listInfo, "", AFTER );
        LoadString128(SETUP_TARGET_DIRECTORY, szBuf);
        ListAddString( listInfo, szBuf, AFTER );
        ListAddString( listInfo, STR_DEFTAB + szAppPath, AFTER );
        ListAddString( listInfo, "", AFTER );
        LoadString128(SETUP_COMPONENT_SELECTION, szBuf);
        ListAddString( listInfo, szBuf, AFTER );
        if (bAppSelected) then
            ListAddString( listInfo, STR_DEFTAB + ITEM_APP, AFTER );
        endif;
        if ( !bRunMapsFromCdrom ) then
            if (bMapSelected) then
                LoadString128(SETUP_MAP_EUROPE, szBuf);
                if ( ComponentIsItemSelected (szComponentList, szBuf) ) then
                    ListAddString( listInfo, STR_DEFTAB + szBuf, AFTER );
                endif;
                LoadString128(SETUP_MAP_NORTHA, szBuf);
                if ( ComponentIsItemSelected (szComponentList, szBuf) ) then
                    ListAddString( listInfo, STR_DEFTAB + szBuf, AFTER );
                endif;
                LoadString128(SETUP_MAP_SOUTHA, szBuf);
                if ( ComponentIsItemSelected (szComponentList, szBuf) ) then
                    ListAddString( listInfo, STR_DEFTAB + szBuf, AFTER );
                endif;
                LoadString128(SETUP_MAP_SASIA, szBuf);
                if ( ComponentIsItemSelected (szComponentList, szBuf) ) then
                    ListAddString( listInfo, STR_DEFTAB + szBuf, AFTER );
                endif;
            endif;
        else
            LoadString128(SETUP_MAP_ALL_FROM_CDROM, szBuf);
            ListAddString( listInfo, STR_DEFTAB + szBuf, AFTER );
        endif;
        LoadString128(SETUP_ARC, szBuf);
        if (bAlarmRelayContactsSelected) then
            ListAddString( listInfo, STR_DEFTAB + szBuf, AFTER );
        endif;
        ListAddString( listInfo, "", AFTER );
        LoadString128(SETUP_OPTIONS, szBuf);
        ListAddString( listInfo, szBuf, AFTER );
        if (bEnableIpSupport) then
           LoadString128(SETUP_YES, szYN);
        else
           LoadString128(SETUP_NO, szYN);
        endif;
        LoadString128(SETUP_ENABLE_IP, szBuf);
        szItem = STR_DEFTAB + szBuf + ": " + szYN;
        ListAddString( listInfo, szItem, AFTER );
       // Alarm Forwarding is not offered under Win 95
        if ( SysInfo.lOS != IS_WINDOWS95) then
            if (bEnableAlarmForwarding) then
               LoadString128(SETUP_YES, szYN);
            else
               LoadString128(SETUP_NO, szYN);
            endif;
            LoadString128(SETUP_ENABLE_ALARM_FORWARDING, szBuf);
            szItem = STR_DEFTAB + szBuf + ": " + szYN;
            ListAddString( listInfo, szItem, AFTER );
        endif;
        if (bEnableQT1000Support) then
           LoadString128(SETUP_YES, szYN);
        else
           LoadString128(SETUP_NO, szYN);
        endif;
        LoadString128(SETUP_ENABLE_QT1000, szBuf);
        szItem = STR_DEFTAB + szBuf + ": " + szYN;
        ListAddString( listInfo, szItem, AFTER );
       if (bAddAppToStartupGrp) then
           LoadString128(SETUP_YES, szYN);
        else
           LoadString128(SETUP_NO, szYN);
        endif;
        LoadString128(SETUP_ADD_TO_STARTUP_GRP, szBuf);
        szItem = STR_DEFTAB + szBuf + ": " + szYN;
        ListAddString( listInfo, szItem, AFTER );
        if (bAutoLogon) then
           LoadString128(SETUP_YES, szYN);
           LoadString128(SETUP_AUTO_LOGON, szBuf);
           szItem = STR_DEFTAB + szBuf + ": " + szYN;
           ListAddString( listInfo, szItem, AFTER );
        endif;
        if (bInstallPCSoundDriver) then
           LoadString128(SETUP_YES, szYN);
        else
           LoadString128(SETUP_NO, szYN);
        endif;
        LoadString128(SETUP_INSTALL_SPEAKERDRV, szBuf);
        szItem = STR_DEFTAB + szBuf + ": " + szYN;
        ListAddString( listInfo, szItem, AFTER );
        if (bMapSelected) then
            if (bMapUnitsKm) then
               LoadString128(SETUP_YES, szYN);
            else
               LoadString128(SETUP_NO, szYN);
            endif;
            LoadString128(SETUP_MAP_UNITS_KM, szBuf);
            szItem = STR_DEFTAB + szBuf + ": " + szYN;
            ListAddString( listInfo, szItem, AFTER );
        endif;
        // Display the dialog
        lResult = SdStartCopy( "", szMsg, listInfo );
        ListDestroy( listInfo );
        return lResult;
end;
/*-------------------------------------------------------------------------*\
 *
 * Function:  LaunchDateTimeApplet()
 *
 *  Purpose:  Launch the ControlPanel Date-Time applet to let the user
 *            set the Time Zone and Daylight Savings Time registry entries.
 *
 *            The registry entries are only used by N2000 if the
 *            [] Local Time checkbox is set.  However, we want the user to
 *            set them so that Jeromes local date perversion logic will
 *            work if they do turn the checkbox on.
 *
 *            Read and log the registry entries in setup.log:
 *            HKEY_LOCAL_MACHINE
 *             SYSTEM\CurrentControlSet\Control\TimeZoneInformation
 *            This will give Warren something to work with.
 *
 *            Read, log, and delete any TZ registry entries set by
 *            the System applet (see comment below for detail).
 *
 *    Input:
 *
 *   Output:
 *
 * Comments:  The registry entries are binary encoded.
 *            See Dave's file for decode.
 *
 *          - The user can set TZ directly in 4 places:
 *            Date-Time Applet HKEY_LOCAL_MACHINE ~~ TimeZoneInformation
 *            System Applet
 *               (System)      HKEY_LOCAL_MACHINE ~~ Session Manager Environment
 *               (User),       HKEY_CURRENT_USER  ~~ Environment
 *            DOS box,
 *            and indirectly in autoexec.bat.
 *
 *          - Changes made to TZ with the Date-Time, System, and DOS box
 *            have no dynamic effect on a running N2000 system.
 *
 *          - N2000 must be re-started for changes to be picked up.
 *
 *          - When N2000 is re-started if the System applet or another app
 *            has been used, then the  TZ vars in the Session Manager registry
 *            key group and not the TZ vars set by the Date-Time applet in the
 *            TimeZoneInformation registry key group are used.
 *
 *            To ensure that the Date-Time set TZ registry parms will prevail,
 *            setup logs to setup.log and then deletes any System applet set
*             TZ vars it finds in the registry in this function.
 *
 *          - When Windows is re-started if the autoexec.bat contains TZ sets
 *            then these TZ values and not the TZ vars set by the Date-Time
 *            applet in the TimeZoneInformation registry key group are used.
 *
 *            To ensure that the Date-Time set TZ registry parms will prevail,
 *            setup deletes any TZ vars it finds in autoexec.bat when doing
 *            UpdateAutoexecBat().
 *
 *
\*---------------------------------------------------------------------------*/
function LaunchDateTimeApplet()
STRING szFn, szKey, szKeyPath, szName, szValue, szMsg, szTitle,
       szProgram, szCmdLine, szErr, szReason, szExpand, szInfo;
LONG   lErr, lType, lSize;
LIST   listInfo;
begin
        LoadString128(SETUP_DATE_TIME_TITLE, szTitle);
        szFn = "LaunchDateTimeApplet()";
        SetStatusWindow( -1, szTitle );
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        WriteLogMsg( " " );
        WriteLogMsg( szFn);
        WriteLogMsg( " " );
        // Read, log, and delete the TZ registry entries set by
        // the System applet if they exist.
        //
        if ( RegDBSetDefaultRoot( HKEY_CURRENT_USER) = 0 ) then
            szKey = REG_KEY_ENVIRONMENT;
            szKeyPath = "HKEY_CURRENT_USER" + "\\" + szKey;
            // If the key exists
            // IS3 TRAP : RegDbKeyExists returns 1 not 0 if key found
            if ( RegDBKeyExist(szKey) = 1) then
                // Read-Log the values
                szName = REG_VALUE_NAME_TZ;
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    if (RegDBDeleteValue( szKey, szName) < 0 ) then
                        LoadString128(SETUP_ERROR, szErr);
                        LoadString128(SETUP_FAILED_TO_DELETE, szExpand);
                        Sprintf(szReason, szExpand, szKeyPath);
                        Sprintf(szMsg, "%s%s", szErr, szReason);
                    else
                        LoadString128(SETUP_DELETED, szExpand);
                        Sprintf(szMsg, szExpand, szKeyPath);
                     endif;
                    WriteLogMsg(szMsg);
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
            endif;
        endif;
        if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) = 0 ) then
            szKey = REG_HLM_SESSION_MGR + "\\" + REG_KEY_ENVIRONMENT;
            szKeyPath = "HKEY_LOCAL_MACHINE" + "\\" + szKey;
            // If the key exists
            // IS3 TRAP : RegDbKeyExists returns 1 not 0 if key found
            if ( RegDBKeyExist(szKey) = 1) then
                // Read-Log the values
                szName = REG_VALUE_NAME_TZ;
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    if (RegDBDeleteValue( szKey, szName) < 0 ) then
                        LoadString128(SETUP_ERROR, szErr);
                        LoadString128(SETUP_FAILED_TO_DELETE, szExpand);
                        Sprintf(szReason, szExpand, szKeyPath);
                        Sprintf(szMsg, "%s%s", szErr, szReason);
                    else
                        LoadString128(SETUP_DELETED, szExpand);
                        Sprintf(szMsg, szExpand, szKeyPath);
                     endif;
                    WriteLogMsg(szMsg);
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
            endif;
        endif;
        // Display info panel inciting the user to set time-zone info
        listInfo = ListCreate(STRINGLIST);
        ListAddString(listInfo, "", AFTER);
        // 'Setup will next run the Control Panel Date/Time applet.'
        LoadString128(SETUP_DATE_TIME_MSG1, szInfo);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, "", AFTER);
        // 'Please use this to set the Time Zone for your locality.'
        LoadString128(SETUP_DATE_TIME_MSG2, szInfo);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, "", AFTER);
        if ( SysInfo.lOS = IS_WINDOWSNT && SysInfo.lWinMajor > 3) then
            ListAddString(listInfo, " ", AFTER);
            // 'Warning! Changing the time zone may also change the local time.'
            LoadString128(SETUP_DATE_TIME_MSG3, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            // 'Check the local time shown on the Taskbar.'
            LoadString128(SETUP_DATE_TIME_MSG4, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            // 'Adjust the local time if necessary.'
            LoadString128(SETUP_DATE_TIME_MSG5, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, "", AFTER);
        endif;
        //IS3BUGFIX - Suppress dup last line
        ListAddString(listInfo, " ", AFTER);
        // Display the dialog
        Disable( BACKBUTTON );
        LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
        SdShowInfoList (szTitle, szMsg, listInfo);
        ListDestroy( listInfo );
        // Run the Date/Time Applet
        szProgram = CONTROL_DOT_EXE;
        if ( SysInfo.lOS = IS_WINDOWS95 ||
             (SysInfo.lOS = IS_WINDOWSNT && SysInfo.lWinMajor > 3)
           ) then
            szCmdLine = " " + TIMEDATE_DOT_CPL;
        else
            szCmdLine = ' "Date/Time"';
        endif;
        // IS3 Bug : The WAIT doesn't, and rul file WILL continue
        // after a timeout!
        lErr = LaunchAppAndWait(szProgram, szCmdLine, WAIT);
        if (lErr <= 0) then
            // Date_Time applet did not run
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_UNABLE_TO_RUN_PROGRAM, szExpand);
            Sprintf(szReason, szExpand, szProgram + szCmdLine);
            Sprintf(szMsg, "%s%s", szErr, szReason);
            SprintfBox( SEVERE, szFn, szMsg);
            WriteLogMsg( szFn + " " + szMsg);
        endif;
        // Read and log the current time zone settings:
        if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) = 0 ) then
            szKey = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
            szKeyPath = "HKEY_LOCAL_MACHINE" + "\\" + szKey;
            // If the key exists
            // IS3 TRAP : RegDbKeyExists returns 1 not 0 if key found
            if ( RegDBKeyExist(szKey) = 1) then
                 WriteLogMsg( " " );
                 LoadString128(SETUP_ADDED, szExpand);
                 Sprintf(szMsg, szExpand, szKeyPath);
                 WriteLogMsg(szMsg);
                // Read-Log the values
                szName = "ActiveTimeBias";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "Bias";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "DaylightBias";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "DaylightName";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "DaylightStart";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "StandardBias";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "StandardName";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
                szName = "StandardStart";
                if (RegDBGetKeyValueEx(szKey, szName,
                                       lType, szValue, lSize) = 0 ) then
                    LogRegistryKey(szKey, szName, lType, szValue, lSize);
                endif;
            endif;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateRegistry
 *
 *  Purpose:  - Add giveio service to support Alarm Relay Contacts under NT
 *            - Fix the NT Service pack 2 RAS Bug
 *            - Add automatic logon under NT
 *            - Increase pagefile.sys size for Windows NT systems if necessary
 *
 *    Input:  bAutoLogon
 *
 *  Returns:  BOOL  TRUE=Success FALSE=Failure
 *
 * Comments:  We keep going even if a critical key error happens so that
 *            the log will contain full documentation on the keys that
 *            need to be created and values set.
 *
 \*---------------------------------------------------------------------------*/
function UpdateRegistry()
STRING szFn, szMsg, szExpand, szError, szAction;
begin
        szFn = "UpdateRegistry()";
        if (SysInfo.lOS = IS_WINDOWSNT ) then
            LoadString128(SETUP_UPDATING, szExpand);
            Sprintf(szMsg, szExpand, SETUP_REGISTRY);
            SetStatusWindow( -1, szMsg);
            WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
            WriteLogMsg( szFn );
            WriteLogMsg( " ");
            if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) < 0 ) then
                LoadString128(SETUP_UNABLE_TO_UPDATE_REG, szMsg);
                WriteLogMsg( szMsg );
                LoadString128(SETUP_ARC_DRIVER_NOT_INSTALLED, szMsg);
                WriteLogMsg( szMsg );
                LoadString128(SETUP_PAGEFILE_SIZE_NOT_INC, szMsg);
                WriteLogMsg( szMsg );
                LoadString128(SETUP_AUTO_LOGON_NOT_INSTALLED, szMsg);
                WriteLogMsg( szMsg );
                LoadString128(SETUP_UNABLE_TO_UPDATE_REG, szError);
                LoadString128(SETUP_CALL_CUSTOMER_SERVICE, szAction);
                if ( !RegistryError(szError, szAction) ) then
                     return FALSE;
                endif;
            else
               // Update for ourselves.
               // If it fails log problem but do not involve user.
               UpdateRegistryAppPath();
                if ( !UpdateRegistryRelayDriver() ) then
                     return FALSE;
                endif;
                if ( !UpdateRegistryAutoLogon() ) then
                     return FALSE;
                endif;
                if ( !UpdateRegistryPagefile() ) then
                     return FALSE;
                endif;
            endif;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateRegistryAppPath
 *
 *  Purpose:  Keep track of where the app is installed so we won't have to
 *            search the disc to find out where the user put it.
 *
 *    Input:
 *
 *  Returns:  BOOL  TRUE=Success FALSE=Failure
 *
 * Comments:  We keep going even if a critical key error happens so that
 *            the log will contain full documentation on the keys that
 *            need to be created and values set.
 *
 *      Key:  HKEY_LOCAL_MACHINE\SOFTWARE\
 *                COMPANY_NAME\PRODUCT_NAME\PRODUCT_VERSION
 *
 *            AppPath:REG_SZ: d:\n2000
 *
 *    szKey:  Do NOT:
 *            - use the append to path (^) operator as this turns szKey
 *              into a valid path with a .\ in front of it.
 *            - use a "\\" at the beginning of szKey.
 *
 *            Doing either of the above will cause the key not to be created
 *            on 'certain operating systems' (IS Kbase article Q10309)
\*---------------------------------------------------------------------------*/
function UpdateRegistryAppPath()
STRING szFn, szMsg, szKey, szKeyPath, szExpand, szValue, szError, szAction;
BOOL bReturn;
begin
        szFn = "UpdateRegistryAppPath()";
        WriteLogMsg( szFn );
        bReturn = TRUE;
        szKey = "SOFTWARE" + "\\" + COMPANY_NAME + "\\" + PRODUCT_NAME + "\\"
                           + PRODUCT_VERSION;
        szKeyPath = "HKEY_LOCAL_MACHINE" + "\\" + szKey;
        // The key should already have been created
        if ( RegDBKeyExist(szKey) < 0) then
            LoadString128(SETUP_FAILED_TO_CREATE, szExpand);
            Sprintf(szMsg, szExpand, szKeyPath);
            WriteLogMsg( szMsg );
        else
            // Key already exists
            LoadString128(SETUP_REGISTRY_KEY_EXISTS, szExpand);
            Sprintf(szMsg, szExpand, szKeyPath);
           // Write the sub key+value pair
           CreateRegistryKey(szKey, "AppPath", REGDB_STRING,
                                    szAppPath, -1);
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateRegistryRelayDriver
 *
 *  Purpose:  Install the giveio.sys kernel level driver.
 *
 *    Input:
 *
 *  Returns:  BOOL  TRUE=Success FALSE=Failure
 *
 * Comments:  We keep going even if a critical key error happens so that
 *            the log will contain full documentation on the keys that
 *            need to be created and values set.
 *
 *      Key:  HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\giveio";
 *
 *            DisplayName:REG_SZ: giveio  (same as last key element)
 *            ErrorControl:REG_DWORD: 0x1 (normal. Display warning if loadfails)
 *            ImagePath:REG_EXPAND_SZ: \SystemRoot\System32\drivers\giveio.sys
 *            Start:REG_DWORD: 0x2        (Always loaded by Service Control Mgr)
 *            Type:REG_DWORD: 0x1         (Kernel Mode driver)
 *
 *
 *    szKey:  Do NOT:
 *            - use the append to path (^) operator as this turns szKey
 *              into a valid path with a .\ in front of it.
 *            - use a "\\" at the beginning of szKey.
 *
 *            Doing either of the above will cause the key not to be created
 *            on 'certain operating systems' (IS Kbase article Q10309)
\*---------------------------------------------------------------------------*/
function UpdateRegistryRelayDriver()
STRING szFn, szMsg, szKey, szKeyPath, szExpand, szValue, szError, szAction;
BOOL bReturn;
begin
        szFn = "UpdateRegistryRelayDriver()";
        WriteLogMsg( szFn );
        bReturn = TRUE;
        szKey = "SYSTEM\\CurrentControlSet\\Services\\giveio";
        szKeyPath = "HKEY_LOCAL_MACHINE" + "\\" + szKey;
        // If the key doesn't already exist
        if ( RegDBKeyExist(szKey) < 0) then
            // Create main key. Enable logging so UninstallShield
            // can delete ....\giveio and it's sub-keys.
            Enable(LOGGING);
            if ( RegDBCreateKeyEx(szKey, "") < 0) then
                LoadString128(SETUP_FAILED_TO_CREATE, szExpand);
                Sprintf(szMsg, szExpand, szKeyPath);
                bReturn = FALSE;
            else
                LoadString128(SETUP_ADDED, szExpand);
                Sprintf(szMsg, szExpand, szKeyPath);
            endif;
            Disable(LOGGING);
        else
            // Key already exists
            LoadString128(SETUP_REGISTRY_KEY_EXISTS, szExpand);
            Sprintf(szMsg, szExpand, szKeyPath);
        endif;
        WriteLogMsg( szMsg );
        // Write the sub key+value pairs
        if (!CreateRegistryKey(szKey, "DisplayName", REGDB_STRING,
                                      "giveio", -1) ) then
            bReturn = FALSE;
        endif;
        if (!CreateRegistryKey(szKey, "ErrorControl", REGDB_NUMBER,
                                      "1", -1) ) then
            bReturn = FALSE;
        endif;
        szValue = "\\SystemRoot\\System32\\" ^
                                      DRIVERS ^ CONS_NT_PORTIO_DRIVER;
        if (!CreateRegistryKey(szKey, "ImagePath", REGDB_STRING_EXPAND,
                                      szValue, -1)) then
            bReturn = FALSE;
        endif;
        // Start = 0x2 = 'Always loaded by Service Control Manager'
        if (!CreateRegistryKey(szKey,"Start", REGDB_NUMBER,
                                      "2", -1)) then
            bReturn = FALSE;
        endif;
        // Type = 0x1 = Kernel Driver
        if (!CreateRegistryKey(szKey, "Type", REGDB_NUMBER,
                                      "1", -1)) then
             bReturn = FALSE;
        endif;
        szMsg = "";
        if (!bReturn) then
            LoadString128(SETUP_ARC_DRIVER_NOT_INSTALLED, szError);
            LoadString128(SETUP_CALL_CUSTOMER_SERVICE, szAction);
            if ( !RegistryError(szError, szAction) ) then
                return bReturn;
            endif;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateRegistryAutoLogon
 *
 *  Purpose:  Set Registry entries to enable auto-logon.
 *
 *      Key:  HKEY_LOCAL_MACHINE\SOFTWARE\\
 *                            Microsoft\\Windows NT\\CurrentVersion\\Winlogon
 *              AutoAdminLogon:REG_SZ:1
 *              DefaultUserName:REG_SZ:dgoodall
 *              DefaultDomainName:REG_SZ:DCG_TEST
 *              DefaultPassword:REG_SZ:daveshighlysecretpassword
 *
 *    Input:
 *
 *  Returns:  BOOL  TRUE=Success FALSE=Failure
 *
 * Comments:  We keep going even if a critical key error happens so that
 *            the log will contain full documentation on the keys that
 *            need to be created and values set.
 *
 *    szKey:  Do NOT:
 *            - use the append to path (^) operator as this turns szKey
 *              into a valid path with a .\ in front of it.
 *            - use a "\\" at the beginning of szKey.
 *
 *            Doing either of the above will cause the key not to be created
 *            on 'certain operating systems' (IS Kbase article Q10309)
\*---------------------------------------------------------------------------*/
function UpdateRegistryAutoLogon()
STRING szFn, szMsg, szKey, szKeyPath, szValue, szError, szAction;
BOOL bReturn;
begin
        szFn = "UpdateRegistryAutoLogon()";
        bReturn = TRUE;
        szKey = CSTR_REG_AUTO_LOGON_KEY;
        if (bAutoLogon) then
                    if ( !CreateRegistryKey(szKey, CSTR_REG_DEFAULT_USER_NAME,
                                  REGDB_STRING, szNewUserName, -1)) then
                         bReturn = FALSE;
                    endif;
                    if ( !CreateRegistryKey(szKey, CSTR_REG_DEFAULT_DOMAIN_NAME,
                                  REGDB_STRING, szNewDomainName, -1)) then
                         bReturn = FALSE;
                    endif;
                    if ( !CreateRegistryKey(szKey, CSTR_REG_DEFAULT_PASSWORD,
                                  REGDB_STRING, szNewPassword, -1)) then
                         bReturn = FALSE;
                    endif;
                    if ( !CreateRegistryKey(szKey, CSTR_REG_AUTO_ADMIN_LOGON,
                                  REGDB_STRING, "1", -1)) then
                         bReturn = FALSE;
                    endif;
        endif;
        if (!bReturn) then
            LoadString128(SETUP_AUTO_LOGON_NOT_INSTALLED, szError);
            LoadString128(SETUP_CALL_CUSTOMER_SERVICE, szAction);
            if ( !RegistryError(szError, szAction) ) then
                return bReturn;
            endif;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateRegistryPagefile
 *
 *  Purpose:  Increase pagefile.sys size for Windows NT systems if necessary
 *
 *    Input:
 *
 *  Returns:  BOOL  TRUE=Success FALSE=Failure
 *
 * Comments:  We keep going even if a critical key error happens so that
 *            the log will contain full documentation on the keys that
 *            need to be created and values set.
 *
 *    szKey:  Do NOT:
 *            - use the append to path (^) operator as this turns szKey
 *              into a valid path with a .\ in front of it.
 *            - use a "\\" at the beginning of szKey.
 *
 *            Doing either of the above will cause the key not to be created
 *            on 'certain operating systems' (IS Kbase article Q10309)
\*---------------------------------------------------------------------------*/
function UpdateRegistryPagefile()
STRING szFn, szMsg, szKey, szKeyPath, szValue, szResult, szExpand,
       szFile, szDrive, szInit, szMax, szError, szAction;
LONG lType, lSize, lValue, lPageFileSize, lInit, lMax, lNewInit, lNewMax,
     lDriveFreeSpace, lBiggest;
BOOL bReturn;
LIST listTokens;
begin
        szFn = "UpdateRegistryPagefile()";
        bReturn = TRUE;
        LoadString128(SETUP_PAGEFILE_SIZE_NOT_INC, szError);
        LoadString128(SETUP_CALL_CUSTOMER_SERVICE, szAction);
        if (SysInfo.lOS = IS_WINDOWSNT ) then
            LoadString128(SETUP_UPDATING, szExpand);
            Sprintf(szMsg, szExpand, SETUP_REGISTRY);
            SetStatusWindow( -1, szMsg);
            WriteLogMsg( " ");
            WriteLogMsg( szFn );
            if ( RegDBSetDefaultRoot( HKEY_LOCAL_MACHINE) < 0 ) then
                szMsg = "Failed to set root key to HKEY_LOCAL_MACHINE";
                WriteLogMsg( szMsg );
                bReturn = FALSE;
            else
                // -----------------------------------------------------------
                // Increase pagefile.sys size for Windows NT systems if needed
                // Get the paging File, Initial and Maximum Size settings
                szKey = REG_HLM_SESSION_MGR + "\\" + REG_KEY_MEMORY_MGT;
                szKeyPath = "HKEY_LOCAL_MACHINE" + "\\" + szKey;
                // if the 'Memory Management' key exists
                // IS3 TRAP : RegDbKeyExists returns 1 not 0 if key found
                if ( RegDBKeyExist(szKey) = 1) then
                    // Key exists
                    LoadString128(SETUP_REGISTRY_KEY_EXISTS, szExpand);
                    Sprintf(szMsg, szExpand, szKeyPath);
                    WriteLogMsg( szMsg );
                    // Check if 'PagingFiles' value entry exists
                    if (RegDBGetKeyValueEx(szKey, REG_VALUE_NAME_PAGING_FILES,
                                           lType, szValue, lValue) = 0 ) then
                        Sprintf(szMsg, " %s=%s", REG_VALUE_NAME_PAGING_FILES,
                                                   szValue);
                        WriteLogMsg( szMsg );
                        if (lType = REGDB_STRING_MULTI) then
                            // Parse: 'D:\pagefile.sys iii mmm\0\0' OR
                            //        'D:\pagefile.sys iii\0\0'
                            // for szDrive, szFile, lInit, lMax.
                            StrSub(szDrive, szValue, 0, 2);
                            // Note 1: The sub strings in szValue are space
                            // separated, NOT NULL separated as documented
                            // by IS in the RegDBGetKeyValueEx "Comments"!
                            // Note 2: lValue is 2 bytes LONGER than
                            // a StrLength(szValue) will report!
                            listTokens = ListCreate( STRINGLIST);
                            if ( StrGetTokens( listTokens, szValue, " ") = 0)
                                                                           then
                                ListGetFirstString(listTokens, szFile);
                                ListGetNextString(listTokens, szInit);
                                StrToNum( lInit,szInit);
                                ListGetNextString(listTokens, szMax);
                                StrToNum( lMax, szMax);
                                if (lMax = 0 ) then
                                    lMax = lInit;
                                endif;
                                ListDestroy(listTokens);
                                // Get the free space on the drive in MB
                                lSize = GetDiskSpace(szDrive);
                                lDriveFreeSpace = lSize / 1024000;
                                if (lSize > 0 &&
                                    lDriveFreeSpace > MIN_FREE_DISC_SPACE_MB)
                                    then
                                    // Get the pagefile.sys size in MB
                                    if ( GetFileInfo( szFile, FILE_SIZE,
                                                lSize, szResult) = 0 ) then
                                        lPageFileSize = lSize / 1024000;
                                        if (lPageFileSize <
                                            MIN_PAGEFILE_SIZE_MB) then
                                            // Gotta increase the page file
                                            // Calc new putative max limit
                                            lNewMax = MIN_PAGEFILE_SIZE_MB
                                                      + lMax
                                                      - lInit;
                                            // Calc max possible file size
                                            lBiggest = lPageFileSize
                                                       + lDriveFreeSpace
                                                       - MIN_FREE_DISC_SPACE_MB;
                                            if ( lNewMax > lBiggest ) then
                                                lNewMax = lBiggest;
                                            endif;
                                            lNewInit = MIN_PAGEFILE_SIZE_MB;
                                            if (lNewMax < lNewInit) then
                                                // If DEBUG ASSERT error
                                                lNewMax = lNewInit;
                                            endif;
                                            lSize = StrLength(szValue);
                                            // Assemble new value
                                            NumToStr(szInit, lNewInit);
                                            szValue = szFile + " " + szInit;
                                            if (lMax > lInit) then
                                                NumToStr(szMax, lNewMax);
                                                szResult = szValue;
                                                szValue =
                                                     szResult + " " + szMax;
                                            endif;
                                            lValue = StrLength(szValue);
                                            // Append two padding nulls
                                            SetByte(szValue,lValue,0);
                                            SetByte(szValue,lValue+1,0);
                                            lValue = lValue + 2;
                                            // Write new value
                                            Enable(LOGGING);
                                            if (CreateRegistryKey(szKey,
                                                REG_VALUE_NAME_PAGING_FILES,
                                                REGDB_STRING_MULTI,
                                                szValue, lValue) ) then
                                                Sprintf(szMsg, "   %s=%s",
                                                   REG_VALUE_NAME_PAGING_FILES,
                                                   szValue);
                                                WriteLogMsg( szMsg );
                                                // Force system reboot
                                                bReboot = TRUE;
                                                if ( DEBUG) then
                                                     WriteLogMsg(szFn +
                           " Set bReboot = TRUE for Pagefiles size increase.");
                                                    endif;
                                            else
                                                bReturn = FALSE;
                                            endif;
                                            Disable(LOGGING);
                                        endif;
                                    endif;
                                else
                                    LoadString128(SETUP_IN_SPACE_FOR_PAGEFILE,
                                                  szExpand);
                                    NumToStr(szValue, MIN_PAGEFILE_SIZE_MB);
                                    Sprintf(szError, szExpand,
                                                     szDrive, szValue);
                                    LoadString128(SETUP_SEE_X_FOR_MORE_INFO,
                                                  szExpand);
                                    Sprintf(szAction, szExpand, README_DOT_TXT);
                                    Sprintf(szMsg,"Drive %s  free space %i MB",
                                                  szDrive, lDriveFreeSpace);
                                    WriteLogMsg( szMsg );
                                    bReturn = FALSE;
                                endif;
                            endif;
                        endif;
                    endif;
                else
                    // Key does not exist
                    LoadString128(SETUP_FAILED_TO_SET_KEY, szMsg);
                    WriteLogMsg( szMsg );
                    Sprintf(szMsg, "   %s", szKeyPath);
                    WriteLogMsg( szMsg );
                     bReturn = FALSE;
                endif;
                if (!bReturn) then
                    // Warn user of problem and ask if he wants to continue.
                    if ( !RegistryError(szError, szAction) ) then
                        return bReturn;
                    endif;
                endif;
            endif;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  RegistryError( STRING, STRING )
 *
 *  Purpose:  Report registry processing error and ask the user
 *            what he wants to do about it.
 *
 *    Input:  STRING szError  - Error message
 *            STRING szAction - User action message
 *
 *  Returns:  BOOL   TRUE  User wants to continue
 *                   FALSE User wants to exit.
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function RegistryError( szError, szAction )
STRING szExpand, szMsg;
begin
        WriteLogMsg( szError );
        LoadString128(SETUP_UPDATING, szExpand);
        Sprintf(szMsg, szExpand, SETUP_REGISTRY);
        SetDialogTitle(DLG_ASK_YESNO, szMsg);
        LoadString128(SETUP_DO_U_WANT_TO_CONTINUE, szExpand);
        szMsg = szError + "\n" + szAction + "\n" + szExpand;
        MessageBeep(0);
        if ( AskYesNo(szMsg, YES) = YES) then
             return TRUE;
        else
             return FALSE;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CreateRegistryKey( STRING, STRING, LONG, STRING, LONG)
 *
 *  Purpose:  Wrap the RegDBSetKeyValueEx function so that we can
 *            error check registry key additions and log sucessfull
 *            key entries or problems
 *
 *    Input:  STRING szKey
 *            STRING szName
 *            LONG lType
 *            STRING szValue
 *            LONG lSize
 *
 *  Returns:  BOOL   TRUE= Success FALSE=Failure
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function CreateRegistryKey( szKey, szName, lType, szValue, lSize)
STRING szResult, szMsg, szType;
LONG   lValue;
BOOL   bReturn;
begin
        if ( RegDBSetKeyValueEx(szKey, szName, lType, szValue, lSize) < 0) then
            LoadString128(SETUP_FAILED_TO_SET_KEY, szResult);
            bReturn = FALSE;
        else
            LoadString128(SETUP_SET_KEY, szResult);
            bReturn = TRUE;
        endif;
        LogRegistryKey(szKey, szName, lType, szValue, lSize);
        return bReturn;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  LogRegistryKey( szKey, szName, lType, szValue, lSize)
 *
 *  Purpose:  Log a current  registry key value
 *
 *    Input:  STRING szKey
 *            STRING szName
 *            LONG lType
 *            STRING szValue
 *            LONG lSize
 *
 *  Returns:
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function LogRegistryKey( szKey, szName, lType, szValue, lSize)
STRING szMsg, szType;
LONG   lValue;
BOOL   bReturn;
begin
        switch (lType)
            case REGDB_STRING:
                szType = CSTR_REG_SZ;
            case REGDB_STRING_MULTI:
                szType = CSTR_REG_MULTI_SZ;
            case REGDB_NUMBER:
                szType = CSTR_REG_DWORD;
                Sprintf(szValue,"0x%s", lValue);
            case REGDB_BINARY:
                szType = CSTR_REG_BINARY;
                GetByte(lValue, szValue, 0);
                Sprintf(szValue,"0x%lx", lValue);
            case REGDB_STRING_EXPAND:
                szType = CSTR_REG_EXPAND_SZ;
            default:
                szType = "Undefined";
        endswitch;
        Sprintf(szMsg," %s:%s:%s", szName, szType, szValue);
        WriteLogMsg( szMsg );
end;
/*-----------------------------------------------------------------------
   Name    : DeleteOldFiles()
   Purpose : This function will delete these files from the application
             directory if they exist:
             netman.exe      n2000.exe as of 2.5
             netman.hlp      n2000.hlp as of 2.5
             netman.def      n2000.def as of 2.5
             netman.pif      n2000.pif as of 2.5
             arc16_31.dll    Obsoleted as of 2.5
             arc32_31.dll    Obsoleted as of 2.5
             serial.386      Obsoleted as of 2.5
             serial.org      Obsoleted as of 2.5
             gcl*.dll        statically bound as of 2.5
             sh22*.dll       obsoleted as of 2.5
             ha10*.dll       obsoleted as of 2.5
             driver.exe      obsoleted as of 2.6
             dbsave.bat      obsoleted as of 3.0
             calldrvr.exe    obsoleted as of 3.0
-------------------------------------------------------------------------*/
function DeleteOldFiles()
begin
        VarSave( SRCTARGETDIR );        // Save
        TARGETDIR = szAppPath;
        if ( Is( FILE_EXISTS, TARGETDIR ^ NETMAN + DOT_EXE) ) then
           DeleteFile( NETMAN + DOT_EXE );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ NETMAN + DOT_HLP) ) then
           DeleteFile( NETMAN + DOT_EXE );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ NETMAN + DOT_DEF) ) then
           DeleteFile( NETMAN + DOT_EXE );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ NETMAN + ".pif") ) then
           DeleteFile( NETMAN + DOT_EXE );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ "arc61_31.dll") ) then
           DeleteFile( "arc16_31.dll" );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ "arc32_31.dll") ) then
           DeleteFile( "arc32_31.dll" );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ "serial.386") ) then
           DeleteFile( "serial.386" );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ "serial.org") ) then
           DeleteFile( "serial.org" );
        endif;
        DeleteFile( "gcl*.dll" );
        DeleteFile( "sh22*.dll" );
        DeleteFile( "ha10*.dll" );
        if ( Is( FILE_EXISTS, TARGETDIR ^ "driver.exe") ) then
           DeleteFile( "driver.exe" );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ "dbsave.bat") ) then
           DeleteFile( "dbsave.bat" );
        endif;
        if ( Is( FILE_EXISTS, TARGETDIR ^ "calldrvr.exe") ) then
           DeleteFile( "calldrvr.exe" );
        endif;
       VarRestore( SRCTARGETDIR );     // Restore
end;
/*-----------------------------------------------------------------------
   Name    : CreateArcDirectories()
   Purpose : This function will create the directories for Alarm
             Relay Contact support or die trying.
-------------------------------------------------------------------------*/
function CreateArcDirectories()
STRING szFn, szDir;
begin
       szFn = "CreateArcDirectories()";
       // create directory for Computer Boards hardware test utilities
       szDir = TARGETDISK ^ PDIS08DIR;
       if ( !CreateDirectory( szDir, szFn ) ) then
           ExitHandler();
       endif;
       // create directory for Computer Boards configuration routines
       szDir = TARGETDISK ^ CBDIR;
       if ( !CreateDirectory( szDir, szFn ) ) then
           ExitHandler();
       endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SetupFileTransfer( szComponentList, szFileSet, szFileXferDest )
 *
 *  Purpose:  This function defines the file set based on the user's choices
 *            of components. This queues up the copy/decompress commands in
 *            the file set in memory to be executed by PerformFileTransfer().
 *
 *    Input:  szAppPath - Fully qualified path to the application directory
 *            SRCDIR    - Typically for installation from:
 *                        network drive = R:\NOTES.DCG\INSTALL\CDROM\
 *                        cdrom drive   = E:\
 *                        Diskette      = A:\
 *
 *  Returns:
 *
 * Comments:  To view the contents of library file use icomp data.n -l.
 *
 *            Before splitting, the data.z compressed library is:
 *
 *            DATA.Z Library file
 *             :.....N2000         ; contains all arcxx_nn.dll flavors
 *             :                              & speaker.drv
 *             :                              n2000.def (default .ini)
 *             :.....CB            ; contains cbul.386
 *             :.....PDIS08
 *
 *            After splitting into data.1, data.2, data.3 etc the
 *            content of each .n file individual chunk is a subset of
 *            this, broken arbitrarily across diskette boundaries.
 *
 *            For un-compressed CDROM installation the data.z file is
 *            not split:
 *
 *            DATA.Z Library file
 *             :.....N2000         ; contains all arcxx_nn.dll flavors
 *             :                              & speaker.drv
 *             :                              n2000.def (default .ini)
 *             :.....MAPDATA
 *             :.....NORTHA
 *             :.....SOUTHA
 *             :.....EUROPE
 *             :.....SASIA
 *             :
 *             :.....CB            ; contains cbul.386
 *             :.....PDIS08
 *
 *
 *    NOTE!:  This module is strongly coupled with BUILD.BAT.
 *            The code here MUST line up with BUILD.BAT icomp statements
 *
 *    NOTE!:  Your setup.lst packing list path entry for data.z MUST
 *            point at the directory in which buildd.bat places it or
 *            your billboards will cycle quickly through the entire set
 *            then stick on the last billboard.
 *
 *    NOTE!   If RunMapFromCdrom is set, Map data is NOT copied from the
 *            Cdrom to the user's disk, but UpdateOrReplaceAppIni
 *            sets the app .ini [Datafiles] section VoronoiTable,
 *            IndexFile, and DataFile0 parms to point at the Cdrom.
 *
\*---------------------------------------------------------------------------*/
function SetupFileTransfer( szComponentList, szFileSet, szFileXferDest )
STRING szInputSRCDIR, szNewSrcDir, szBuf, szMap, szTmp;
LONG lLen;
begin
        if (CDROM_INSTALL) then
            // CdRom (Uncompressed) file set
            VarSave( SRCTARGETDIR );        // Save
            // Remember the Input SRCDIR
            szInputSRCDIR = SRCDIR;         // Remember the input SRCDIR
            FileSetBeginDefine( szFileSet );
                // Warning! You are about to enter the Twilight Zone.
                //
                // Be aware that the On-Line reference pages for 'FileSetRoot'
                // 'XCopyFile' and 'CompressGet' are totally incorrect in
                // stating that these functions let you change the value of
                // SRCDIR inside a fileset using the FileSetRoot function.
                // Ignore every reference to SRCDIR on these pages!.
                // The FileSetRoot function will NOT change the value of
                // SRCDIR within a file set. If you display SRCDIR before
                // and after a FileSetRoot call it will NOT have changed.
                // I haven't checked this but I suspect that the cleanup
                // FileSetRoot(szFileSet, " "); call just before the
                // FileSetEndDefine(szFileSet) does NOT change the
                // value of SRCDIR either.
                // As far as you are concerned, what you do when you call
                // FileSetRoot function with szNewSrcDir as argument 2 is
                // to tell the file set where to find the source files.
                // In point of fact what seems to be happening is that
                // the code between the FileSetBegin and EndDefines is
                // EXECUTED once just to GENERATE code. The code is then
                // stashed in memory, but EXECUTION 'for real' is deferred.
                // The stashed code won't actually happen until the
                // PerformFileTransfer() function is called.
                // So at the time the first (code generation) execution
                // happens, which is all you as a programmer can see,
                // nothing happens to SRCDIR.  It will later, but you'll
                // never know it.
                // Of course you can shoot holes in this theory by
                // pointing out that the assigns to TARGETDIR do actually
                // work and TARGETDIR does change as a result but the
                // general trend of it is probably right.
                // All this goes to show that the entire confused discussion
                // on page 15-9 on how Installshield's internal language
                // implementation works (although correct) is something that
                // you don't need to know. If you can't control it, it you
                // don't need to know about it.
                // This is why we pass the szNewSrcDir as an explicit
                // parameter into the GaXCopyFile function we are using
                // to wrap the native InstallShield XCopyFile function.
                // We need to know the new source directory in the
                // GaXCopyFile function but SRCDIR, after the FileSetRoot()
                // does NOT reflect it.
                // The final kick in the head is that the szNewSrcDir
                // parameter of FileSetRoot() MUST have a trailing backslash
                // appended it, as the toad who wrote the function was too
                // inept to add it if it wasn't supplied.  Omit this at
                // your peril.
                // ----------------------------------------------------------
                // If Application component selected, then add to the file set.
                // Set SRCDIR and TARGETDIR.
                szNewSrcDir = szInputSRCDIR ^ N2000 + "\\";
                FileSetRoot(szFileSet, szNewSrcDir);
                TARGETDIR = szAppPath;
                szFileXferDest = TARGETDIR;
                if (ComponentIsItemSelected(szComponentList, ITEM_APP)) then
                   LoadString128(SETUP_COPYING_N2000_APP_FILES, szTmp);
                   Sprintf(szBuf,szTmp, N2000);
                   SetStatusWindow( -1, szBuf );
                   GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                else
                   // Always copy the latest db files.
                   // We need them to check the databases
                   LoadString128( SETUP_COPYING_DB_CHECK_PRGS, szBuf);
                   SetStatusWindow( -1, szBuf );
                   GaXCopyFile(szNewSrcDir, DBFIX_DOT_EXE,    DBFIX_DOT_EXE,
                                                                COMP_NORMAL);
                   GaXCopyFile(szNewSrcDir, DBCONVW_DOT_EXE,  DBCONVW_DOT_EXE,
                                                                COMP_NORMAL);
                   GaXCopyFile(szNewSrcDir, FILTCONV_DOT_EXE, FILTCONV_DOT_EXE,
                                                                COMP_NORMAL);
                endif;
                // If Alarm Relay Component selected, then add the file set.
                LoadString128(SETUP_ARC, szBuf);
                if (ComponentIsItemSelected(szComponentList, szBuf )) then
                   LoadString128(SETUP_COPYING_ALARM_RELAY_FILES, szBuf);
                   SetStatusWindow( -1, szBuf );
                   szNewSrcDir = szInputSRCDIR ^ CBDIR + "\\";
                   FileSetRoot(szFileSet, szNewSrcDir);
                   TARGETDIR = TARGETDISK ^ CBDIR;
                   szFileXferDest = TARGETDIR;
                   GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                   szNewSrcDir = szInputSRCDIR ^ PDIS08DIR + "\\";
                   FileSetRoot(szFileSet, szNewSrcDir);
                   TARGETDIR = TARGETDISK ^ PDIS08DIR;
                   szFileXferDest = TARGETDIR;
                   GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                endif;
               // If the user selected a map component,
               // and doesn't want to run from the CdRom then
               // add the file set.
                if (bRunMapsFromCdrom = FALSE) then
                    if (bMapSelected) then
                       LoadString128(SETUP_COPYING_MAP_VIX_FILE, szBuf);
                       SetStatusWindow( -1, szBuf );
                       szNewSrcDir = szInputSRCDIR ^ MDV_MAPDATA + "\\";
                       FileSetRoot(szFileSet, szNewSrcDir);
                       TARGETDIR = szAppPath ^ MDV_MAPDATA;
                       szFileXferDest = TARGETDIR;
                       GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                    endif;
                    LoadString128(SETUP_MAP_EUROPE, szMap);
                    if (ComponentIsItemSelected(szComponentList, szMap)) then
                       LoadString128(SETUP_COPYING_MAP_FOR_REGION, szTmp);
                       Sprintf(szBuf, szTmp, szMap);
                       SetStatusWindow( -1, szBuf );
                       szNewSrcDir = szInputSRCDIR ^ MDV_EUROPE + "\\";
                       FileSetRoot(szFileSet, szNewSrcDir);
                       TARGETDIR = szAppPath ^ MDV_MAPDATA ^ MDV_EUROPE;
                       szFileXferDest = TARGETDIR;
                       GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                    endif;
                    LoadString128(SETUP_MAP_NORTHA, szMap);
                    if (ComponentIsItemSelected(szComponentList, szMap)) then
                       LoadString128(SETUP_COPYING_MAP_FOR_REGION, szTmp);
                       Sprintf(szBuf, szTmp, szMap);
                       SetStatusWindow( -1, szBuf );
                       szNewSrcDir = szInputSRCDIR ^ MDV_NORTHA + "\\";
                       FileSetRoot(szFileSet, szNewSrcDir);
                       TARGETDIR = szAppPath ^ MDV_MAPDATA ^ MDV_NORTHA;
                       szFileXferDest = TARGETDIR;
                       GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                    endif;
                    LoadString128(SETUP_MAP_SOUTHA, szMap);
                    if (ComponentIsItemSelected(szComponentList, szMap)) then
                       LoadString128(SETUP_COPYING_MAP_FOR_REGION, szTmp);
                       Sprintf(szBuf, szTmp, szMap);
                       SetStatusWindow( -1, szBuf );
                       szNewSrcDir = szInputSRCDIR ^ MDV_SOUTHA + "\\";
                       FileSetRoot(szFileSet, szNewSrcDir);
                       TARGETDIR = szAppPath ^ MDV_MAPDATA ^ MDV_SOUTHA;
                       szFileXferDest = TARGETDIR;
                       GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                    endif;
                    LoadString128(SETUP_MAP_SASIA, szMap);
                    if (ComponentIsItemSelected(szComponentList, szMap)) then
                       LoadString128(SETUP_COPYING_MAP_FOR_REGION, szTmp);
                       Sprintf(szBuf, szTmp, szMap);
                       SetStatusWindow( -1, szBuf );
                       szNewSrcDir = szInputSRCDIR ^ MDV_SASIA + "\\";
                       FileSetRoot(szFileSet, szNewSrcDir);
                       TARGETDIR = szAppPath ^ MDV_MAPDATA ^ MDV_SASIA;
                       szFileXferDest = TARGETDIR;
                       GaXCopyFile(szNewSrcDir, ASK_DOT_ASK, ASK_DOT_ASK,
                                                                COMP_NORMAL);
                    endif;
            endif;
            FileSetRoot( szFileSet, "" );      // RESTORE SRCDIR
            FileSetEndDefine( szFileSet );
            VarRestore( SRCTARGETDIR );        // Restore
        else
            // Diskette (Compressed) file set
            FileSetBeginDefine( szFileSet );
                // Note: For diskette SRCDIR is the path to the data.1,
                //       data.2, data.3 library files - usually 'A:\'.
                //
                //       CompressGet's second parameter, execrably mis-
                //       named by InstallShield as 'szTargetFiles' is
                //       actually the file specification of the SOURCE
                //       files to be transferred FROM the library file
                //       TO the target disk. As such you need to specify
                //       the directory in the library AND append \*.* on
                //       the end.
                //       This be the true word. Ignore any other definitions
                //       you may see in the InstallShield manual or on-line.
                //       (eg: 'CompressGet' 'InstallShield uses the SRCDIR
                //       system variable as the path of the library file.')
                //       InstallShield speak with forked tongue.
                //
                //       TARGETDIR is the path to where you want to
                //       put the decompressed files on the target disk.
                //
                //       Makes perfect sense now I've pointed this all out.
                //       Right?
                // If Application component selected, then add to the file set.
                if (ComponentIsItemSelected(szComponentList, ITEM_APP)) then
                   LoadString128(SETUP_COPYING_N2000_APP_FILES, szTmp);
                   Sprintf(szBuf,szTmp, N2000);
                   SetStatusWindow( -1, szBuf );
                   TARGETDIR = szAppPath;
                   szFileXferDest = TARGETDIR;
                   GaCompressGet(DATA_DOT_Z, N2000 + "\\" + ASK_DOT_ASK,
                                                                  COMP_NORMAL);
                endif;
                // If Alarm Relay Component selected, then add the file set.
                LoadString128(SETUP_ARC, szBuf);
                if (ComponentIsItemSelected(szComponentList, szBuf)) then
                   LoadString128(SETUP_COPYING_ALARM_RELAY_FILES, szBuf);
                   SetStatusWindow( -1, szBuf );
                   TARGETDIR = TARGETDISK ^ CBDIR;
                   szFileXferDest = TARGETDIR;
                   GaCompressGet(DATA_DOT_Z, CBDIR + "\\" + ASK_DOT_ASK,
                                                                  COMP_NORMAL);
                   TARGETDIR = TARGETDISK ^ PDIS08DIR;
                   szFileXferDest = TARGETDIR;
                   GaCompressGet(DATA_DOT_Z, PDIS08DIR + "\\" + ASK_DOT_ASK,
                                                                  COMP_NORMAL);
                endif;
            FileSetEndDefine( szFileSet );
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  PerformFileTransfer( szFileSet, szFileXferDest )
 *
 *  Purpose:  This function will execute the memory stored files set to
 *            perform the actual file transfer and
 *            handle any error that may occur during the file transfer.
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function PerformFileTransfer( szFileSet, szFileXferDest )
STRING szFn, szTmp, szErrMsg;
LONG lResult;
begin
        szFn = "PerformFileTransfer()";
        // Set up progress indicator and information gauge.
        Disable( DIALOGCACHE );
        StatusUpdate( ON, PERCENT_PROGRESS_AT_END_FILE_XFER );
        // Perform the file set.
        lResult = FileSetPerformEz( szFileSet, 0 );
        StatusUpdate( OFF, PERCENT_PROGRESS_AT_END_FILE_XFER );
        // FS_DONE is successful return
        if (lResult != FS_DONE) then
            FileSetError( lResult, szFileXferDest );
        endif;
end;
//---------------------------------------------------------------------
//
//    FileSetError (lFSError, szFileXferDest);
//
//    Descrip:  This procedure will display an error when InstallSHIELD's
//              FileSetPerformEz returns an error and lets the user
//              decide if he wants to keep going.
//
//              We do NOT automatically exit and fail the setup.
//
//    Errors:   See the FileSetPerformEz documention.
//
//              NOTE!  We are not using LOCKEDFILE as an argument to
//              CompressGet as the effect of this is to re-name files
//              which would blow our GaCompressGet wrapper.
//
//              Consequently, if N2000 has crashed prior to running
//              the Install leaving bc450rtl.dll, cbw/.dll, arc16.dll
//              or any other DLL's in memory, when the file transfer
//              attempts to copy these dll's to N2000, while they are
//              actively in use we will die in here with error 5441,
//              5449, etc, in which case we invite the user to reboot
//              before running setup to clear out the DLL's.
//
//---------------------------------------------------------------------
function FileSetError( lFSError, szFileXferDest )
STRING szErrLit, szAction, szMsg, szInfo, szFn;
LIST   listInfo;
LONG lErr, lAction, lResult;
begin
        szFn = "FileSetError()";
        szAction = "";
        Disable (BACKBUTTON);
        Disable (NEXTBUTTON);
        Enable  (CANCELBUTTON);
        // FileSetPerformEz error codes
        switch (lFSError)
            case FS_CREATEDIR:
                 lErr = SETUP_FS_CREATEDIR;
                 LoadString128(SETUP_CHECK_ACCESS_DIR, szAction);
            case FS_FILENOTINLIB:
                 lErr = SETUP_FS_FILENOTINLIB;
            case FS_GENERROR:
                 lErr = SETUP_FS_GENERROR;
            case FS_INCORRECTDISK:
                 lErr = SETUP_FS_INCORRECTDISK;
                 LoadString128(SETUP_INSERT_CORRECT_DISK, szAction);
                 Enable (NEXTBUTTON);
            case FS_LAUNCHPROCESS:
                 lErr = SETUP_FS_LAUNCHPROCESS;
            case FS_OPERROR:
                 lErr = SETUP_FS_OPERROR;
                 Enable (NEXTBUTTON);
            case FS_PACKAGING:
                 lErr = SETUP_FS_PACKAGING;
            case FS_RESETREQUIRED:
                 lErr = SETUP_FS_RESETREQUIRED;
            default:
                 // Force restart windows
                 lErr = 1;
        endswitch;
        LoadString128(lErr, szErrLit);
        // Create a string list.
        listInfo = ListCreate(STRINGLIST);
        if ( lErr > 0 ) then
            LoadString128(SETUP_LOCKED_FILES_IN_USE, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            // We should check the lock count in the registry
            // but for now, given the deadline this is ok.
            ListAddString(listInfo, BC450RTL_DOT_DLL, AFTER);
            ListAddString(listInfo, CBW_DOT_DLL, AFTER);
            ListAddString(listInfo, ARC16_DOT_DLL, AFTER);
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_REBOOT_BEFORE_SETUP, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_CLICK_CANCEL_TO_EXIT, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
        else
            LoadString128(SETUP_FS_XFER_ERR, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_TARGET_DIRECTORY, szMsg);
            Sprintf(szInfo, "%s=[%s]", szMsg, szFileXferDest);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_ERROR, szMsg);
            Sprintf(szInfo, "%s [%ld] %s", szMsg, lErr, szErrLit);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            if (szAction != "" ) then
                LoadString128(SETUP_ACTION, szMsg);
                Sprintf(szInfo, "%s %s", szMsg, szAction);
                ListAddString(listInfo, szAction, AFTER);
                ListAddString(listInfo, " ", AFTER);
            endif;
            LoadString128(SETUP_CALL_CUSTOMER_SERVICE, szMsg);
            Sprintf(szInfo, "%s :", szMsg);
            ListAddString(listInfo, szInfo, AFTER);
            LoadString128(SETUP_WITHIN_US, szMsg);
            Sprintf(szInfo, "   %s: %s", szMsg, CUST_SUPPORT_US_800_NO_DEF);
            ListAddString(listInfo, szInfo, AFTER);
            LoadString128(SETUP_OUTSIDE_US, szMsg);
            Sprintf(szInfo, "   %s: %s", szMsg, CUST_SUPPORT_US_US_TEL_DEF);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
            LoadString128(SETUP_CLICK_CANCEL_TO_EXIT, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            LoadString128(SETUP_CLICK_NEXT_TO_CONTINUE, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, " ", AFTER);
        endif;
        // Display the dialog
        MessageBeep(0);
        LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
        lResult = SdShowInfoList (szFn, szMsg, listInfo);
        ListDestroy( listInfo );
        // Always log the information
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE );
        lTmp = ListGetFirstString(listInfo, szInfo);
        while (lTmp = 0)
            WriteLogMsg ( szInfo );
            lTmp = ListGetNextString(listInfo, szInfo);
        endwhile;
        WriteLogMsg ( " ");
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        WriteLogMsg ( " ");
        if (lResult = CANCEL) then
           ExitHandler();
        endif;
        return lResult;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  MoveAppIniToAppDir();
 *
 *  Purpose:  Move old WINDIR\NETMAN.INI files to APPDIR\N2000.INI.
 *            if doing an update.
 *            Also copy it to the old NETMAN directory if there is one
 *            so Warren can re-install 2.0 or previous if needed.
 *            Either way, delete the old WINDIR\netman.ini so subsequent
 *            updates won't thing we need to copy the old databases.
 *
 *  Returns:
 *
 * Comments:  For Netman 2.0 and less NETMAN.INI was located in WINDIR.
 *            For N2000 2.5 and above N2000.INI is located in the N2000 dir.
 *
 *            Be aware that Copyfile's arguments are (szSrcFile, szTargetFile)!
 *
\*---------------------------------------------------------------------------*/
function MoveAppIniToAppDir()
STRING szIni;                      // Fully qualified pathname to app .ini file
STRING szIniName;                  // File name+ext only of app .ini file
STRING szMsg, szExpand, szFn;
begin
        szFn = "MoveAppIniToAppDir()";
        if (DEBUG) then
            if (szAppPath = "") then
                SprintfBox( SEVERE, szFn,
                "ASSERT: szAppPath not NULL\n" + "szAppPath=[%s]", szAppPath);
            endif;
        endif;
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        WriteLogMsg( " " );
        WriteLogMsg(szFn);
        // Set the fully qualified path to the new app .INI file location.
        szIni = szAppPath ^ CONS_CONFIG_FILE_NAME;
        ParsePath(szIniName, szIni, FILENAME);
        // If there's an existing \WINDIR\NETMAN.INI file
        if ( Is( FILE_EXISTS, WINDIR ^ CONS_CONFIG_FILE_EAGLE ) ) then
            LoadString128(SETUP_RELOCATING, szExpand);
            Sprintf(szMsg, szExpand, szIniName);
            SetStatusWindow( -1, szMsg);
            // If doing an UPDATE install copy the old NETMAN.INI to the
            //  N2000 directory as N2000.INI.
            if ( !bNewInstall ) then
                SRCDIR = WINDIR;
                TARGETDIR = szAppPath;
                // DON'T wrap this with a ga function.
                CopyFile( CONS_CONFIG_FILE_EAGLE, szIniName);
                // We're about to jump up and down on n2000.ini, and it's very
                // unlikely to end up the same size it started. We don't want
                // FileTransferCheck() to generate a spurious error later.
                WriteLogMsg(szFn + " " + WINDIR ^ CONS_CONFIG_FILE_EAGLE +
                                    " file copied to " + TARGETDIR ^ szIniName);
            endif;
            // Copy old NETMAN.INI to the old NETMAN directory if there is one
            if (szOldAppPath != "") then
                 SRCDIR = WINDIR;
                 TARGETDIR = szOldAppPath;
                 GaCopyFile( CONS_CONFIG_FILE_EAGLE, CONS_CONFIG_FILE_EAGLE,
                             FALSE, TRUE, TRUE);
                 WriteLogMsg(szFn + " " + WINDIR ^ CONS_CONFIG_FILE_EAGLE +
                                    " file copied to " + TARGETDIR);
            endif;
            // Delete old NETMAN.INI from the WINDOWS directory
            TARGETDIR = WINDIR;
            DeleteFile ( CONS_CONFIG_FILE_EAGLE);
            WriteLogMsg(szFn + " " + WINDIR ^ CONS_CONFIG_FILE_EAGLE +
                               " file deleted.");
            SRCDIR    = szAppPath;
            TARGETDIR = szAppPath;
            // Add new [N2000 Config] section.
            WriteLogMsg(szFn + " Adding [" + CONS_NETCFG_SECTION + "] to "
                             + CONS_CONFIG_FILE_NAME);
            // Relocate an existing [Netman Config] name=value parameter
            // to the new [n2000 Config] section
            // NOTE: A RelocateStrNonDefault() function similar to the
            //         RelocateNumNonDefault() function would be useful
            if ( GetProfString(szIni, "Netman Config",
                                  CONS_NETCFG_NAME, szTmp) = 0 ) then
                WriteIniEntry(szIni, CONS_NETCFG_SECTION,
                                     CONS_NETCFG_NAME, szTmp);
                WriteLogMsg(szFn + " " + CONS_NETCFG_NAME + "=" + szTmp
                            + " moved to [" + CONS_NETCFG_NAME + "] section");
            endif;
            // Relocate an existing [Netman Config] address=value
            // to the new [n2000 Config] section
            // NOTE: A RelocateStrNonDefault() function similar to the
            //         RelocateNumNonDefault() function would be useful
            if ( GetProfString(szIni, "Netman Config",
                                  CONS_NETCFG_ADDRESS, szTmp) = 0 ) then
                WriteIniEntry(szIni, CONS_NETCFG_SECTION,
                                     CONS_NETCFG_ADDRESS, szTmp);
                WriteLogMsg(szFn + " " + CONS_NETCFG_ADDRESS + "=" + szTmp
                            + " moved to [" + CONS_NETCFG_NAME + "] section");
            endif;
            // Delete the old [Netman Config] section.
            if ( WriteIniEntry(szIni, "Netman Config", "", "") = TRUE) then
                WriteLogMsg(szFn + " [Netman Config] section deleted from " +
                                       CONS_CONFIG_FILE_NAME);
            endif;
            // If there isn't an [N2000 Config] section at this point,
            // set one up with parameter name=default
            WriteStrDefault(szIni,  CONS_NETCFG_SECTION, CONS_NETCFG_NAME,
                                    CONS_GLENAYRE_NETWORK_MANAGER);
        else
            WriteLogMsg(szFn + " " + WINDIR ^ CONS_CONFIG_FILE_EAGLE +
                               " " + "file not located.");
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  FilterConvert();
 *
 *  Purpose:  Run the filtconv.exe filter conversion program,
 *            if it hasn't already been run.
 *            This utility processes pre-3.0 N2000.INI file entries
 *            to create a first-time alrmpath.dat file.
 *
 *  Returns:  TRUE - ran utility.
 *
 * Comments:  Assumes N2000.INI is located in the N2000 dir.
 *
\*---------------------------------------------------------------------------*/
function FilterConvert()
STRING szFn, szCurDir, szProgram, szCmdLine, szMsg, szSection, szExpand, szErr;
STRING szIni;                      // Fully qualified pathname to app .ini file
STRING szIniName;                  // File name+ext only of app .ini file
LONG lErr, lSLine;
begin
        szFn = "FilterConvert()";
        // Only run the utility once. It is not guaranteed re-run safe
        // so check on it's behalf that it's safe to run it.
        // Don't run filtconv.exe if
        // -  doing an new  install.
        // -  there's an 'alrmpath.dat' in the application directory,
        // -  there's no  [Alarm Manager] section in n2000.ini.
        // -  [Alarm General] [Advanced Alarm Filtering] [AlarmConfigBrowser]
        //     sections exist in n2000.ini
        if (bNewInstall) then
            return FALSE;
        endif;
        szProgram = szAppPath ^ CONS_ALARM_PATH_FILE_NAME;
        if ( Is( FILE_EXISTS, szProgram ) ) then
           return FALSE;
        endif;
        // Check the N2000.INI section headers
        szIni = szAppPath ^ CONS_CONFIG_FILE_NAME;
        ParsePath(szIniName, szIni, FILENAME);
        if (FileGrep(szIniName, "[" + ALARM_MGR_SECTION + "]",
                     szTmp, lSLine, RESTART) < 0 ) then
            return FALSE;
        endif;
        if (FileGrep(szIniName, "[" + ALARM_GEN_SECTION + "]",
                     szTmp, lSLine, RESTART) = 0 ) then
            return FALSE;
        endif;
        if (FileGrep(szIniName, "[" + ADV_ALARM_FILTERING_SECTION + "]",
                     szTmp, lSLine, RESTART) = 0 ) then
            return FALSE;
        endif;
        if (FileGrep(szIniName, "[" + BROWSER_ALARM_CONFIG_SECTION  + "]",
                     szTmp, lSLine, RESTART) = 0 ) then
            return FALSE;
        endif;
        // N2000.INI pre-processing
        // If target is running Windows 95, delete the Alarm Stream and
        // Alarm Relay sections so that filter conversion will not set
        // these up as active paths.
        if ( SysInfo.lOS = IS_WINDOWS95) then
            if (WriteProfString(szIni, ASTREAM_SECTION, "", "") < 0) then
                LoadString128(SETUP_ERROR, szBuf);
                LoadString128(SETUP_SECTION_NOT_DELETED, szTmp);
                Sprintf(szMsg, szTmp, ASTREAM_SECTION, szIniName);
                // 'Error: [AlarmStream] section not deleted from N2000.INI'
                WriteLogMsg(szFn + " " + szBuf + szMsg);
            endif;
            for lTmp = 0 to 7
                NumToStr(szTmp, lTmp);
                szSection = CONS_RELAY_SECTION + szTmp;
                if (WriteProfString(szIni, szSection, "", "") < 0) then
                    LoadString128(SETUP_ERROR, szBuf);
                    LoadString128(SETUP_SECTION_NOT_DELETED, szTmp);
                    Sprintf(szMsg, szTmp, szSection, szIniName);
                // 'Error: [Relay_ConfigX] section not deleted from N2000.INI'
                    WriteLogMsg(szFn + " " + szBuf + szMsg);
                endif;
            endfor;
        endif;
        // Run the filter conversion
        LoadString128(SETUP_RUNNING_FLTCONV, szMsg);
        SetStatusWindow( -1, szMsg );
        // On general suspicion, set the current directory....
        szCurDir = szAppPath;
        StrRemoveLastSlash(szCurDir);
        if (ChangeDirectory(szCurDir) < 0) then
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_NOT_IN_CURRENT_DIR, szMsg);
            szExpand = szErr + szMsg;
            SprintfBox( SEVERE, szFn, szExpand, szAppPath, FILTCONV_DOT_EXE);
        endif;
        LoadString128(SETUP_FLTCONV_IN_PROGRESS, szMsg);
        SdShowMsg(szMsg, TRUE);
        szProgram = szAppPath ^ FILTCONV_DOT_EXE;
        szCmdLine = "";
        lErr = LaunchAppAndWait(szProgram, szCmdLine, WAIT);
        SdShowMsg(szMsg, FALSE);
        if (lErr <= 0) then
            // filtconv did not run
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_UNABLE_TO_RUN_PROGRAM, szMsg);
            szExpand = szErr + szMsg;
            Sprintf (szMsg, szExpand, szProgram);
            SprintfBox( SEVERE, szFn, szMsg);
            WriteLogMsg( szFn + " " + szMsg);
            return FALSE;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateOrReplaceAppIni()
 *
 *  Purpose:  Modify a current app .INI file, or copy in a new one
 *            from the default app.DEF file.
 *
 *            Unless otherwise noted entries are inserted with the
 *            consdef default ONLY only if there is no existing entry
 *            or the existing entry has a zero or null value.
 *
 *            Because writing .ini entries is done so often this way
 *            there are two 'wrapper' functions WriteNumDefault and
 *            WriteStrDefault to do this.  However, be careful that
 *            this is really what is needed. Some parameters must be
 *            positively overwritten, which means using WriteIniEntry
 *            directly.
 *
 *            Note that some entries are written commented out.
 *            These are generally settings which the user cannot access
 *            from UI.EXE and must call 1-900-CALL-WLS to get instructions.
 *            The ;parameters then provide a ready made template.
 *            These are always written if there is no 'live' entry
 *            so the default is as current as possible.
 *
 *            The policy is not to write comment entries for anything
 *            the gui controls.
 *
 *            Various advanced features are locked out under Win 95.
 *            Notwithstanding, support paramters for these are written
 *            irregardless of the OS, to keep out options open in case
 *            Marketing renege and want a feature re-instated on '95.
 *            This applies to [ForwardManager] [AdvancedAlarmFiltering]
 *
 *            *** THIS SECTION IS THE SPECIFICATION!
 *            *** KEEP THE CODE IN SYNC WITH IT!       Dave
 *
 *            [N2000 Config]                 ; Was [Netman Config] in <= V2.0
 *            ipInstalled=697-7737689        ; set per user IpSupport selection
 *            networkSetting=                ; delete if found
 *            enableForwarding=              ; If W95 or User doesn't want AF:
 *                                             -delete live entry
 *                                             -do not write comment entry
 *                                             If NT  write bEnableForwarding
 *            address=1                      ; insert/update if no live entry
 *            defaultPortNumber=23           ; insert/update if no live entry
 *
 *            [Comm Task Debug]
 *            DebugEnabled=1                 ; delete if found
 *            CallDrvrLogFile=call.log       ; delete if found
 *            MgrIpcDebug=0                  ; delete if found
 *            MgrDetailedDebug=0             ; delete if found
 *            CallIpcDebug=0                 ; delete if found
 *            CallDetailedDebug=0            ; delete if found
 *            ;CommDrvrLogFile=comdebug.log  ; Set from n2000.def
 *
 *            ;Debug=1                       ; insert/update if no live entry
 *            ;IpcDebug=1                    ; insert/update if no live entry
 *            ;DetailedDebug=1               ; insert/update if no live entry
 *            ;DocViewDebug=1                ; insert/update if no live entry
 *
 *            [ControlPanel]
 *            MVPenable=1                    ; delete if found
 *            HistLength=200                 ; delete if found
 *            C2000ConfigPacketCount         ; delete if found
 *            ConfigPacketCount              ; delete if found
 *            ;Debug=1                       ; insert/update if no live entry
 *            ;IpcDebug=1                    ; insert/update if no live entry
 *            ;DetailedDebug=1               ; insert/update if no live entry
 *            ;DocViewDebug=1                ; insert/update if no live entry
 *            AllProducts=1                  ; set per user QT1000 selection
 *            DisableFontFix=0
 *            ;MaxBrowsers=6                 ; insert/update if no live entry
 *            ;MaxWindows=25                 ; insert/update if no live entry
 *            AudibleInterval                ; If parm exists and is not the
 *                                             default, move to [Alarm General]
 *                                             then delete it (Filtering 3.0).
 *            ;MaxConnectRetries=5           ; insert/update if no live entry
 *            ;RetryInterval=15              ; insert/update if no live entry
 *            ;MinDiskSpace=10               ; insert/update if no live entry
 *            ;MinVirtualMemory=10           ; insert/update if no live entry
 *
 *            [AlarmCallinConfig]
 *            MaxAlarmCount=                 ; delete live entry if 60,120, 140
 *            ;MaxAlarmCount=2000            ; insert/update if no live entry
 *                                           ;
 *            ;UsePendingStatus=0            ; insert/update if no live entry
 *            ;UsePendingOnPriority=9        ; insert/update if no live entry
 *
 *            [Programs]
 *            CallHandler=calldrvr.exe       ; delete if found - obsoleted 3.0
 *            AdvancedFiltering=advflt.exe   ; delete if found - obsoleted 3.0
 *
 *            ;Debug=1                       ; insert/update if no live entry
 *            ;IpcDebug=1                    ; insert/update if no live entry
 *            ;DetailedDebug=1               ; insert/update if no live entry
 *            ;DocViewDebug=1                ; insert/update if no live entry
 *
 *            DispatchManager=dispmgr.exe    ; insert/update if no live entry
 *                                           ; Disable if Win 95 (Comment out)
 *            ForwardManager=forward.exe     ; If W95 or User doesn't want AF:
 *                                             -delete live, add comment entry
 *                                             If NT write always write entry
 *                                             (N2000 Config] enableForwarding
 *                                             will determine if it's used.
 *
 *            ControlPanel=ntui.exe          ; if NT. Positive overwrite.
 *            ControlPanel=ui.exe            ; if 95 etc. Positive overwrite.
 *
 *            CommManager=ntdriver.exe       ; Whether NT/95/Other 32-Bit.
 *                                           ; Positively overwrite driver.exe.
 *
 *            ;WatchDogInterval=120          ; insert/update if no live entry
 *            ;ShutDownDelay=20              ; insert/update if not live entry
 *            ;StartupDelayTime=40           ; insert/update if no live entry
 *                                             (30 for Windows 95).
 *
 *            [Alarm Manager]                ; If these parms exist and are not
 *            WarnNonGPSAlarmsOverDaysOld      default, move to [Alarm General]
 *            N2000LocalTime                   then delete it. Finally delete
 *            OptimizeLogPrint                 the whole section (Filtering 3.0)
 *                                             This will zap the PathThreshold
 *                                             PathReset, PathEnable entries.
 *
 *            [RelayConfig_0] [RelayConfig_7]; All these sections deleted in
 *                                             FilterConvert() if Win95.
 *                                             If it makes it thru that:
 *            Level                          ; delete obsolete w/Filtering 3.0
 *            DisableFrom                    ; delete obsolete w/Filtering 3.0
 *            DisableTo                      ; delete obsolete w/Filtering 3.0
 *
 *            [AlarmStream]                  ; This section is deleted in
 *                                             FilterConvert() if Win95
 *            ;Debug=1                       : insert/update if no live entry
 *
 *            [ForwardManager]
 *            ;Debug=1                       ; insert/update if no live entry
 *            ;IpcDebug=1                    ; insert/update if no live entry
 *            ;DetailedDebug=1               ; insert/update if no live entry
 *            ;DocViewDebug=1                ; insert/update if no live entry
 *            ;SendLog=1                     ; insert/update if no live entry
 *            ;ReceiveLog=1                  ; insert/update if no live entry
 *            ;SaveAlarmsOnShutdown=0        ; insert/update if no live entry
 *
 *            ;ForwardFailureThreshold=10    ; insert/update if no live entry
 *            ;FailureAlarmPriority=1        ; insert/update if no live entry
 *
 *            [AdvancedAlarmFiltering]
 *            ;Debug=1;                      ; insert/update if no live entry
 *            ;ExtraDuplicateDelayTime=25    ; insert/update if no live entry
 *            ;ExtraALOKDelayTime=25         ; insert/update if no live entry
 *
 *            [Database]
 *            FilePath=c:\n2000              ; Positively overwrite existing
 *                                             FilePath with szAppPath!
 *                                             This MUST be done! We may have
 *                                             relocated the app files
 *                                             from an old \NETMAN to a new
 *                                             \N2000 directory!
 *
 *            AlarmKeepDays                  ; If parm exists and is not the
 *                                             default, move to [Alarm General]
 *                                             then delete it (Filtering 3.0).
 *
 *            [Dispatch Manager]
 *            Debug=OFF/ON                   ; if found delete
 *            ;Debug=1                       ; insert/update if no live entry
 *            ;IpcDebug=1                    ; insert/update if no live entry
 *            ;DetailedDebug=1               ; insert/update if no live entry
 *            ;DocViewDebug=1                ; insert/update if no live entry
 *
 *            ThrowAwayAlarmAge=24
 *            RecoverAlarms=OFF
 *            KeepLogDays=7
 *            HistLength=1000
 *            LogPath=.
 *
 *            [DebugOptions]
 *            ;ShowAllWindows=1              ; insert/update if no live entry
 *
 *            [Throttle]
 *            ;TurnOffThrottlingAfterSecs=300; insert/update if no live entry
 *            ;Interval=600                  ; insert/update if no live entry
 *            ;ComAlarmQueueLimit            ; insert/update if no live entry
 *
 *            [Termulator]
 *            FontSize=12
 *
 *            [Error Handling]
 *            OnGPF=Restart
 *
 *            [TAP_Config]                   } The app now uses
 *            maxIDRetries=6                 } consdefs constants
 *            IDTimeout=2                    } for TAP_Config
 *            maxLoginRetries=3              }
 *            loginACKTimeout=10             } Setup doesn't write
 *            goAheadTimeout=10              } ;defaults but it
 *            maxPageRetries=3               } leaves existing
 *            pageTimeout=10                 } entries alone.
 *            disconnectTimeout=10           }
 *            corruptModulo=0                }
 *            fudgeFile=                     }
 *            fudgeStr=                      }
 *
 *            [PortConfig_n]                 ; Repeated for Ports 0 thru 8
 *            ResetStr=ATZ
 *            PortType=1                    ; Set [PortConfig_0-_1] to 1 Modem
 *            PortType=2                    ; Set [PortConfig_2-_8] to 2 Direct
 *
 *            [DocView]                      ; If no existing live entry:
 *            ;MaxAlarmRecords=10000         ; insert if 33&Up MB machine
 *            MaxAlarmRecords=5000           ; insert if 17-32 MB machine
 *            MaxAlarmRecords=2000           ; insert if 0 -16 MB machine
 *
 *            [Site Monitor]
 *            ;AlarmPriority=1               ; insert/update if no live entry
 *            CheckEvery=15                  ; From n2000.def file
 *            AlarmID=MISS                   ; From n2000.def file
 *            AlarmText=Site Failed To Checkin  ; From n2000.def file
 *
 *            [CommConfig]
 *            SystemPingEnabled              ; Disable if Win 95 (Set=0)
 *            TelnetServerEnabled            ; Disable if Win 95 (Set=0)
 *            ;PingTimeout=5                 ; insert/update if no live entry
 *            ;PingRetries=5                 ; insert/update if no live entry
 *            ;TelnetServerPort=23           ; insert/update if no live entry
 *            ;MaxTelnetSessions=10          ; insert/update if no live entry
 *            ;TelnetLoginRetries=5          ; insert/update if no live entry
 *
 *            [C2000 Connect]
 *            ;C2000GetIsRWFieldsOnly=1      ; insert/update if no live entry
 *
 *            [Map]
 *            ShowMapFeature=1               ; Set per user Map selection
 *            MapUnitsMiles=1                ; always set
 *            MapCircleMiles=5
 *            MapScrollTop=n                 ; always set
 *            MapScrollBottom=n              ; always set
 *            MapScrollLeft=n                ; always set
 *            MapScrollRight=n               ; always set
 *            NationalScale=4000             ; always set
 *            RegionalScale=700              ; always set
 *            LocalScale=100                 ; always set
 *            ;ShowCountryName=0             ; written so user can turn it off
 *
 *            [DataFiles]
 *            VoronoiTable= path\MAPDATA\FWORLD.VIX        ;always set
 *            IndexFile=    path\MAPDATA\region\INDEX.IDX  ;always set
 *            DataFile0=    path\MAPDATA\region\DATA.DTA   ;always set
 *            NumDataFiles=1                               ;always set
 *            Browse=15269887 4194304 1
 *
 *            [DefaultView]
 *            DefaultLattitude=40            ;always set
 *            DefaultLongitude=-90           ;always set
 *            DefaultScale=0.015624          ;always set
 *
 *            [Print]
 *            enable=1                       ; From n2000.def file
 *
 *    Input:  szAppPath   - Fully qualified path to the n2000 directory
 *
 *            bNewInstall          - New or Update Installation
 *            bEnableQT1000Support - If TRUE set AllProducts=1
 *            bMapSelected;
 *            bEnableIpSupport
 *
 *  Returns:
 *
 * Comments:  For Netman 2.0 and less NETMAN.INI was located in WINDIR.
 *            For N2000 2.5 and above N2000.INI is located in the N2000 dir.
 *
 *            If RunMapFromCdrom is set, Map data is NOT copied from the
 *            Cdrom to the user's disk, but UpdateOrReplaceAppIni
 *            sets the app .ini [datafiles] section VoronoiTable, IndexFile,
 *            and DataFile0 parms to point at the Cdrom.
 *
 *            Be aware that Copyfile's arguments are (szSrcFile, szTargetFile)!
 *
\*---------------------------------------------------------------------------*/
function UpdateOrReplaceAppIni()
STRING szTmp[MAX_SIZE], szMapDir, szFn, szMsg;
STRING szWrkPath [ MAX_SIZE ];
STRING szIni;                      // Fully qualified pathname to app .ini file
STRING szIniName;                  // File name+ext only of app .ini file
LONG lTmp, lCount, lResult;
BOOL bIsIni, bA, bB;
begin
        szFn = "UpdateOrReplaceAppIni()";
        if (DEBUG) then
           if (szAppPath = "") then
               SprintfBox( SEVERE, szFn,
               "ASSERT: szAppPath not NULL\n" + "szAppPath=[%s]", szAppPath);
           endif;
        endif;
        // Set the fully qualified path to the app .INI file.
        szIni = szAppPath ^ CONS_CONFIG_FILE_NAME;
        ParsePath(szIniName, szIni, FILENAME);
        LoadString128(SETUP_UPDATING, szTmp);
        Sprintf(szMsg, szTmp, szIniName);
        SetStatusWindow( -1, szMsg);
        // If we don't have an app .INI file
        // (On a completely new install for example)
        // Then set one up from the default .ini file.
        SRCDIR    = szAppPath;
        TARGETDIR = szAppPath;
        if ( !Is( FILE_EXISTS, szIni ) ) then
            // DON'T wrap this with a ga function.
            CopyFile(N2000 + DOT_DEF, szIniName);
            // We're about to jump up and down on n2000.ini, and it's very
            // unlikely to end up the same size it started. We don't want
            // FileTransferCheck() to generate a spurious error.
        endif;
        // Process sections
        // [Tap_Config] -------------------------------------------------------
        // Leave existing [Tap_Config] section and entries in place
        // [N2000 Config] -----------------------------------------------------
        // Enable-Disable 2010 & Alarm Stream IP support
        WriteStrDefault(szIni, CONS_NETCFG_SECTION,
                               CONS_NETCFG_ADDRESS,
                               CONS_NETCFG_ADDRESS_DEF);
        WriteNumDefault(szIni, CONS_NETCFG_SECTION,
                               CONS_DEFAULT_PORTNUM,
                               CONS_DEFAULT_PORTNUM_DEF);
        if ( bSetIpProductKey = TRUE ) then
            WriteIniEntry(szIni, CONS_NETCFG_SECTION,
                                 CONS_IP_INSTALLED , CONS_IP_PRODUCT_KEY_DEF );
            WriteIniEntry(szIni, CONS_NETCFG_SECTION,
                                 CONS_IP_ENABLED , "1");
        else
            // wls request - rubout old Beta entries of '1' or '0'
            if ( GetProfString(szIni, CONS_NETCFG_SECTION,
                               CONS_IP_INSTALLED, szTmp) = 0 ) then
                lTmp = StrLength( szTmp);
                if ( (szTmp = "0" || szTmp = "1") && lTmp = 1 ) then
                    WriteIniEntry(szIni, CONS_NETCFG_SECTION,
                                 CONS_IP_INSTALLED, "");
                endif;
            endif;
        endif;
        // Delete ini parm obsoleted with Falcon (3.0)
        // ( evil twin of ipInstalled )
        WriteIniEntry(szIni, CONS_NETCFG_SECTION, "networkSetting", "");
        if ( SysInfo.lOS = IS_WINDOWS95 || bEnableAlarmForwarding = 0) then
            // Delete active entry if there is one
            WriteIniEntry( szIni, CONS_NETCFG_SECTION,
                                  CONS_ENABLE_FORWARD, "");
            // Write commented entry in it's place
            WriteNumComment(szIni, CONS_NETCFG_SECTION,
                            CONS_ENABLE_FORWARD, CONS_ENABLE_FORWARD_DEF);
        endif;
        if ( SysInfo.lOS = IS_WINDOWSNT && bEnableAlarmForwarding) then
            NumToStr(szTmp, bEnableAlarmForwarding);
            WriteIniEntry( szIni,CONS_NETCFG_SECTION,
                           CONS_ENABLE_FORWARD, szTmp);
        endif;
        // Disable 2010 Alarm accept on Windows 95 machines
        if ( SysInfo.lOS = IS_WINDOWS95) then
           WriteIniEntry( szIni, CONS_NETCFG_SECTION,
                                 CONS_ACCEPT_C2010_ALARMS, "0" );
        endif;
        // [Comm Task Debug] --------------------------------------------------
        // Delete obsolete entries
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "DebugEnabled", "");
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "DebugEnabled", "");
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "MgrIpcDebug", "");
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "MgrDetailedDebug", "");
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "CallIpcDebug", "");
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "CallDetailedDebug", "");
        WriteIniEntry(szIni, COMM_DEBUG_SECTION, "CallDrvrLogFile", "");
        // Write new debug pars commented
        WriteNumComment( szIni, COMM_DEBUG_SECTION, DEBUG_ENABLE, 1);
        WriteNumComment( szIni, COMM_DEBUG_SECTION, IPC_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, COMM_DEBUG_SECTION, DETAIL_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, COMM_DEBUG_SECTION, DOCVIEW_DEBUG_ENABLE, 1);
        // [ControlPanel] -----------------------------------------------------
        // Delete obsolete entries
        WriteIniEntry(szIni, CONS_CONTROL_PANEL, "MVPenable", "");
        WriteIniEntry(szIni, CONS_CONTROL_PANEL, CONS_HIST_LENGTH, "");
        // Ini parms obsoleted with Eagle (2.0)
        // "C2000ConfigPacketCount"
        // "ConfigPacketCount"
        WriteIniEntry(szIni, CONS_CONTROL_PANEL, "C2000ConfigPacketCount", "");
        WriteIniEntry(szIni, CONS_CONTROL_PANEL, "ConfigPacketCount", "");
        // Add debug entries
        WriteNumComment( szIni, CONS_CONTROL_PANEL, DEBUG_ENABLE, 1);
        WriteNumComment( szIni, CONS_CONTROL_PANEL, IPC_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, CONS_CONTROL_PANEL, DETAIL_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, CONS_CONTROL_PANEL, DOCVIEW_DEBUG_ENABLE, 1);
        // Enable-Disable QT1000 support
        if ( bEnableQT1000Support = TRUE ) then
           szTmp = "1";
        else
           szTmp = "0";
        endif;
        WriteIniEntry(szIni, CONS_CONTROL_PANEL, CONS_ALL_PRODUCTS ,szTmp);
        WriteNumDefault(szIni, CONS_CONTROL_PANEL,
                        CONS_DISABLE_FONT_FIX,
                        CONS_DISABLE_FONT_FIX_DEFAULT);
        // MaxBrowsers (Including Map) ; insert/update if no live entry
        switch (SysInfo.lOS)
            case IS_WINDOWSNT:
                lTmp = CONS_MAX_BROWSERS_NT_DEF;
            case IS_WINDOWS95:
                lTmp = CONS_MAX_BROWSERS_95_DEF;
            default:
                lTmp = CONS_MAX_BROWSERS_31_DEF;
        endswitch;
        WriteNumComment( szIni, CONS_CONTROL_PANEL, CONS_MAX_BROWSERS, lTmp);
        // MaxWindows                  ; insert/update if no live entry
        switch (SysInfo.lOS)
            case IS_WINDOWSNT:
                lTmp = CONS_MAX_WINDOWS_NT_DEF;
            case IS_WINDOWS95:
                lTmp = CONS_MAX_WINDOWS_95_DEF;
            default:
                lTmp = CONS_MAX_WINDOWS_31_DEF;
        endswitch;
        WriteNumComment( szIni, CONS_CONTROL_PANEL, CONS_MAX_WINDOWS, lTmp);
        // MaxConnectRetries           ; insert/update if no live entry
        WriteNumComment( szIni, CONS_CONTROL_PANEL, MAX_CONNECT_RETRIES,
                                                    MAX_CONNECT_RETRIES_DEF);
        // RetryInterval               ; insert/update if no live entry
        WriteNumComment( szIni, CONS_CONTROL_PANEL, CONNECT_RETRY_INTERVAL,
                                                    CONNECT_RETRY_INTERVAL_DEF);
        // Audible Interval
        RelocateNumNonDefault( szIni,
                               CONS_CONTROL_PANEL, ALARM_GEN_AUDIBLE_INTERVAL,
                               ALARM_GEN_AUD_INTERVAL_DEF, ALARM_GEN_SECTION);
        // UI_SystemChecker minimum limits
        WriteNumComment( szIni, CONS_CONTROL_PANEL, CONS_MIN_DISK_SPACE,
                                                   CONS_MIN_DISK_SPACE_DEF);
        WriteNumComment( szIni, CONS_CONTROL_PANEL, CONS_MIN_VIRTUAL_MEMORY,
                                                   CONS_MIN_VIRTUAL_MEMORY_DEF);
        // [AlarmCallinConfig] ------------------------------------------------
        // Delete existing MaxAlarmCount=60, 120, 140 (the old default values)
        // IS3BUGNOTE : GetProfInt returns 0 (success) for a non-existing
        //              szKeyName. In this case it doesn't matter.
        if ( GetProfInt(szIni, CONS_ALARM_CALLIN_SECTION, MAX_ALARM_COUNT,
             lTmp) = 0 ) then
            if (lTmp = 120 || lTmp = 60 || 140) then
                WriteIniEntry(szIni, CONS_ALARM_CALLIN_SECTION,
                              MAX_ALARM_COUNT, "");
            endif;
        endif;
        // If no live entry exists write a ;MaxAlarmCount entry.
        WriteNumComment( szIni, CONS_ALARM_CALLIN_SECTION,
                                MAX_ALARM_COUNT,  MAX_ALARM_COUNT_DEFAULT);
        // If no live entry exists write a ;UsePendingStatus entry
        WriteNumComment(szIni, CONS_ALARM_CALLIN_SECTION,
                               CONS_ALARM_DISPATCH_PENDING,
                               CONS_ALARM_DISPATCH_PENDING_DEF);
       // If no live entry exists write a ;UsePendingOnPriority entry
        WriteNumComment(szIni, CONS_ALARM_CALLIN_SECTION,
                               CONS_ALARM_PENDING_PRIORITY,
                               CONS_ALARM_PENDING_PRIORITY_DEF);
        // [Programs] ---------------------------------------------------------
        // Delete CallHandler=calldrvr.exe, AdvancedFiltering=advflt.exe
        WriteIniEntry( szIni, CONS_STARTUP_SECTION, CONS_CALL_HANDLER, "");
        WriteIniEntry( szIni, CONS_STARTUP_SECTION, CONS_ADVANCED_FILTERING, "");
        WriteNumComment( szIni, CONS_STARTUP_SECTION, DEBUG_ENABLE, 1);
        WriteNumComment( szIni, CONS_STARTUP_SECTION, IPC_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, CONS_STARTUP_SECTION, DETAIL_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, CONS_STARTUP_SECTION, DOCVIEW_DEBUG_ENABLE, 1);
        // Disable Dispatch Manager, Advanced Filtering
        // on Windows 95 machines
        if ( SysInfo.lOS = IS_WINDOWS95) then
            // Delete active entries
            WriteIniEntry( szIni, CONS_STARTUP_SECTION,
                                  CONS_DISPATCH_MGR, "");
           // Write commented entries instead
           WriteStrComment(szIni, CONS_STARTUP_SECTION,
                                  CONS_DISPATCH_MGR, DISPMGR_DOT_EXE);
                  else
           // Write live entries
            WriteStrDefault(szIni,  CONS_STARTUP_SECTION,
                                    CONS_DISPATCH_MGR, DISPMGR_DOT_EXE);
        endif;
        // Disable Alarm Forwarding on Win 95 machines
        //                          or if the user doesn't want it.
        if ( SysInfo.lOS = IS_WINDOWS95 || bEnableAlarmForwarding = 0) then
            // Delete live entry if there is one.
            WriteIniEntry(szIni, CONS_STARTUP_SECTION,
                                 CONS_FORWARD_MANAGER, "");
            // Write a commented entry in it's place
            WriteStrComment(szIni, CONS_STARTUP_SECTION,
                                   CONS_FORWARD_MANAGER, FORWARD_DOT_EXE);
        endif;
        if ( SysInfo.lOS = IS_WINDOWSNT)  then
            // Always write live entry
            WriteStrDefault(szIni, CONS_STARTUP_SECTION,
                                   CONS_FORWARD_MANAGER, FORWARD_DOT_EXE);
        endif;
        // Always write the proper ControlPanel for the target OS.
        switch (SysInfo.lOS)
          case IS_WINDOWSNT:
              szTmp = NTUI_DOT_EXE;
          default:
              szTmp = UI_DOT_EXE;
        endswitch;
        WriteIniEntry(szIni, CONS_STARTUP_SECTION,
                      CONS_CONTROL_PANEL, szTmp);
        // Note! driver.exe is dead but maintain this logic to ensure
        //       that any driver.exe entries out there get positively
        //       changed to ntdriver.exe whatever the (32-bit)OS.
        WriteIniEntry(szIni, CONS_STARTUP_SECTION,
                      CONS_COMM_MANAGER, NTDRIVER_DOT_EXE);
        // WatchdogInterval            ; insert/update if no live entry
        WriteNumComment( szIni, CONS_STARTUP_SECTION,
                                WATCHDOG_INTERVAL,
                                WATCHDOG_INTERVAL_DEFAULT);
        // StartupDelayTime            ; insert/update if no live entry
        switch (SysInfo.lOS)
          case IS_WINDOWS95:
              WriteNumComment( szIni, CONS_STARTUP_SECTION,
                                      CONS_STARTUP_DELAY_TIME,
                                      CONS_STARTUP_DELAY_TIME_W95_DEF);
          default:
              WriteNumComment( szIni, CONS_STARTUP_SECTION,
                                      CONS_STARTUP_DELAY_TIME,
                                      CONS_STARTUP_DELAY_TIME_DEF);
        endswitch;
        // ShutDownDelay
        WriteNumComment( szIni, CONS_STARTUP_SECTION,
                                TIME_TO_WAIT_FOR_SHUTDOWN,
                                TIME_TO_WAIT_FOR_SHUTDOWN_DEFAULT);
        // [Alarm Manager] ----------------------------------------------------
        RelocateNumNonDefault( szIni,
                               ALARM_MGR_SECTION,
                               ALARM_GEN_WARN_OVER_DAYS_OLD,
                               ALARM_GEN_WARN_OVER_DAYS_OLD_DEF,
                               ALARM_GEN_SECTION);
        // The default was changed to 1 from 0 for 3.0
        RelocateNumNonDefault( szIni,
                               ALARM_MGR_SECTION,
                               ALARM_GEN_N2000_LOCAL_TIME,
                               ALARM_GEN_N2000_LOCAL_TIME_DEF,
                               ALARM_GEN_SECTION);
        RelocateNumNonDefault( szIni,
                               ALARM_MGR_SECTION,
                               ALARM_GEN_OPTIMIZE_LOG_PRINT,
                               ALARM_GEN_OPTIMIZE_LOG_PRINT_DEF,
                               ALARM_GEN_SECTION);
        // Having moved anything useful, delete the section completely.
        if (WriteProfString(szIni, ALARM_MGR_SECTION, "", "") < 0) then
            LoadString128(SETUP_ERROR, szBuf);
            LoadString128(SETUP_SECTION_NOT_DELETED, szTmp);
            Sprintf(szMsg, szTmp, ALARM_MGR_SECTION, szIniName);
            // 'Error: [Alarm Manager] section not deleted from N2000.INI'
            WriteLogMsg(szFn + " " + szBuf + szMsg);
        endif;
        // [RelayConfig_0] [RelayConfig_7] ------------------------------------
        // On an NT machine, after fltconv runs, delete
        // Ini parms obsoleted with Falcon (3.0)
        for lTmp = 0 to 7
            NumToStr(szTmp, lTmp);
            WriteIniEntry(szIni, CONS_RELAY_SECTION + szTmp, "Level", "");
            WriteIniEntry(szIni, CONS_RELAY_SECTION + szTmp, "DisableFrom", "");
            WriteIniEntry(szIni, CONS_RELAY_SECTION + szTmp, "DisableTo", "");
        endfor;
        // [Alarm Stream] -----------------------------------------------------
        if ( SysInfo.lOS = IS_WINDOWSNT ) then
           WriteNumComment( szIni, ASTREAM_SECTION, DEBUG_ENABLE, 1);
        endif;
        // [Forward Manager] --------------------------------------------------
        //  Insert commented entries if no live entry
        //  irregardless of the OS - See policy note.
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION, DEBUG_ENABLE, 1);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION, IPC_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION, DETAIL_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION, DOCVIEW_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION,
                                FORWARD_LOG_SEND, 1);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION,
                                FORWARD_LOG_RECEIVE, 1);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION,
                                FORWARD_SAVE_ON_SHUTDOWN,
                                FORWARD_SAVE_ON_SHUTDOWN_DEFAULT);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION,
                                FWD_SEND_FAIL_ALARM_LIMIT,
                                FWD_SEND_FAIL_ALARM_LIMIT_DEF);
        WriteNumComment( szIni, FORWARD_MANAGER_SECTION,
                                FWD_FAIL_ALARM_PRIORITY,
                                FWD_FAIL_ALARM_PRIORITY_DEF);
        // [AdvancedAlarmFiltering]
        //  Insert commented entry if no live entry
        //  irregardless of the OS - See policy note.
        WriteNumComment( szIni, ADV_ALARM_FILTERING_SECTION,
                                DEBUG_ENABLE, 1);
        WriteNumComment( szIni, ADV_ALARM_FILTERING_SECTION,
                                ADV_ALARM_FILTERING_EXTRA_DUP_DELAY_TIME,
                                ADV_ALARM_FILTERING_EXTRA_DUP_DELAY_TIME_DEF);
        WriteNumComment( szIni, ADV_ALARM_FILTERING_SECTION,
                                ADV_ALARM_FILTERING_EXTRA_ALOK_DELAY_TIME,
                                ADV_ALARM_FILTERING_EXTRA_ALOK_DELAY_TIME_DEF);
        // [Database] ---------------------------------------------------------
        // Positively set FilePath=the path to the application directory
        // (Which may have changed from \NETMAN to \N2000!)
        // Path MUST be WITHOUT a trailing backslash
        szWrkPath = szAppPath;
        StrRemoveLastSlash(szWrkPath);
        WriteIniEntry(szIni, CONS_DATABASE_SECTION,
                             DATABASE_FILEPATH, szWrkPath);
        RelocateNumNonDefault( szIni,
                               CONS_DATABASE_SECTION, ALARM_GEN_KEEP_DAYS,
                               ALARM_GEN_KEEP_DAYS_DEF, ALARM_GEN_SECTION);
        // [Dispatch Manager] -------------------------------------------------
        // Debug=O,OFF,1,0  - delete any entries found
        if ( GetProfString(szIni, DISPATCH_MANAGER_SECTION,
                                  DEBUG_ENABLE, szTmp) = 0 ) then
            WriteIniEntry(szIni, DISPATCH_MANAGER_SECTION,
                                 DEBUG_ENABLE, "");
        endif;
        WriteNumComment( szIni, DISPATCH_MANAGER_SECTION, DEBUG_ENABLE, 1);
        WriteNumComment( szIni, DISPATCH_MANAGER_SECTION, IPC_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, DISPATCH_MANAGER_SECTION, DETAIL_DEBUG_ENABLE, 1);
        WriteNumComment( szIni, DISPATCH_MANAGER_SECTION, DOCVIEW_DEBUG_ENABLE, 1);
        WriteNumDefault(szIni, DISPATCH_MANAGER_SECTION,
                        DISPATCH_THROW_AWAY_ALARM_AGE,
                        DISPATCH_THROW_AWAY_ALARM_AGE_DEF);
        WriteStrDefault(szIni, DISPATCH_MANAGER_SECTION,
                        DISPATCH_RECOVER_ALARMS,
                        DISPATCH_RECOVER_ALARMS_DEFAULT);
        WriteNumDefault(szIni, DISPATCH_MANAGER_SECTION,
                        DISPATCH_KEEP_LOG_DAYS,
                        DISPATCH_KEEP_LOG_DAYS_DEFAULT);
        WriteNumDefault(szIni, DISPATCH_MANAGER_SECTION,
                        DISPATCH_HIST_LENGTH,
                        DISPATCH_HIST_LENGTH_DEFAULT);
        // The LogPath=. entry is a wls patented magic cookie
        // to cause the .PGL files to be put in the default/
        // current/application directory.
        WriteStrDefault(szIni, DISPATCH_MANAGER_SECTION,
                        DISPATCH_LOG_PATH, DISPATCH_LOG_PATH_DEFAULT);
        // [DebugOptions]
        WriteNumComment( szIni, CONS_DEBUG_OPTIONS,
                                CONS_SHOW_DEBUG_WINDOWS, 1);
        // [Throttle Section]
        WriteNumComment( szIni, THROTTLE_SECTION, THROTTLE_TURN_OFF_AFTER_SECS,
                                THROTTLE_TURN_OFF_AFTER_SECS_DEF);
        WriteNumComment( szIni, THROTTLE_SECTION, THROTTLE_INTERVAL,
                                THROTTLE_INTERVAL_DEF);
        WriteNumComment( szIni, THROTTLE_SECTION, THROTTLE_COM_ALRM_QUEUE_LIMIT,
                                THROTTLE_COM_ALRM_QUEUE_LIMIT_DEF);
        // [Termulator] -------------------------------------------------------
        WriteNumDefault(szIni, TERMULATOR_SECTION,
                        TERMULATOR_FONTSIZE, TERMULATOR_FONTSIZE_12);
        // [Error Handling] ---------------------------------------------------
        WriteStrDefault(szIni, ERROR_HANDLING_SECTION,
                        ERROR_HANDLING_ONGPF, ERROR_HANDLING_ONGPF_DEFAULT);
        // [PortConfig_n] -----------------------------------------------------
        // Prior to release 1.0, only port configs 0..3 were defined.
        // The reset string was added in netman 1.0.
        for lTmp = 0 to 8
            NumToStr(szTmp, lTmp);
            WriteStrDefault(szIni, CONS_PORT_SECTION + szTmp,
                            CONS_RESET_STR, CONS_RESET_STR_DEFAULT);
        endfor;
        //  PortType set to 1 (Modem) for [PortConfig_0 thru _1]
        for lTmp = 0 to 1
            NumToStr(szTmp, lTmp);
            WriteStrDefault(szIni, CONS_PORT_SECTION + szTmp,
                            CONS_PORT_TYPE, "1");
        endfor;
        //  PortType set to 2 (Direct) for [PortConfig_2 thru _8]
        for lTmp = 2 to 8
            NumToStr(szTmp, lTmp);
            WriteStrDefault(szIni, CONS_PORT_SECTION + szTmp,
                            CONS_PORT_TYPE, "2");
        endfor;
        // [DocView] ----------------------------------------------------------
        // MaxAlarmRecords
        //Delete any comment entry
        WriteStrComment(szIni, CONS_DOCVIEW_SECTION,
                               CONS_DOCVIEW_MAX_ALARMS, "");
        // The app uses only the CONS_DOCVIEW_MAX_ALARMS_33_UP_MB_DEF
        // constant (10000), which wls has found is too large for
        // machines with 32 MB or memory or less.
        // So... if there is no live entry:
        // For 33&Up MB machines insert comment ';MaxAlarmRecords=10000' entry.
        // For 17-32 MB machines insert live    'MaxAlarmRecords=5000'   entry.
        // For  0-16 MB machines insert live    'MaxAlarmRecords=2000'   entry.
        if (SysInfo.lSystemMemoryMb > 32) then
            WriteNumComment( szIni, CONS_DOCVIEW_SECTION,
                                    CONS_DOCVIEW_MAX_ALARMS,
                                    CONS_DOCVIEW_MAX_ALARMS_33_UP_MB_DEF);
        else
            // IS3BUGFIX : GetProfInt returns 0 (success) for a non-existing
            //             szKeyName. Use getProfString() instead. This does
            //             work and correctly returns -1.
            lResult = GetProfString(szIni, CONS_DOCVIEW_SECTION,
                                           CONS_DOCVIEW_MAX_ALARMS, szTmp);
            if ( lResult < 0 ) then
                if (SysInfo.lSystemMemoryMb > 16) then
                   lTmp = CONS_DOCVIEW_MAX_ALARMS_17_32_MB_DEF;
                else
                   lTmp = CONS_DOCVIEW_MAX_ALARMS_0_16_MB_DEF;
                endif;
                NumToStr( szTmp, lTmp);
                WriteIniEntry(szIni, CONS_DOCVIEW_SECTION,
                                     CONS_DOCVIEW_MAX_ALARMS, szTmp);
            endif;
        endif;
        // [Site Monitor] -----------------------------------------------------
        WriteNumComment( szIni, CONS_SITE_MONITOR_SECTION,
                                CONS_SITE_MONITOR_ALARMPRIORITY,
                                CONS_SITE_MONITOR_ALARMPRIORITY_DEFAULT);
        // [CommConfig]
       // Disable Ping,Telnet Server features on Windows 95 machines
       if ( SysInfo.lOS = IS_WINDOWS95) then
           WriteIniEntry( szIni, CONS_COMM_CFG_SECTION,
                                 CONS_SYS_PING_ENABLED, "0" );
           WriteIniEntry( szIni, CONS_COMM_CFG_SECTION,
                                 CONS_TELNET_SERVER_ENABLED, "0" );
       endif;
        WriteNumComment( szIni, CONS_COMM_CFG_SECTION,
                                CONS_COMM_PING_TIMEOUT,
                                CONS_COMM_PING_TIMEOUT_DEF);
        WriteNumComment( szIni, CONS_COMM_CFG_SECTION,
                                CONS_COMM_PING_RETRIES,
                                CONS_COMM_PING_RETRIES_DEF);
        WriteNumComment( szIni, CONS_COMM_CFG_SECTION,
                                CONS_TELNET_SERVER_PORT,
                                CONS_TELNET_SERVER_PORT_DEF);
        WriteNumComment( szIni, CONS_COMM_CFG_SECTION,
                                CONS_TELNET_MAX_SESSIONS,
                                CONS_TELNET_MAX_SESSIONS_DEF);
        WriteNumComment( szIni, CONS_COMM_CFG_SECTION,
                                CONS_TELNET_LOGIN_RETRIES,
                                CONS_TELNET_LOGIN_RETRIES_DEF);
        // [C2000 Connect] ----------------------------------------------------
        // Don't use the consdefs default but always write
        // C2000GetIsAllFields=1 so all the user has to do
        // is remove the comment to change DB gets to read
        // all fields, not just read/write ones.
        WriteNumComment( szIni, C2000_CONNECT_INI_TOPIC,
                                C2000_DB_GET_IS_ALL_FIELDS,
                                1);
        // [Map] --------------------------------------------------------------
        // [DataFiles]
        // [DefaultView]
        if ( bMapSelected = TRUE ) then
            // Re-set the map defaults for the new map region
            UpdateMapDefaults(szIni);
        else
            // The user may elect to do a new or update install without Map.
            // We must still set the [DataFiles] section. Otherwise, when
            // the app runs it would display a "Voronoi table missing" dialog
            // because the consdefs default for ShowMapFeature is 1 (enabled).
            // (We do not want to change the consdefs default because
            // 1 (enabled) will probably be the norm).
            if (bRunMapsFromCdrom) then
                // The user has previously selected which map of the multiple
                // maps to start with in the 'SelectMapOptions()' dialog,
                // and lSetDefaultsForMap has been set accordingly.
                UpdateMapDefaults(szIni);
            else
                szWrkPath = szAppPath ^ MDV_MAPDATA;
                if ( ExistsDir( szWrkPath ) != EXISTS  ) then
                   // Set ShowMapFeature=0 if there's no MAPDATA directory
                   WriteIniEntry(szIni, MAP_SECTION, MAP_SHOW_MAP_FEATURE, "0");
                else
                   // There is a MAPDATA directory off of the target directory.
                   // Find out which map he has and point at that.
                   // If he has more than one map, last one found wins.
                   szWrkPath = szAppPath ^ MDV_MAPDATA ^ MDV_EUROPE;
                   if ( ExistsDir( szWrkPath ) = EXISTS  ) then
                       lSetDefaultsForMap = ENUM_EUROPE;
                       UpdateMapDefaults(szIni);
                   endif;
                   szWrkPath = szAppPath ^ MDV_MAPDATA ^ MDV_NORTHA;
                   if ( ExistsDir( szWrkPath ) = EXISTS  ) then
                       lSetDefaultsForMap = ENUM_NORTHA;
                       UpdateMapDefaults(szIni);
                   endif;
                   szWrkPath = szAppPath ^ MDV_MAPDATA ^ MDV_SOUTHA;
                   if ( ExistsDir( szWrkPath ) = EXISTS  ) then
                       lSetDefaultsForMap = ENUM_SOUTHA;
                       UpdateMapDefaults(szIni);
                   endif;
                   szWrkPath = szAppPath ^ MDV_MAPDATA ^ MDV_SASIA;
                   if ( ExistsDir( szWrkPath ) = EXISTS  ) then
                       lSetDefaultsForMap = ENUM_SASIA;
                       UpdateMapDefaults(szIni);
                   endif;
                endif;
            endif;
        endif;
        SRCDIR    = szAppPath;
        TARGETDIR = szAppPath;
        // Log the result to the setup log
        WriteFileToLog ( szAppPath, N2000 + ".ini" );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateMapDefaults(szIni)
 *
 *  Purpose:
 *
 *  Input  :  szIni                     Fully qualified path to app .ini file
 *            lSetDefaultsForMap        ENUM_Region
 *            bMapUnitsKm
 *            bRunMapsFromCdrom
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function UpdateMapDefaults(szIni)
STRING szTmp;
begin
        // [Map]
        WriteIniEntry(szIni, MAP_SECTION, MAP_SHOW_MAP_FEATURE, "1");
        // Flip flag - ini entry is Miles-centric
        if (bMapUnitsKm) then
           szTmp = "0";
        else
           szTmp = "1";
        endif;
        WriteIniEntry(szIni, MAP_SECTION, MAP_UNITS_MILES, szTmp);
        WriteNumDefault(szIni, MAP_SECTION, MAP_CIRCLE_MILES,
                        MAP_CIRCLE_MILES_DEF);
        WriteStrDefault(szIni, MAP_SECTION, MAP_NATIONAL_SCALE,
                        MAP_NATIONAL_SCALE_DEFAULT);
        WriteStrDefault(szIni, MAP_SECTION, MAP_REGIONAL_SCALE,
                        MAP_REGIONAL_SCALE_DEFAULT);
        WriteStrDefault(szIni, MAP_SECTION, MAP_LOCAL_SCALE,
                        MAP_LOCAL_SCALE_DEFAULT);
        WriteNumComment(szIni, MAP_SECTION, CONS_SHOW_COUNTRY_NAME, 0);
        // [DataFiles]
        if (bRunMapsFromCdrom = TRUE) then
            szTmp = szSrcPath ^ MDV_MAPDATA;
        else
            szTmp = szAppPath ^ MDV_MAPDATA;
        endif;
        WriteIniEntry(szIni, MAP_DATAFILES_SECTION,
                             MAP_DATAFILES_VORONOITABLE,
                             szTmp ^ "FWORLD.VIX" );
        // Conditionally set [Datafiles] Browse=
        // if it doesn't exist.  The reason we are handling this is
        // because for a new install, without default settings for
        // the Border and Fill color, the map comes up blue nothing.
        WriteStrDefault(szIni, MAP_DATAFILES_SECTION,
                               MAP_DATAFILES_BROWSE,
                               MAP_DATAFILES_BROWSE_DEF);
        // Setup Map Region-Specific defaults
        if ( lSetDefaultsForMap = ENUM_EUROPE) then
            WriteMapRegionDefaults(szIni, MDV_EUROPE,
                                   MDV_EUROPE_LATITUDE_DEF,
                                   MDV_EUROPE_LONGITUDE_DEF,
                                   MDV_EUROPE_SCALE_DEF,
                                   MAP_EUROPE_SCROLL_TOP_LAT_DEF,
                                   MAP_EUROPE_SCROLL_BOT_LAT_DEF,
                                   MAP_EUROPE_SCROLL_LEFT_LONG_DEF,
                                   MAP_EUROPE_SCROLL_RIGHT_LONG_DEF);
        endif;
        if ( lSetDefaultsForMap = ENUM_NORTHA) then
            WriteMapRegionDefaults(szIni, MDV_NORTHA,
                                   MDV_NORTHA_LATITUDE_DEF,
                                   MDV_NORTHA_LONGITUDE_DEF,
                                   MDV_NORTHA_SCALE_DEF,
                                   MAP_NORTHA_SCROLL_TOP_LAT_DEF,
                                   MAP_NORTHA_SCROLL_BOT_LAT_DEF,
                                   MAP_NORTHA_SCROLL_LEFT_LONG_DEF,
                                   MAP_NORTHA_SCROLL_RIGHT_LONG_DEF);
        endif;
        if ( lSetDefaultsForMap = ENUM_SOUTHA) then
            WriteMapRegionDefaults(szIni, MDV_SOUTHA,
                                   MDV_SOUTHA_LATITUDE_DEF,
                                   MDV_SOUTHA_LONGITUDE_DEF,
                                   MDV_SOUTHA_SCALE_DEF,
                                   MAP_SOUTHA_SCROLL_TOP_LAT_DEF,
                                   MAP_SOUTHA_SCROLL_BOT_LAT_DEF,
                                   MAP_SOUTHA_SCROLL_LEFT_LONG_DEF,
                                   MAP_SOUTHA_SCROLL_RIGHT_LONG_DEF);
        endif;
        if ( lSetDefaultsForMap = ENUM_SASIA) then
            WriteMapRegionDefaults(szIni, MDV_SASIA,
                                   MDV_SASIA_LATITUDE_DEF,
                                   MDV_SASIA_LONGITUDE_DEF,
                                   MDV_SASIA_SCALE_DEF,
                                   MAP_SASIA_SCROLL_TOP_LAT_DEF,
                                   MAP_SASIA_SCROLL_BOT_LAT_DEF,
                                   MAP_SASIA_SCROLL_LEFT_LONG_DEF,
                                   MAP_SASIA_SCROLL_RIGHT_LONG_DEF);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WriteMapRegionDefaults( szIni, szMapRegion,
 *                                    szLatitude, szLongitude, szScale,
 *                                    lTopLat, lBotLat, lLeftLong, lRightLong )
 *
 *  Purpose:
 *
 *  Returns:
 *
 * Comments:  If we are doing an update then the region specific map settings
 *            must be adjusted in the existing app .ini file.
 *
 *            If we are doing a new install, then any settings there may
 *            be in the app .def file are in the nature of examples,
 *            and MUST be overridden with the defaults for the region the
 *            user has chosen.
 *
 *            Therefore the updates in this function are NOT wrapped with
 *            WriteXxxDefault function calls, and specifically overwrite
 *            any existing entries.
 *
 *
 *
\*---------------------------------------------------------------------------*/
function WriteMapRegionDefaults( szIni, szMapRegion,
                                 szLatitude, szLongitude, szScale,
                                 lTopLat, lBotLat, lLeftLong, lRightLong )
STRING szTmp;
begin
        // [DataFiles]  (again)
        // Note! The cdrom map region directory tree is flattened
        //       with respect to the installed tree ie:
        //       rom installed      to Cdrom
        //
        //           mapdata           mapdata
        //              :...europe     europe
        //              :...northa     northa
        if (bRunMapsFromCdrom = TRUE) then
            szTmp = szSrcPath ^ szMapRegion;
        else
            szTmp = szAppPath ^ MDV_MAPDATA ^ szMapRegion;
        endif;
        WriteIniEntry(szIni, MAP_DATAFILES_SECTION,
                             MAP_DATAFILES_INDEXFILE,
                             szTmp ^ "INDEX.IDX" );
        // Note these next two only work for one file
        WriteIniEntry(szIni, MAP_DATAFILES_SECTION,
                             MAP_DATAFILES_DATAFILE + "0",
                             szTmp ^ "DATA.DTA" );
        if (DEBUG) then
           if (MAP_DATAFILES_NUMDATAFILES_DEF != 1) then
               SprintfBox( SEVERE, "WriteMapRegionDefaults()",
               "ASSERT: %s=[%d] not 1",
                        MAP_DATAFILES_NUMDATAFILES,
                        MAP_DATAFILES_NUMDATAFILES_DEF);
           endif;
        endif;
        NumToStr(szTmp, MAP_DATAFILES_NUMDATAFILES_DEF);
        WriteIniEntry(szIni, MAP_DATAFILES_SECTION,
                             MAP_DATAFILES_NUMDATAFILES,
                             szTmp);
        // [Map] (again)
        NumToStr(szTmp, lTopLat);
        WriteIniEntry(szIni, MAP_SECTION, MAP_SCROLL_TOP, szTmp);
        NumToStr(szTmp, lBotLat);
        WriteIniEntry(szIni, MAP_SECTION, MAP_SCROLL_BOTTOM, szTmp);
        NumToStr(szTmp, lLeftLong);
        WriteIniEntry(szIni, MAP_SECTION, MAP_SCROLL_LEFT, szTmp);
        NumToStr(szTmp, lRightLong);
        WriteIniEntry(szIni, MAP_SECTION, MAP_SCROLL_RIGHT, szTmp);
        // [DefaultView]
        WriteIniEntry(szIni, MDV_SECTION, MDV_DEFAULT_LATITUDE,
                                          szLatitude);
        WriteIniEntry(szIni, MDV_SECTION, MDV_DEFAULT_LONGITUDE,
                                          szLongitude);
        WriteIniEntry(szIni, MDV_SECTION, MDV_DEFAULT_SCALE,
                                          szScale);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WriteNumComment( szIni, szSection, szParm, lDefault)
 *
 *  Purpose:  Delete existing ;szParameter if there is one.
 *            If there is no live szParameter entry,
 *            add a ;szParameter entry with the new consdefs default value
 *
 *            This adds commented parameters to user app .ini files
 *            currently without them, and replaces existing comments.
 *
 *            The user then has a template he can just uncomment if he
 *            wants to change the default.
 *
 *  Returns:
 *
 * Comments:  This function is implememented the hard way because
 *            of an IS3 bug:
 *            WriteIniEntry(szIni, szSection, szCommentedParm, "");
 *            works on 3.1 to delete a comment parm but fails on NT3.51
 *
 *            Note that parms are not necesarily unique ie 'Debug=' may
 *            occur in several sections, so extra logic is required to
 *            locate the parm in the right section.
 *
\*---------------------------------------------------------------------------*/
function WriteNumComment( szIni, szSection, szParm, lDefault)
STRING szTmp, szCommentedParm;
STRING szIniName;                  // File name+ext only of app .ini file
LONG lTmp, lSLine, lNLine, lCLine;
begin
        szCommentedParm = ";" + szParm;
        VarSave( SRCTARGETDIR );        // Save
        ParsePath(SRCDIR, szIni, PATH);
        ParsePath(szIniName, szIni, FILENAME);
        // Locate the section header
        if (FileGrep(szIniName, "[" + szSection + "]", szTmp, lSLine, RESTART)
                                                = 0 ) then
            // Locate the Next section header (or EOF)
            lTmp = FileGrep(szIniName, "[", szTmp, lNLine, CONTINUE);
            if (lTmp = 0 || lTmp = END_OF_FILE) then
                if (lTmp = END_OF_FILE) then
                    lNLine = END_OF_FILE;
                endif;
                // Locate the Comment line to be deleted
                FileGrep(szIniName, "[" + szSection + "]", szTmp, lSLine,
                                                                  RESTART);
                if (FileGrep(szIniName, szCommentedParm, szTmp, lCLine,
                                                         CONTINUE) =0 ) then
                    if ( (lCLine > lSLine) &&
                         ( (lCLine < lNLine) || (lNLine = END_OF_FILE) ) ) then
                        // Delete the old commented parm
                        FileDeleteLine(szIniName, lCLine, lCLine);
                    endif;
                endif;
            endif;
        endif;
        VarRestore( SRCTARGETDIR );     // Restore
        // Insert new commented parameter
        // IS3BUGFIX:
        // GetProfInt returns 0 (success) for a non-existing szKeyName.
        // Use getProfString() instead. This works and correctly returns -1.
        if ( GetProfString(szIni, szSection, szParm, szTmp) < 0 ) then
            NumToStr(szTmp, lDefault);
            WriteIniEntry(szIni, szSection, szCommentedParm, szTmp);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WriteStrComment( szIni, szSection, szParm, szDefault)
 *
 *  Purpose:  Delete existing ;szParameter if there is one.
 *
 *            If the supplied default value is not null, and
 *            there is no existing live szParameter entry,
 *            add a ;szParameter entry with the new consdefs default value
 *
 *            This adds commented parameters to user app .ini files
 *            currently without them, and replaces existing comments.
 *
 *            The user then has a template he can just uncomment if he
 *            wants to change the default.
 *
 *  Returns:
 *
 * Comments:  This function is implememented the hard way because
 *            of an IS3 bug:
 *            WriteIniEntry(szIni, szSection, szCommentedParm, "");
 *            works on 3.1 to delete a comment parm but fails on NT3.51
 *
 *            Note that parms are not necesarily unique ie 'Debug=' may
 *            occur in several sections, so extra logic is required to
 *            locate the parm in the right section.
 *
\*---------------------------------------------------------------------------*/
function WriteStrComment( szIni, szSection, szParm, szDefault)
STRING szTmp, szCommentedParm;
STRING szIniName;                  // File name+ext only of app .ini file
LONG lTmp, lSLine, lNLine, lCLine;
begin
        szCommentedParm = ";" + szParm;
        VarSave( SRCTARGETDIR );        // Save
        ParsePath(SRCDIR, szIni, PATH);
        ParsePath(szIniName, szIni, FILENAME);
        // Locate the section header
        if (FileGrep(szIniName, "[" + szSection + "]", szTmp, lSLine, RESTART)
                                                = 0 ) then
            // Locate the Next section header (or EOF)
            lTmp = FileGrep(szIniName, "[", szTmp, lNLine, CONTINUE);
            if (lTmp = 0 || lTmp = END_OF_FILE) then
                if (lTmp = END_OF_FILE) then
                    lNLine = END_OF_FILE;
                endif;
                // Locate the Comment line to be deleted
                FileGrep(szIniName, "[" + szSection + "]", szTmp, lSLine,
                                                                  RESTART);
                if (FileGrep(szIniName, szCommentedParm, szTmp, lCLine,
                                                         CONTINUE) =0 ) then
                    if ( (lCLine > lSLine) &&
                         ( (lCLine < lNLine) || (lNLine = END_OF_FILE) ) ) then
                        // Delete the old commented parm
                        FileDeleteLine(szIniName, lCLine, lCLine);
                    endif;
                endif;
            endif;
        endif;
        VarRestore( SRCTARGETDIR );     // Restore
        // Insert new commented parameter if default non-null
        if (szDefault != "") then
           // IS3BUGFIX:
           // GetProfInt returns 0 (success) for a non-existing szKeyName.
           // Use getProfString() instead. This works and correctly returns -1.
           if ( GetProfString(szIni, szSection, szParm, szTmp) < 0 ) then
               WriteIniEntry(szIni, szSection, szCommentedParm, szDefault);
           endif;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WriteNumDefault( szIni, szSection, szParm, lDefault)
 *
 *  Purpose:  Tests to see if a 'Parameter=n' type entry exists.
 *            If the entry does not exist, write the entry with
 *            the consdefs default value
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function WriteNumDefault( szIni, szSection, szParm, lDefault)
STRING szTmp;
LONG lTmp;
begin
        // IS3BUGFIX : GetProfInt returns 0 (success) for a non-existing
        //             szKeyName. Use getProfString() instead. This does
        //             work and correctly returns -1.
        if ( GetProfString(szIni, szSection, szParm, szTmp) < 0 ) then
            NumToStr(szTmp, lDefault);
            WriteIniEntry(szIni, szSection, szParm, szTmp);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WriteStrDefault( szIni, szSection, szParm, szDefault)
 *
 *  Purpose:  Tests to see if a 'Parameter=string' type entry exists.
 *            If the entry does not exist, or the parameter value is
 *            null, write the entry with the consdefs default value
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function WriteStrDefault( szIni, szSection, szParm, szDefault)
STRING szTmp;
LONG lTmp;
begin
        if ( GetProfString(szIni, szSection, szParm, szTmp) < 0) then
            WriteIniEntry(szIni, szSection, szParm, szDefault);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  RelocateNumNonDefault( szIni, szSection, szParm, lDefault,
 *                                   szNewSection)
 *
 *  Purpose:  Tests to see if a 'Parameter=n' type entry exists.
 *            If the entry exists and has a value not the same as
 *            the default, write the entry into the new section with
 *            the non-default value.
 *            Delete the parameter from the original section.
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function RelocateNumNonDefault( szIni, szSection, szParm, lDefault,
                                szNewSection)
STRING szTmp;
LONG lTmp;
begin
        // IS3BUGFIX : GetProfInt returns 0 (success) for a non-existing
        //             szKeyName. Use getProfString() instead. This does
        //             work and correctly returns -1.
        if ( GetProfString(szIni, szSection, szParm, szTmp) < 0 ) then
             return;
        endif;
        StrToNum( lTmp, szTmp);
        if (lTmp != lDefault) then
            WriteIniEntry(szIni, szNewSection, szParm, szTmp);
        endif;
        WriteIniEntry(szIni, szSection, szParm, "");
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  WriteIniEntry ( szIni, szSection, szParm, szValue )
 *
 *  Purpose:  Add entries to an INI file with WriteProfString
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function WriteIniEntry( szIni, szSection, szParm, szValue )
begin
        if (WriteProfString(szIni, szSection, szParm, szValue) < 0) then
            SetupError( "Unable to write to " + szIni + " file." );
            return FALSE;
        endif;
        return TRUE;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateWinIni
 *
 *  Purpose:  Add entries to the Windows directory WIN.INI file;
 *
 *            [windows]
 *            spooler=yes
 *
 *            [spooler]
 *            netspool=yes
 *
 *    Input:
 *
 *  Returns:
 *
 * Comments:  Warren reported having problems when the entries
 *            had ;Network Manager comment tags, so these have
 *            been removed.
 *
\*---------------------------------------------------------------------------*/
function UpdateWinIni()
STRING szIni, szMsg, szExpand;
begin
        szIni = WINDIR ^ WIN_DOT_INI;
        LoadString128(SETUP_UPDATING, szExpand);
        Sprintf(szMsg, szExpand, szIni);
        SetStatusWindow( -1, szMsg);
        WriteIniEntry( szIni, "windows", "spooler",  "yes");
        WriteIniEntry( szIni, "spooler", "netspool", "yes");
         // Log the result to the setup log
        WriteFileToLog ( WINDIR, WIN_DOT_INI );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateSystemIni()
 *
 *  Purpose:  Add entries to the Windows directory SYSTEM.INI file;
 *
 *            [386Enh]
 *            device=c:\cb\cbul.386             ; N2000 ARC Support
 *            DMABufferSize=064                 ; N2000 ARC Support
 *
 *            [Network]
 *            LoadNetDDE=no                     ; N2000
 *
 *            [drivers]
 *            Wave=speaker.drv                  ; N2000
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *            bAlarmRelayContactsSelected
 *            bInstallPCSoundDriver
 *  Returns:
 *
 * Comments:  Before using FileGrep and FileInsertLine you MUST set SRCDIR
 *            to WINSYSDIR.  Supply just "system.ini" as the first parm.
 *            If you supply a fully qualified path+filename FileGrep
 *            will fail with FILE_NOT_FOUND.
 *
 * Comments:  sets 'bRestartWin' if necessary to force restart at end
 *
 * Comments:  Setup no longer writes ComIrqSharing=TRUE, but
 *            it doesn't delete it from old installations either.
 *            ComIrqSharing is True for MCA and EISA machines.
 *            false all others.
 *            Specifies whether COM interrupt lines are shareable
 *            between multiple serial ports and other devices.
 *            Enable this setting if your machine uses the same
 *            interrupt for COM3&4 as it does for COM1&2.
 *
\*---------------------------------------------------------------------------*/
function UpdateSystemIni()
STRING szIni, szTmp, szFile, szTag, sz386, szSwapFile, szExpand;
LONG lLine, lTmp, lHeader, lTest;
begin
        szIni = WINDIR + SYSTEM_DOT_INI;     // Need fully qualified path
                                             // for WriteIniEntry
        LoadString128(SETUP_UPDATING, szExpand);
        Sprintf(szTmp, szExpand, szIni);
        SetStatusWindow( -1, szTmp);
        szTag = "; " + N2000 + " ";
        // Add system.ini entries for Alarm Relay Contacts
        // alarm relay contacts installed?
        if ( bAlarmRelayContactsSelected = TRUE ) then
            // NOTE : 'device' is a non-unique key so to add
            //        'device =c:\cb\cbul.386' we have to grep for the
            //        line (if it exists) and use FileInsertLine to
            //        avoid zapping other 'device=x' lines.
            sz386 = "device=" + TARGETDISK ^ CBDIR ^ "cbul.386" +
                    "             " + szTag + CSTR_ARC_SUPPORT;
            VarSave( SRCTARGETDIR );        // Save
            SRCDIR = WINDIR;                // For FileGrep & FileInsertLine
            lTest = FileGrep(SYSTEM_DOT_INI, CSTR_386ENH, szTmp, lLine,
                                                                   RESTART);
            if (lTest = 0 ) then
               // There is a [386Enh] section
               lHeader = lLine;             // Remember it's line number
               // Look for an existing (any case) 'device=path\cbul.386' line
               if ( FileGrep(SYSTEM_DOT_INI, "cbul.386", szTmp, lLine,
                             CONTINUE) = 0 ) then
                   // replace it
                   FileInsertLine(SYSTEM_DOT_INI, sz386, lLine, REPLACE);
               else
                   // Add new entry after the header
                   FileInsertLine( SYSTEM_DOT_INI, sz386, lHeader, AFTER);
               endif;
            else
                // No [386Enh] section. Add it with the entry.
                WriteIniEntry( szIni, CSTR_386ENH, "device", sz386);
            endif;
            // Another Alarm Relay Contacts requirement per /cb/rev.doc
            WriteIniEntry( szIni, CSTR_386ENH, "DMABufferSize", "064" +
                "                 " + szTag  + CSTR_ARC_SUPPORT);
        endif;
        // Update system.ini file to turn off loading of NetDDE
        // This turns off direct printing to the network drive
        // and greatly increases the speed of Network Manager
        // while printing.
        WriteIniEntry(szIni, "Network", "LoadNetDDE", "no" +
                      "                     " +  szTag);
        // Install pc speaker driver if requested by the user:
        // - Copy over the driver file to the Windows System directory.
        // - update system.ini.
        if (bInstallPCSoundDriver = TRUE ) then
                SRCDIR = szAppPath;
                TARGETDIR = WINSYSDIR;
                GaCopyFile(SPEAKER_DOT_DRV,SPEAKER_DOT_DRV, FALSE, TRUE, TRUE);
                TARGETDIR = szAppPath;
                WriteIniEntry(szIni, "drivers", "Wave", SPEAKER_DOT_DRV +
                              "                  " + szTag);
        endif;
        // Log the result to the setup log
        WriteFileToLog ( WINDIR, SYSTEM_DOT_INI );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateAutoexecBat
 *
 *  Purpose:  Add-change entries in AUTOEXEC.BAT
 *
 *            set CBDIREC=C:\CB
 *            scandisk x: /autofix /nosave /nosummary
 *              (where x: is the disk N2000 is installed on)
 *            win
 *
 *    Input:  SysInfo
 *            bAlarmRelayContactsSelected
 *            bSetTZEnvVariable & szTZEnvVariable
 *
 *  Returns:  BOOL
 *
 * Comments:  sets 'bReboot' if necessary to force reboot at end of install
 *            Layoff the InstallShield Batch functions - they're poorly
 *            designed and buggy as hell.
 *
\*---------------------------------------------------------------------------*/
function UpdateAutoexecBat()
LIST    listAuto, listWork;
STRING  szFn, szPath, szAuto, szString, szCmd, szMsg1, szMsg2;
LONG    lCheck, bWriteWin;
begin
        szFn = "UpdateAutoexecBat()";
        LoadString128(SETUP_UPDATING, szMsg1);
        Sprintf(szTmp, szMsg1, AUTOEXEC_DOT_BAT);
        SetStatusWindow( -1, szTmp);
        // Get the default batch file fully qualified name
        if ( BatchGetFileName(szAuto) < 0) then
            LoadString128(SETUP_ERROR, szMsg1);
            LoadString128(SETUP_ERR_CANT_FIND_AUTOEXEC, szMsg2);
            SprintfBox( SEVERE, "UpdateAutoexecBat()", "%s %s", szMsg1, szMsg2);
            return FALSE;
        endif;
        // Create auto list
        listAuto = ListCreate(STRINGLIST);
        if (listAuto = LIST_NULL) then
            //MessageBox("Error: Autoexec list could not be created", SEVERE);
            return (ENUM_ERR_CREATING_LIST);
        endif;
        // Read autoexec.bat file into list
        if ( ListReadFromFile(listAuto, szAuto) < 0 ) then
            LoadString128(SETUP_ERROR, szMsg1);
            LoadString128(SETUP_ERR_READING_AUTOEXEC, szMsg2);
            SprintfBox( SEVERE, "UpdateAutoexecBat()", "%s %s", szMsg1, szMsg2);
            ListDestroy( listAuto);
            return (ENUM_ERR_READING_FILE);
        endif;
        // Write autoexec.bak backup file
        ParsePath(szPath, szAuto, PATH);
        if ( ListWriteToFile(listAuto, szPath ^ AUTOEXEC_DOT_BAK) < 0 ) then
            LoadString128(SETUP_ERROR, szMsg1);
            LoadString128(SETUP_ERR_WRITING_AUTOEXEC_BAK, szMsg2);
            SprintfBox( SEVERE, "UpdateAutoexecBat()", "%s %s", szMsg1, szMsg2);
            ListDestroy( listAuto);
            return (ENUM_ERR_WRITING_FILE);
        endif;
        // Create work list
        listWork = ListCreate(STRINGLIST);
        if (listWork = LIST_NULL) then
            //MessageBox("Error: Work list could not be created", SEVERE);
            ListDestroy( listAuto);
            return (ENUM_ERR_CREATING_LIST);
        endif;
        // Copy Auto to Work List
        lCheck = ListGetFirstString(listAuto, szString);
        // Delete old entries
        while (lCheck = 0)
            if (StrFind(szString, CSTR_WIN) = 0 ) then
               bWriteWin = TRUE;
            endif;
            if ( StrFind(szString, CSTR_ADDED_BY) >= 0    ||
                 (bAlarmRelayContactsSelected &&
                  StrFind(szString, CONS_CBDIREC) >= 0 )  ||
                 // POSITIVELY RUB OUT TZ ENTRIES! WLS.
                 StrFind(szString, REG_VALUE_NAME_TZ + "=") >= 0    ||
                 StrFind(szString, CSTR_SCANDISK) = 0     ||
                 bWriteWin
               ) then
               // Drop the record
             else
                 ListAddString(listWork, szString, AFTER);
             endif;
            lCheck = ListGetNextString(listAuto, szString);
        endwhile;
        // Add new entries to list
        if ( bAlarmRelayContactsSelected = TRUE ) then
            szCmd = CSTR_SET + " " + CONS_CBDIREC + "=" + TARGETDISK ^ CBDIR;
            ListAddString(listWork, CSTR_REM + szCmd +
                                    CSTR_ADDED_BY + ITEM_APP, AFTER);
            ListAddString(listWork, szCmd, AFTER);
            bReboot = TRUE;
            if ( DEBUG) then
               WriteLogMsg(szFn + " Set bReboot = TRUE for CBDIREC.");
            endif;
        endif;
        // Add "scandisk" of the drive where the app is to autoexec.bat.
        ParsePath(szString, szAppPath, DISK);
        szCmd = CSTR_SCANDISK + " " + szString + " /autofix /nosave /nosummary";
        ListAddString(listWork, CSTR_REM + szCmd +
                                CSTR_ADDED_BY + ITEM_APP, AFTER);
        ListAddString(listWork, szCmd, AFTER);
        if (bWriteWin = TRUE) then
            szCmd = CSTR_WIN;
            ListAddString(listWork, CSTR_REM + szCmd +
                                    CSTR_ADDED_BY + ITEM_APP, AFTER);
            ListAddString(listWork, szCmd, AFTER);
        endif;
        // Re-write autoexec.bat file to the default batch file drive.
        ParsePath(szPath, szAuto, PATH);
        if ( ListWriteToFile(listWork, szPath ^ AUTOEXEC_DOT_BAT) < 0 ) then
            LoadString128(SETUP_ERROR, szMsg1);
            LoadString128(SETUP_ERR_CANT_UPDT_AUTOEXEC, szMsg2);
            SprintfBox( SEVERE, szFn, "%s %s", szMsg1, szMsg2);
            ListDestroy( listAuto);
            ListDestroy( listWork);
            return (ENUM_ERR_WRITING_FILE);
        endif;
        ListDestroy( listAuto);
        ListDestroy( listWork);
        // Log the result to the setup log
        WriteFileToLog ( szPath, AUTOEXEC_DOT_BAT );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  UpdateConfigSys
 *
 *  Purpose:  Add-change BUFFER and FILE entries in CONFIG.SYS
 *    Input:
 *
 *  Returns:
 *
 * Comments:  sets 'bReboot' if necessary to force reboot at end of install
\*---------------------------------------------------------------------------*/
function UpdateConfigSys()
STRING szFn, szFqFile, szPath, szFile;
LONG lResult, lFiles, lBuffers;
begin
        szFn = "UpdateConfigSys()";
        SetStatusWindow( -1, "Updating CONFIG.SYS");
        lResult = EzConfigGetValue ("FILES", lFiles);
        if ( lResult < 0 || (lResult = 0 && lFiles < MIN_FILES)  ) then
             EzConfigSetValue( "FILES",   MIN_FILES );
             bReboot = TRUE;
            if ( DEBUG) then
               WriteLogMsg(szFn + " Set bReboot = TRUE for FILES.");
            endif;
        endif;
        lResult = EzConfigGetValue ("BUFFERS", lBuffers);
        if ( lResult < 0 || (lResult = 0 && lBuffers < MIN_BUFFERS ) ) then
             EzConfigSetValue( "BUFFERS",   MIN_BUFFERS );
             bReboot = TRUE;
            if ( DEBUG) then
               WriteLogMsg(szFn + " Set bReboot = TRUE for BUFFERS.");
            endif;
        endif;
        // Log the result to the setup log
        if ( ConfigFileLoad( "" ) = 0 ) then
            if ( ConfigGetFileName(szFqFile) = 0 ) then
                ParsePath(szPath, szFqFile, PATH);
                ParsePath(szFile, szFqFile, FILENAME);
                WriteFileToLog ( szPath, szFile );
                ConfigFileSave( "" );    // Don't want a backup created
            endif;
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SetupArcDLLsEXEs( SYSINFOSTRUCT )
 *
 *  Purpose:  This function renames, copies, deletes files as necessary
 *            the files placed into the application directory by the copy
 *            step to setup the files needed by Alarm Relay Contacts under
 *            either Win95 or WinNT.
 *
 *            The sources and targets reside in the application directory
 *            with the exception of giveio.sys which we are placing into
 *            the windows system32 driver directory.
 *
 *            Cdrom\As Copied N2000         Final User N2000 directory
 *                  Source                  95 Target    NT Target
 *
 *            \drivers\alrelay\giveio.sys  -- n/a ---   \winnt\system32
 *                                                              \drivers
 *
 *            \N2000\arc16_95.dll          arc16.dll    -- n/a ---
 *                   arc32_95.dll          arc32.dll    -- n/a ---
 *                   cbw_95.dll            cbw.dll      -- n/a ---
 *                   cbw_nt.dll            -- n/a ---   cbw.dll
 *                   cbi_dio.dll           -- n/a ---   cbi_dio.dll
 *                   cbi_ctr.dll           -- n/a ---   cbi_ctr.dll
 *                   ui.exe                ui.exe       -- n/a ---
 *                   ntui.exe              -- n/a ---   ntui.exe *
 *
 *            * UpdateOrReplaceAppIni() sets
 *              [Programs]
 *              ControlPanel=ui.exe   for Win95
 *              ControlPanel=ntui.exe for WinNT
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *
 *  Returns:
 *
 *  Comments: We're using the wrapped GaCopyFile function, although it's
 *            more I/O than a rename because we've got full error checking
 *            built in for free with it.
 *
 *            If it's not NT then we're going to default to the Win95 logic.
 *            This is in case the user later tries this install on
 *            a Win96/97 etc machine.  My best guess is that InstallShield
 *            will stay with the IS_WINDOWSNT return code, but may change
 *            the return code for Win9X flavors.
 *
 *            As policy we're going to rub out files which are not
 *            appropriate to the operating system.  The user can't
 *            boot under another OS and run N2000 without saving their
 *            databases and re-installing the app.
 *
\*---------------------------------------------------------------------------*/
function SetupArcDLLsEXEs( SysInfo )
STRING szMsg;
begin
        if (DEBUG) then
           if (szAppPath = "") then
               SprintfBox( SEVERE, "SetupArcDLLsEXEs()",
               "ASSERT: szAppPath not NULL\n" + "szAppPath=[%s]", szAppPath);
           endif;
        endif;
        LoadString128(SETUP_NT95_ARC_UI_FILES, szMsg);
        SetStatusWindow( -1, szMsg);
        VarSave( SRCTARGETDIR );
        switch (SysInfo.lOS)
            case IS_WINDOWSNT:
                  SRCDIR    = szAppPath;
                  // Copy giveio.sys to WINSYSDIR\system32\drivers
                  TARGETDIR = WINSYSDIR ^ DRIVERS;
                  if ( Is( FILE_EXISTS, SRCDIR ^ CONS_NT_PORTIO_DRIVER) ) then
                      GaCopyFile(CONS_NT_PORTIO_DRIVER, CONS_NT_PORTIO_DRIVER,
                                 FALSE, TRUE, TRUE);
                     // Leave the original driver in N2000.
                      // If it didn't copy it'll help to have it there.
                  endif;
                  // Do the rest of the work in the N2000 directory
                  TARGETDIR = szAppPath;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ ARC16_95_DOT_DLL) ) then
                      DeleteFile( ARC16_95_DOT_DLL );
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ ARC32_95_DOT_DLL) ) then
                      DeleteFile( ARC32_95_DOT_DLL );
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ CBW_NT_DOT_DLL) ) then
                      GaCopyFile(CBW_NT_DOT_DLL, CBW_DOT_DLL,
                                 FALSE, TRUE, TRUE);
                      DeleteFile(CBW_NT_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ CBW_95_DOT_DLL) ) then
                      DeleteFile( CBW_95_DOT_DLL );
                  endif;
                 if ( Is( FILE_EXISTS, TARGETDIR ^ UI_DOT_EXE) ) then
                      DeleteFile( UI_DOT_EXE );
                  endif;
            default:
                  // Windows 95
                  SRCDIR    = szAppPath;
                  TARGETDIR = szAppPath;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ ARC16_95_DOT_DLL) ) then
                      GaCopyFile(ARC16_95_DOT_DLL, ARC16_DOT_DLL,
                                 FALSE, TRUE, TRUE);
                      DeleteFile(ARC16_95_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ ARC32_95_DOT_DLL) ) then
                      GaCopyFile(ARC32_95_DOT_DLL, ARC32_DOT_DLL,
                                 FALSE, TRUE, TRUE);
                      DeleteFile(ARC32_95_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ ARC32_NT_DOT_DLL) ) then
                      DeleteFile(ARC32_NT_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ CBW_95_DOT_DLL) ) then
                      GaCopyFile(CBW_95_DOT_DLL, CBW_DOT_DLL,
                                 FALSE, TRUE, TRUE);
                      DeleteFile(CBW_95_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ CBW_NT_DOT_DLL) ) then
                      DeleteFile(CBW_NT_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ CBI_DIO_DOT_DLL) ) then
                      DeleteFile(CBI_DIO_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ CBI_CTR_DOT_DLL) ) then
                      DeleteFile(CBI_CTR_DOT_DLL);
                  endif;
                  if ( Is( FILE_EXISTS, TARGETDIR ^ NTUI_DOT_EXE) ) then
                      DeleteFile(NTUI_DOT_EXE);
                  endif;
        endswitch;
        VarRestore( SRCTARGETDIR );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CreateProgramFolderAndIcons
 *
 *  Purpose:  Create application Program Folder and Icons
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function CreateProgramFolderAndIcons()
STRING szItemName, szProgram, szParam, szWrkPath, szIconPath;
LONG lResult, lGroupItemType, lItems, lType;
begin
        LoadString128(SETUP_CREATE_FOLDER_ICONS, szBuf);
        SetStatusWindow( -1, szBuf );
        if (DEBUG) then
           if (szAppPath = "") then
               SprintfBox( SEVERE, "CreateProgramFolderAndIcons()",
               "ASSERT: szAppPath not NULL\n" + "szAppPath=[%s]", szAppPath);
           endif;
        endif;
        AppCommand( PROGMAN, CMD_RESTORE );
        TARGETDIR = szAppPath;
        // On NT systems
        if (SysInfo.lOS = IS_WINDOWSNT) then
            // Trash shortcut icon with name 'N2000' or 'NETMAN'
            // or 'OLD_APP_NAME' OR 'Netman' that the
            // user may have drug to the desktop with location
            // X:\winnt\Profiles\username\Desktop
            ProgDefGroupType(PERSONAL);
            DeleteFolderIcon(FOLDER_DESKTOP, NETMAN);
            DeleteFolderIcon(FOLDER_DESKTOP, N2000);
            DeleteFolderIcon(FOLDER_DESKTOP, OLD_APP_NAME);   // req wls 12/2/97
            DeleteFolderIcon(FOLDER_DESKTOP, "Netman");       // req wls 12/2/97
            DeleteFolderIcon(FOLDER_DESKTOP, "netman");       // req wls 12/2/97
            //  Make program group 'COMMON'
            // Only users with Administrator rights can create Common groups
            ProgDefGroupType(COMMON);
        endif;
        lResult = CreateProgramFolder( PROGRAM_FOLDER_NAME );
        // For NT3.51 place a Program Group on the desktop with
        // Icons inside it
        // For W95 place a folder on the Start Programs Menu with
        // the folder's contents (the app's executable icons)
        // accessible through a submenu.
        // Note! This is NOT M'soft's recommended practice.
        DeleteFolderIcon(PROGRAM_FOLDER_NAME, OLD_APP_NAME);
        szItemName = N2000;
        szProgram  = TARGETDIR ^ PRODUCT_KEY;
        szParam    = "";
        szIconPath = TARGETDIR ^ N2000_DOT_EXE;
        AddIcon( szItemName, szProgram, szParam, TARGETDIR, szIconPath);
        // Add N2000 shortcut icon to the desktop
        // (with location X:\winnt\Profiles\AllUsers\Desktop under NT 4.0).
        // Note: FOLDER_DESKTOP supported on W95 and NT4.0 only.
        if ( SysInfo.lOS = IS_WINDOWS95 ||
             (SysInfo.lOS = IS_WINDOWSNT && SysInfo.lWinMajor > 3)
           ) then
            AddFolderIcon(FOLDER_DESKTOP, szItemName, szProgram,
                          TARGETDIR, szIconPath, 0, szItemName, REPLACE);
        endif;
        DeleteFolderIcon(PROGRAM_FOLDER_NAME, OLD_APP_NAME + " " + "README");
        szItemName = N2000 + " " + "README";
        szProgram  = WINDIR ^ "notepad";
        szParam    = TARGETDIR ^ "readme.txt";
        szIconPath = NOTEPAD_DOT_EXE;              // Windows directory
        AddIcon( szItemName, szProgram, szParam, TARGETDIR, szIconPath);
        DeleteFolderIcon(PROGRAM_FOLDER_NAME, OLD_APP_NAME + " " + "Help");
        szItemName = N2000 + " " + "Help";
        szProgram  = WINDIR ^ "winhelp";
        szParam    = TARGETDIR ^ N2000 + DOT_HLP;
        szIconPath = WINHELP_DOT_EXE;              // Windows directory
        AddIcon( szItemName, szProgram, szParam, TARGETDIR, szIconPath);
        szItemName = "DB Convert";
        szProgram  = TARGETDIR ^ DBCONVW_DOT_EXE;
        szParam    = "";
        szIconPath = TARGETDIR ^ DBCONVW_DOT_EXE;
        AddIcon( szItemName, szProgram, szParam, TARGETDIR, szIconPath);
        szItemName = "DB Fix";
        szProgram  = TARGETDIR ^ DBFIX_DOT_EXE;
        szParam    = "";
        szIconPath = TARGETDIR ^ DBFIX_DOT_EXE;
        AddIcon( szItemName, szProgram, szParam, TARGETDIR, szIconPath);
        // Dbsave.bat obsoleted with Version 3.0
        DeleteFolderIcon(PROGRAM_FOLDER_NAME, "DB Save");
        // UNINST global variable stores the file name
        // (with full path) of the uninstaller file.
        DeleteFolderIcon(PROGRAM_FOLDER_NAME,
                         "unInstall" + " " + OLD_APP_NAME);
        szItemName = "unInstall" + " " + N2000;
        szProgram  = UNINST;
        szParam    = "-f" + svUninstLogFile;
        szIconPath = UNINST_DOT_EXE;               // Windows directory
        AddIcon( szItemName, szProgram, szParam, WINDIR, szIconPath);
        // Add a new icon to edit the N2000.ini
        szItemName = "Edit " + CONS_CONFIG_FILE_NAME;
        szProgram  = WINDIR ^ "notepad";
        szParam    = TARGETDIR ^ CONS_CONFIG_FILE_NAME;
        szIconPath = NOTEPAD_DOT_EXE;              // Windows directory
        AddIcon( szItemName, szProgram, szParam, TARGETDIR, szIconPath);
        // Add Event Viewer shortcut icon to NT4.0 desktops
        // Note: FOLDER_DESKTOP not supported on NT3.51.
        if ( SysInfo.lOS = IS_WINDOWSNT && SysInfo.lWinMajor > 3) then
            szProgram = WINSYSDIR ^ "EVENTVWR.EXE";
            AddFolderIcon(FOLDER_DESKTOP, "Event Viewer", szProgram,
                          "", szIconPath, 0, "", REPLACE);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  AddIcon( szItemName, szProgram, szParam, szWorkingDir,
 *                     szIconPath )
 *
 *  Purpose:  Service function for CreateFoldersAndIcons
 *
 *    Input:  szAppPath   - Fully qualified path to the n2000 directory
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function AddIcon( szItemName, szProgram, szParam, szWorkingDir, szIconPath )
STRING szCommandLine, szErr, szExpand, szMsg1, szMsg2, szTitle;
LONG lResult;
begin
        LongPathToQuote(szProgram, TRUE);
        LongPathToShortPath(szParam);
        szCommandLine= szProgram + " " + szParam;
        lResult = AddFolderIcon( PROGRAM_FOLDER_NAME,
                                 szItemName, szCommandLine, szWorkingDir,
                                 szIconPath, 0, "", REPLACE );
        if (lResult < 0) then
            LoadString128(SETUP_ADD_ICON, szTitle);
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_UNABLE_TO_CREATE_ICON, szMsg1);
            LoadString128(SETUP_COMMAND_LINE, szMsg2);
            szExpand = szErr + szMsg1 + "\n" + szMsg2;
            SprintfBox( SEVERE, szTitle, szExpand, szItemName, szCommandLine);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  SetAppIconInStartupFolder ( bAddAppIconToStartupGrp )
 *
 *  Purpose:  Add or delete n2000 icon from the startup group
 *            depending on what the user want's.
 *
 *    Input:  szAppPath   - Fully qualified path to the netman directory
 *
 *  Returns:
 *
 * Comments:
\*---------------------------------------------------------------------------*/
function SetAppIconInStartupFolder( bAddAppIconToStartupGrp )
STRING szMsg, szExpand, szIconPath;
begin
        if (DEBUG) then
           if (szAppPath = "") then
               SprintfBox( SEVERE, "SetAppIconInStartupFolder()",
               "ASSERT: szAppPath not NULL\n" + "szAppPath=[%s]", szAppPath);
           endif;
        endif;
        AppCommand( PROGMAN, CMD_RESTORE );
        TARGETDIR = szAppPath;
        //Always delete the app Icon.
        // From the user's personal startup group
        ProgDefGroupType(PERSONAL);
        DeleteFolderIcon( "STARTUP", OLD_APP_NAME );  // Pre-2.5
        DeleteFolderIcon( "STARTUP", N2000 );         // 2.5 and later
        DeleteFolderIcon( "STARTUP", NETMAN);         // req wls 12/2/97
        DeleteFolderIcon( "STARTUP", "Netman");       // req wls 12/2/97
        DeleteFolderIcon( "STARTUP", "netman");       // req wls 12/2/97
        // From common startup group
        ProgDefGroupType(COMMON);
        DeleteFolderIcon( "STARTUP", OLD_APP_NAME );  // Pre-2.5
        DeleteFolderIcon( "STARTUP", N2000 );         // 2.5 and later
        DeleteFolderIcon( "STARTUP", NETMAN);         // req wls 12/2/97
        DeleteFolderIcon( "STARTUP", "Netman");       // req wls 12/2/97
        DeleteFolderIcon( "STARTUP", "netman");       // req wls 12/2/97
        //Add it back if necessary
        LoadString128(SETUP_ADDING_ICON_TO_FOLDER, szExpand);
        Sprintf(szMsg, szExpand, N2000);
        SetStatusWindow( -1, szMsg );
        if ( bAddAppIconToStartupGrp ) then
            szIconPath = TARGETDIR ^ N2000_DOT_EXE;
            AddFolderIcon( "STARTUP",
                           N2000, TARGETDIR ^ PRODUCT_KEY, TARGETDIR,
                           szIconPath, 0, "", REPLACE );
            Delay( 1 );
        endif;
        // Close the folder
        ShowGroup( "STARTUP", SW_SHOWMINNOACTIVE);
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  BackupDatabaseFiles()
 *
 *  Purpose:  Backup the user's database files.
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *
 *  Returns:  BACK if a copy error occurred - eg out of space on target.
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function BackupDatabaseFiles()
STRING szFn, szTitle, szMsg, szMsg1, szMsg2, szErr, szExpand,
       szCurDir, szProgram, szDestDir, szDb;
LONG   lValue, lReturn;
BOOL   bBackupDB;
begin
        LoadString128(SETUP_BACKUP_DB_FILES_TITLE, szTitle);
        SetDialogTitle(DLG_ASK_OPTIONS, szTitle);
        Disable (BACKBUTTON);               // } We're committed now
        Disable (CANCELBUTTON);             // }
        lValue = NONEXCLUSIVE;              // Check Box
        bBackupDB =  FALSE;
        LoadString128(SETUP_BACKUP_DB_FILES_MSG1, szMsg1);
        LoadString128(SETUP_BACKUP_DB_FILES_MSG2, szMsg2);
        szMsg  = szMsg1 + " " + szMsg2;
        LoadString128(SETUP_BACKUP_DB_FIRST, szMsg1);
        szMsg2 = "&" + szMsg1;
        AskOptions(lValue, szMsg, szMsg2, bBackupDB);
        if (bBackupDB = TRUE) then
            szFn = "BackupDatabaseFiles()";
            WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
            WriteLogMsg( " " );
            WriteLogMsg( szFn + "User requested backup of his databases");
            // Set the current directory....
            szCurDir = szAppPath;
            StrRemoveLastSlash(szCurDir);
            if (ChangeDirectory(szCurDir) < 0) then
                LoadString128(SETUP_ERROR, szErr);
                LoadString128(SETUP_NOT_IN_CURRENT_DIR, szMsg);
                szExpand = szErr + szMsg;
                SprintfBox( SEVERE, szTitle, szExpand,
                                             szAppPath, "CopyDbFiles()" );
            endif;
            LoadString128(SETUP_CHOOSE_DEST_DIR_TITLE, szTitle);
            LoadString128(SETUP_CHOOSE_DEST_DIR_MSG, szMsg);
            // IS3Bug : Setting the default to a: triggers InstallShield
            //          to generate a wierd dialog. In any case a user
            //          with any reasonable size databases is likely to
            //          want to back up to a hard disc.
            szDestDir = "C:\\";
            SelectDir(szTitle, szMsg, szDestDir, FALSE);
            VarSave( SRCTARGETDIR );
            SRCDIR = szAppPath;
            TARGETDIR = szDestDir;
            if ( CopyDbFiles() = BACK) then
                VarRestore ( SRCTARGETDIR );
                return (BACK);
            endif;
            VarRestore( SRCTARGETDIR );
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  CopyDbFiles()
 *
 *  Purpose:  This function copies a set of database (.DBF, .CDX, .FPT)
 *            files and also .DAT files (if there are any) to the specified
 *            destination directory.
 *
 *  Assumes:  SRCDIR and TARGETDIR are set appropriately on entry.
 *            status window is enabled.
 *
 *    Input:
 *
 *  Returns:  BACK if any copy fails
 *
 * Comments:  See DCS1113 . InstallShield rubs out icons and undoes other
 *            stuff if it exits thru its own exit handler after a failed
 *            copy (because of lack of disk space on the target).
 *            We can trap this in CopyError and BACK up.
 *
 *            If one file DID copy, then IS's behaviour is different the
 *            second time around, and it offers the 'not-enough-space-on-
 *            target-to-do-copy dialog it should have done in the first
 *            place. Which is worse, because the user can exit without
 *            us being able to trap it and run out icons etc...
 *
\*---------------------------------------------------------------------------*/
function CopyDbFiles()
STRING szFn, szMsg, szTmp;
begin
        szFn = "CopyDbFiles()";
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        WriteLogMsg( " " );
        LoadString128(SETUP_COPYING_FILES_TO_DIR, szTmp);
        Sprintf( szMsg, szTmp, SRCDIR ^ AST_DOT_DBF, TARGETDIR);
        SetStatusWindow( -1, szMsg );
        if ( GaCopyFile(AST_DOT_DBF, AST_DOT_DBF, TRUE,TRUE,FALSE) = BACK) then
            return (BACK);
        else
            WriteLogMsg( szFn + szMsg);
        endif;
        LoadString128(SETUP_COPYING_FILES_TO_DIR, szTmp);
        Sprintf( szMsg, szTmp, SRCDIR ^ AST_DOT_CDX, TARGETDIR);
        SetStatusWindow( -1, szMsg );
        if ( GaCopyFile(AST_DOT_CDX, AST_DOT_CDX, TRUE,TRUE,FALSE) = BACK) then
            return (BACK);
        else
            WriteLogMsg( szFn + szMsg);
        endif;
        LoadString128(SETUP_COPYING_FILES_TO_DIR, szTmp);
        Sprintf( szMsg, szTmp, SRCDIR ^ AST_DOT_FPT, TARGETDIR);
        SetStatusWindow( -1, szMsg );
        if ( GaCopyFile(AST_DOT_FPT, AST_DOT_FPT, TRUE,TRUE,FALSE) = BACK) then
            return (BACK);
        else
            WriteLogMsg( szFn + szMsg);
        endif;
        // Copy .DAT files if there are any
        if ( Is( FILE_EXISTS,  SRCDIR ^ AST_DOT_DAT) ) then
            LoadString128(SETUP_COPYING_FILES_TO_DIR, szTmp);
            Sprintf( szMsg, szTmp, SRCDIR ^ AST_DOT_DAT, TARGETDIR);
            SetStatusWindow( -1, szMsg );
            if ( GaCopyFile(AST_DOT_DAT, AST_DOT_DAT,
                                                 TRUE,TRUE,FALSE) = BACK ) then
                return (BACK);
            else
                WriteLogMsg( szFn + szMsg);
            endif;
        endif;
        WriteLogMsg( " " );
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  ConvertDatabaseFiles
 *
 *  Purpose:  Run dbConvert on the user's database files.
 *            If they're up-to-date nothing happens to them.
 *            If not they get fixed up.
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *
 *  Returns:
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function ConvertDatabaseFiles()
STRING szFn, szCurDir, szProgram, szCmdLine, szErr, szReason, szMsg, szExpand;
LONG lErr;
begin
        szFn = "ConvertDatabaseFiles()";
        LoadString128(SETUP_CONVERT_DB_STATUS, szMsg);
        SetStatusWindow( -1, szMsg );
        // Don't know if DBConvw has bad habits like DBFix (see below)
        // but on general suspicion, set the current directory....
        szCurDir = szAppPath;
        StrRemoveLastSlash(szCurDir);
        if (ChangeDirectory(szCurDir) < 0) then
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_NOT_IN_CURRENT_DIR, szExpand);
            Sprintf(szReason, szExpand, szAppPath, DBCONVW_DOT_EXE);
            Sprintf(szMsg, "%s%s", szErr, szReason);
            SprintfBox( SEVERE, szFn, szMsg);
        endif;
        if (SysInfo.lOS = IS_WINDOWSNT) then
           LoadString128(SETUP_CONVERTING_DB_PLSE_WAIT, szExpand);
           Sprintf(szMsg, szExpand, DBCONVW_DOT_EXE);
           SdShowMsg(szMsg, TRUE);
        endif;
        szProgram = szAppPath ^ DBCONVW_DOT_EXE;
        szCmdLine = "";
        lErr = LaunchAppAndWait(szProgram, szCmdLine, WAIT);
        if (SysInfo.lOS = IS_WINDOWSNT) then
           SdShowMsg(szMsg, FALSE);
        endif;
        if (lErr <= 0) then
            // DBConvw did not run
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_UNABLE_TO_RUN_PROGRAM, szExpand);
            Sprintf(szReason, szExpand, szProgram);
            Sprintf(szMsg, "%s%s", szErr, szReason);
            SprintfBox( SEVERE, szFn, szMsg);
            WriteLogMsg( szFn + " " + szMsg);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  RunDBFix()
 *
 *  Purpose:  Run dbFix on the user's database files.
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *
 *  Returns:
 *
 * Comments:  DBFix has bad habits.  It assumes when it starts running
 *            that the current directory will be the application directory.
 *            If it isn't then dbfix.log, serial.dbc, and serial.cdx will
 *            get written wherever the 'current' directory happens to be.
 *
 *            Under NT after Dbfix completes, there is a long timeout
 *            before the script gets back control.
 *            To cover this we display a keep-the-user-happy-sceeen.
 *
\*---------------------------------------------------------------------------*/
function RunDBFix()
STRING szFn, szTitle, szCurDir, szProgram, szCmdLine, szMsg, szErr, szExpand;
LONG   lErr;
begin
        szFn = "RunDBFix()";
        LoadString128(SETUP_RUN_DBFIX_STATUS, szMsg);
        SetStatusWindow( -1, szMsg );
        szProgram = szAppPath ^ DBFIX_DOT_EXE;
        // Protect ourselves against DBFix
        szCurDir = szAppPath;
        StrRemoveLastSlash(szCurDir);
        if (ChangeDirectory(szCurDir) < 0) then
            // DBFix used to crap gawd knows where
            LoadString128(SETUP_CREATING_APP_DB_FILES, szBuf);
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_NOT_IN_CURRENT_DIR, szExpand);
            Sprintf( szMsg, szExpand, szAppPath, DBFIX_DOT_EXE);
            SprintfBox( SEVERE, szTitle, "%s%s\n%s", szErr, szMsg, szBuf);
            WriteLogMsg( szFn + " " + szErr + szMsg);
        endif;
        // Ensure DBFix doesn't alarm the first time buyers
        if (bNewInstall = TRUE && bExistingApp = FALSE) then
            szCmdLine = " -quiet";
        else
            szCmdLine = "";
        endif;
        if (SysInfo.lOS = IS_WINDOWSNT) then
           LoadString128(SETUP_RUN_DBFIX_MSG, szMsg);
           SdShowMsg(szMsg, TRUE);
        endif;
        lErr = LaunchAppAndWait(szProgram, szCmdLine, WAIT);
        if (SysInfo.lOS = IS_WINDOWSNT) then
           SdShowMsg(szMsg, FALSE);
        endif;
        if( lErr <= 0) then
            // DBFix did not run
            LoadString128(SETUP_ERROR, szErr);
            LoadString128(SETUP_UNABLE_TO_RUN_PROGRAM, szExpand);
            Sprintf(szMsg, szExpand, szProgram);
            SprintfBox( SEVERE, szFn, "%s%s", szErr, szMsg );
            WriteLogMsg( szFn + " " + szErr + szMsg);
        endif;
end;
/*---------------------------------------------------------------------------*\
 *
 * Function:  BugUserToReadTheReadmeFile()
 *
 *  Purpose:  Almost the last chance to torment the user.
 *
 *    Input:  szAppPath   - Fully qualified path to the application directory
 *
 *  Returns:
 *
 * Comments:
 *
\*---------------------------------------------------------------------------*/
function BugUserToReadTheReadmeFile()
STRING szMsg, szProgram, szCmdLine, szReadme;
LONG lValue, lReturn;
BOOL bReadReadme;
begin
        LoadString128(SETUP_READ_README_DLG_TITLE, szReadme);
        SetDialogTitle(DLG_ASK_OPTIONS, szReadme);
        Disable (BACKBUTTON);
        Disable (CANCELBUTTON);
        lValue = NONEXCLUSIVE;
        bReadReadme = TRUE;
        LoadString128(SETUP_READ_README_MSG, szReadme);
        Sprintf(szMsg, szReadme, N2000);
        LoadString128(SETUP_READ_README_CB_OPTION, szReadme);
        AskOptions(lValue, szMsg, "&" + szReadme, bReadReadme);
        if ( bReadReadme ) then
            szProgram = NOTEPAD_DOT_EXE;
            if (CDROM_INSTALL) then
                szCmdLine = szSrcPath ^ N2000 ^ README_DOT_TXT;
            else
                szCmdLine = szAppPath ^ README_DOT_TXT;
            endif;
            LaunchAppAndWait( szProgram, szCmdLine, WAIT );
            Delay(2);
        endif;
end;
/*-----------------------------------------------------------------------
 *
 * Function:  RebootRestartExit()
 *
 *  Purpose:  - If Windows doesent need to be restarted or the machine
 *                 re-booted, and the user didn't select Map, and if he
 *                 did he does have a big enough swap file, then we can go
 *                 for normal exit.
 *
 *                 Just display advice on how to run N2000 afterwards,
 *                 and return 0.
 *
 *            - If not give the user appropriately sage advice:
 *
 *                 - Tell the user to read the following carefully!
 *
 *                 - Tell him that his machine will have to be re-started
 *                   or re-booted before he can run N2000.
 *
 *                 - If we trashed his swapfile tell the user to expect a
 *                   'Corrupt Swap File' message when Windows starts up
 *                   again, and not to sweat it.
 *
 *                 - If he needs to use Control Panel to setup the perm
 *                   swap file tell him and point him at readme for
 *                   more info.
 *
 *                 - Lastly display advice on how to run netman afterwards,
 *
 *                 - Return a boot or restart code
 *
 *    Input:  bReboot, bRestartWin,
 *            SysInfo.lOS
 *
 *  Returns:  lRebootRestartExit     SYS_BOOTWIN - Restart (W31 only)
 *                                   SYS_BOOTMACHINE - Reboot machine
 *                                   0 - do not reboot or restart Win
 *                                   BACK - to the readme
 *
 * Comments:
 *
\*-----------------------------------------------------------------------*/
function RebootRestartExit()
STRING szTitle, szMsg, szSwapMb, szSwapKb, szAction, szInfo;
LIST   listInfo;
LONG   lReturn, lMinWinSwapFileMb;
begin
        Enable (BACKBUTTON);                // For user to backup to the readme
        Disable (CANCELBUTTON);             // We're committed now
        // Create a string list and add lines to it
        listInfo = ListCreate(STRINGLIST);
        if ( bReboot = FALSE && bRestartWin = FALSE ) then
            lReturn = FALSE;                // Can just exit
            // 'To run N2000'
            LoadString128(SETUP_PRE_EXIT_TITLE, szMsg);
            Sprintf(szTitle, szMsg, ITEM_APP);
        else
            // On Win95 or WinNT machines, InstallShield
            // automatically ups a restart to a reboot
            if (bReboot = TRUE ||
                SysInfo.lOS = IS_WINDOWS95 ||
                SysInfo.lOS = IS_WINDOWSNT) then
                // 'Setup will next ask you to reboot your machine'
                LoadString128(SETUP_PRE_EXIT_MSG7, szInfo);
                lReturn = SYS_BOOTMACHINE;
            else
               // 'Setup will next ask you to restart Windows'
                LoadString128(SETUP_PRE_EXIT_MSG8, szInfo);
                lReturn  = SYS_BOOTWIN;
            endif;
            ListAddString(listInfo, szInfo, AFTER);
            ListAddString(listInfo, "", AFTER);
        endif;
        // Give user instructions for running the program
        ListAddString(listInfo, "", AFTER);
        // 'To run the installed program'
        LoadString128(SETUP_PRE_EXIT_MSG1, szInfo);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, "", AFTER);
        switch (SysInfo.lOS)
            case IS_WINDOWS95:
                 // 'On the 'Start Programs' menu select 'Programs'
                 LoadString128(SETUP_PRE_EXIT_MSG2, szInfo);
                 ListAddString(listInfo, szInfo, AFTER);
                 // 'Select Glenayre Solutions'
                 LoadString128(SETUP_PRE_EXIT_MSG3, szMsg);
                 Sprintf(szInfo, szMsg, PROGRAM_FOLDER_NAME);
                 ListAddString(listInfo, szInfo, AFTER);
                 // 'Double-click on the N2000 icon on the sub-menu'
                 LoadString128(SETUP_PRE_EXIT_MSG4, szMsg);
                 Sprintf(szInfo, szMsg, N2000);
                 ListAddString(listInfo, szInfo, AFTER);
            case IS_WINDOWSNT:
                  // 'Double-click on the Glenayre Solutions program group icon'
                 LoadString128(SETUP_PRE_EXIT_MSG5, szMsg);
                 Sprintf(szInfo, szMsg, PROGRAM_FOLDER_NAME);
                 ListAddString(listInfo, szInfo, AFTER);
                 // 'Double-click on the N2000 icon'
                 LoadString128(SETUP_PRE_EXIT_MSG6, szMsg);
                 Sprintf(szInfo, szMsg, N2000);
                 ListAddString(listInfo, szInfo, AFTER);
        endswitch;
        //IS3BUGFIX - Suppress dup last line
        ListAddString(listInfo, " ", AFTER);
        // Display the dialog
        LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
        if (SdShowInfoList (szTitle, szMsg, listInfo) = BACK) then
            lReturn = BACK;
        endif;
        ListDestroy( listInfo );
        return (lReturn);
end;
/*-----------------------------------------------------------------------
 *
 *  Function: ExecuteFinishReboot( lRebootRestartExit )
 *
 *  Purpose : Set up and execute a SdFinishReboot dialog reminding the user
 *            (again) whether a reboot or restart is necessary before he
 *            can run N2000.
 *
 *    Input:  lRebootRestartExit  SYS_BOOTWIN     - Restart (W31 only)
 *                                SYS_BOOTMACHINE - Reboot machine
 *
 *  Returns:  BACK  - to goback to the 'Before you...' dialog or hopefully
 *            not!  Finally a stake thru the heart of this monster...
 *
 * Comments:  NOTE! /ISHIELD/DIALOGS/INCLUDE/SDFINBOT.RUL modified
 *            to stop the BACK button being disabled.
 *            We want the user to be able to re-visit the previous
 *            RebootRestartExit() advisory panel!
 *
\*------------------------------------------------------------------------*/
function ExecuteFinishReboot( lRebootRestartExit )
STRING szTitle, szMsg, szFailed, szLater, szMsg1, szMsg2, szBuf1, szBuf2;
LONG lReturn;
begin
        Enable  (BACKBUTTON);               // So user can backup to advice
        Disable (CANCELBUTTON);             // We're committed now
        if (DEBUG) then
           if ( lRebootRestartExit != SYS_BOOTWIN &&
                lRebootRestartExit != SYS_BOOTMACHINE ) then
               SprintfBox( SEVERE, "ExecuteFinishReboot()",
                           "ASSERT: lRebootRestartExit=[%d] not " +
                           "SYS_BOOTWIN or SYS_BOOTMACHINE",
                           lRebootRestartExit);
           endif;
        endif;
        LoadString128(SETUP_FINISH_TITLE, szTitle);
        SetStatusWindow( 100, szTitle );
        SdProductName( PRODUCT_NAME );
        if (BATCH_INSTALL = TRUE) then
           LoadString128(SETUP_LOCKED_FILES_IN_USE, szBuf1);
           LoadString128(SETUP_SHUTDOWN_BEFORE_RUN_APP, szBuf2);
           szMsg = szBuf1 + " " + szBuf2;
           CommitSharedFiles(0);
           RebootDialog( "", szMsg, lRebootRestartExit );
        else
           // 'Setup is complete.
           // 'You MUST [restart Windows/reboot your machine] before ...'
           LoadString128(SETUP_IS_COMPLETE, szBuf1);
           if ( lRebootRestartExit = SYS_BOOTMACHINE ) then
               LoadString128(SETUP_REBOOT_BEFORE_RUN_APP, szBuf2);
               LoadString128(SETUP_REBOOT_FAILED, szFailed);
               LoadString128(SETUP_REBOOT_LATER, szLater);
           else
               LoadString128(SETUP_RESTART_BEFORE_RUN_APP, szBuf2);
               LoadString128(SETUP_RESTART_FAILED, szFailed);
               LoadString128(SETUP_RESTART_LATER, szLater);
           endif;
           szMsg1 = szBuf1 + "\n\n" + szBuf2 + "\n\n";
           // 'Remove any disks from their drives.
           //  Click Finish to complete Setup.'
           LoadString128(SETUP_REMOVE_DISK, szBuf1);
           LoadString128(SETUP_CLICK_FINISH, szBuf2);
           szMsg2 = szBuf1 + " " + szBuf2;
           // Close log in case we don't come back after a reboot
           CloseLogFile();
           // Display the sdFinishReboot dialog box
           lReturn = SdFinishReboot( szTitle, szMsg1, lRebootRestartExit,
                                     szMsg2, 0);
           // Test the value of lReturn and respond accordingly
           if (lReturn < 0 ) then
               MessageBox( szFailed, INFORMATION);
           elseif ( lReturn = NEXT) then
               MessageBox( szLater, INFORMATION);
           endif;
           if (lReturn = BACK ) then
               return (lReturn);
           endif;
        endif;
        // And we're clear!
end;
/*-----------------------------------------------------------------------
   Name    : SetupError
   Purpose : This function displays an error message
             and shuts down the setup.
-------------------------------------------------------------------------*/
function SetupError( szMsg )
STRING szFn;
begin
        szFn = "SetupError()";
        SprintfBox( SEVERE, szFn, szMsg);
        ExitHandler();
end;
//---------------------------------------------------------------------
//
//  Name   :  GaCopyFile( szSrcFile, szTargetFile, bBack, bNext, bCancel)
//
//  Descrip:  This procedure wraps InstallShield's CopyFile function
//            to provide support for post transfer checks that the
//            files transferred have arrived on the target disk the
//            same size they started off.
//
//  Inputs :  SRCDIR
//            TARGETDIR
//            szSrcFile
//            szTargetFile
//            BOOL Back, Next, Cancel button enable.
//
//  Output :  listCopiedFiles   Updated with fully qualified src-target
//                              for any file InstallShield DIDN'T have
//                              trouble with (apparently).
//
//  Comment:  DO NOT call this function from within a Fileset!
//            When called from within a FileSet, SRCDIR does NOT
//            contain a valid value.
//
//---------------------------------------------------------------------
function GaCopyFile( szSrcFile, szTargetFile, bBack, bNext, bCancel )
LONG lCPRet, lCERet;
begin
            lCPRet = CopyFile(szSrcFile, szTargetFile);
            if LAST_RESULT < 0 then
                lCERet = CopyError(szSrcFile, lCPRet, bBack, bNext, bCancel);
                if (lCERet = BACK) then
                    return (lCERet);
                endif;
            else
                ListAddString(listCopiedFiles,
                              SRCDIR ^ szSrcFile + " " +
                              TARGETDIR ^ szTargetFile,
                              AFTER);
            endif;
end;
//---------------------------------------------------------------------
//
//  Name   :  GaXCopyFile(szSrcDir, szSrcFile, szTargetFile, lOp)
//
//  Descrip:  This procedure wraps InstallShield's XCopyFile function
//            to provide support for post transfer checks that the
//            files transferred have arrived on the target disk the
//            same size they started off.
//
//  Inputs :  szSrcDir
//            TARGETDIR
//            szSrcFile
//            szTargetFile
//            lOp
//
//  Output :  listCopiedFiles   Updated with fully qualified src-target
//                              for any file InstallShield DIDN'T have
//                              trouble with (apparently).
//
//  Comment:  When called from within a FileSet, SRCDIR does NOT
//            contain a valid value. This must be supplied explicitly
//            as the first parameter.
//
//---------------------------------------------------------------------
function GaXCopyFile( szSrcDir, szSrcFile, szTargetFile, lOp )
LONG lReturn;
begin
            lReturn = XCopyFile(szSrcFile, szTargetFile, lOp);
            if LAST_RESULT < 0 then
                CopyError(szSrcDir ^ szSrcFile, lReturn, FALSE, TRUE, TRUE);
            else
                ListAddString(listCopiedFiles,
                              szSrcDir ^ szSrcFile + " " +
                              TARGETDIR ^ szTargetFile,
                              AFTER);
            endif;
end;
//---------------------------------------------------------------------
//
//  Name   :  GaCompressGet(szLibrary, szTargetFiles, lOptions)
//
//  Descrip:  This procedure wraps InstallShield's CompressGet function
//            to provide support for post transfer checks that the
//            files transferred have arrived on the target disk the
//            same size they started off.
//
//  Inputs :  SRCDIR            path to library file eg 'A:\'
//            TARGETDIR
//            szLibrary         library file generically 'data.z'
//            szTargetFiles     source files in library eg 'N2000\*.*'
//            lOptions
//
//  Output :  listCopiedFiles   Updated with fully qualified src-target
//                              for any file InstallShield DIDN'T have
//                              trouble with (apparently).
//
//  Comment:  CompressGet does not use SRCDIR. Instead it used the
//            misnamed szTargetFiles to locate it's source files.
//            See the code comments in SetupFileTransfer().
//
//---------------------------------------------------------------------
function GaCompressGet( szLibrary, szTargetFiles, lOptions )
LONG lCGRet, lCERet;
begin
            lCGRet = CompressGet(szLibrary, szTargetFiles, lOptions);
            if LAST_RESULT < 0 then
                lCERet = CopyError(ERRORFILENAME, lCGRet, FALSE, TRUE, TRUE);
                if (lCERet = BACK) then
                    return (lCERet);
                endif;
            else
                ListAddString(listCopiedFiles,
                              szLibrary + ":" + szTargetFiles +
                              " " +
                              TARGETDIR + "\\" + ASK_DOT_ASK,
                              AFTER);
            endif;
end;
//---------------------------------------------------------------------
//
//    CopyError (szErrFile, lCopyFileReturnValue, bBack, bNext, bCancel);
//
//    Descrip:  This procedure will display an error when InstallSHIELD
//              is unable to decompress or copy a file, and let the user
//              decide if he wants to keep going.
//
//              We do NOT automatically exit and fail the setup.
//              Customer support can hand hold the user for a copy if
//              it's just one file...
//
//    Notes:    CopyFile seems to have a nice unadvertised behaviour in
//              that if it detects an attempt to copy over a Read Only
//              file, you get this messagebox:
//
//              ..................................................
//              :            Read Only File Detected             :
//              :................................................:
//              :   A read only file [filename] was found while  :
//              :   attempting to copy files to the destination  :
//              :   directory. To overwrite the file, click the  :
//              :   Yes button, otherwise click the No button.   :
//              :                                                :
//              :   [ ] Don't show this message again            :
//              :                                                :
//              :               [Yes]   [No]                     :
//              :                                                :
//              .................................................:
//
//
//    Errors:   The following table was assembled the easy InstallShield way.
//              That is we dumped ishield\program\isdbg.dll to get a list
//              of constants, verified their  values by displaying them
//              in a SprintfBox, and added more entries (labeled CDOC_)
//              which the debugger doesn't know about but are documented in
//              the reference 'CopyFile and XCopyFile' page.
//
//              Wot the missing error codes are only InstallShield know.
//
//              A nice touch is the assigning of literal COMP_DONE to -1
//              which Installshield define as 'Unknown Error' in this case
//              and in general use to indicate an function error return!
//
//               -1 COMP_DONE
//               -2 COMP_ERR_OPENINPUT             COPY_ERR_OPENINPUT
//               -3 COMP_ERR_OPENOUTPUT            COPY_ERR_OPENOUTPUT
//               -4 COMP_ERR_WRITE
//               -5 COMP_ERR_INTPUTNOTCOMPRESSED
//               -6 COMP_ERR_MEMORY                COPY_ERR_MEMORY
//               -7 COMP_ERR_HEADER
//               -9 COMP_ERR_DESTCONFLICT
//              -10 COMP_ERR_TARGET
//              -13 File cannot be found in the packaging list  (same as -39)
//              -19 COMP_ERR_INVALIDLIST
//              -20 COMP_ERR_OUTPUTNOTCOMPRESSED
//              -21 COMP_ERR_INCOMPATIBLE
//              -25 COMP_ERR_OPTIONS
//              -27 COMP_ERR_CREATEDIR             COMP_ERR_CREATEDIR
//              -29 ? Error occurred during defining a FileSet
//              -30 COMP_ERR_SPLIT
//              -31 COMP_ERR_FILETOOLARGE
//              -32 COMP_ERR_LAUNCHSERVER
//              -35 COMP_ERR_FILESIZE
//              -36 ? Private object error
//              -37 File transfer process aborted
//              -38 COMP_ERR_NODISKSPACE           COPY_ERR_NODISKSPACE
//              -39 File cannot be found in the packaging list  (same as -13)
//              -41 File is not in the compressed library       (same as -42)
//              -42 COMP_ERR_FILENOTINLIB
//              -46 COMP_ERR_TARGETREADONLY        COPY_ERR_TARGETREADONLY
//              -51 Self Registering DLL failed to register
//
//---------------------------------------------------------------------
function CopyError( szErrFile, lCopyFileReturnValue, bBack, bNext, bCancel )
STRING szErrLit, szMsg, szInfo, szFn;
LIST   listInfo;
LONG lErr, lResult;
begin
        szFn = "CopyError()";
        if (bBack = TRUE) then
             Enable (BACKBUTTON);
        else
             Disable (BACKBUTTON);
        endif;
        if (bNext = TRUE) then
             Enable (NEXTBUTTON);
        else
             Disable (NEXTBUTTON);
        endif;
        if (bCancel = TRUE) then
             Enable (CANCELBUTTON);
        else
             Disable (CANCELBUTTON);
        endif;
        // CopyFile, XCopyFile, Compress Get errors
        switch (lCopyFileReturnValue)
            case -1:
                 lErr = SETUP_ERR_UNKNOWN;
            case COMP_ERR_OPENINPUT:
                 lErr = SETUP_ERR_OPENINPUT;
            case COMP_ERR_OPENOUTPUT:
                 lErr = SETUP_ERR_OPENOUTPUT;
            case COMP_ERR_WRITE:
                 lErr = SETUP_ERR_WRITE;
            case COMP_ERR_INTPUTNOTCOMPRESSED:
                 lErr = SETUP_ERR_INPUTNOTCOMPRESSED;
            case COMP_ERR_MEMORY:
                 lErr = SETUP_ERR_MEMORY;
            case COMP_ERR_HEADER:
                 lErr = SETUP_ERR_HEADER;
            case COMP_ERR_DESTCONFLICT:
                 lErr = SETUP_ERR_DESTCONFLICT;
            case COMP_ERR_TARGET:
                 lErr = SETUP_ERR_TARGET;
            case -13:
                 lErr = SETUP_ERR_FILENOTINPACKINGLIST;
            case COMP_ERR_INVALIDLIST:
                 lErr = SETUP_ERR_INVALIDLIST;
            case COMP_ERR_OUTPUTNOTCOMPRESSED:
                 lErr = SETUP_ERR_OUTPUTNOTCOMPRESSED;
            case COMP_ERR_INCOMPATIBLE:
                 lErr = SETUP_ERR_INCOMPATIBLE;
            case COMP_ERR_OPTIONS:
                 lErr = SETUP_ERR_OPTIONS;
            case COMP_ERR_CREATEDIR:
                 lErr = SETUP_ERR_CREATEDIR;
            case -29:
                 lErr = SETUP_ERR_DEF_FILESET;
            case COMP_ERR_SPLIT:
                 lErr = SETUP_ERR_SPLIT;
            case COMP_ERR_FILETOOLARGE:
                 lErr = SETUP_ERR_FILETOOLARGE;
            case COMP_ERR_LAUNCHSERVER:
                 lErr = SETUP_ERR_LAUNCHSERVER;
            case COMP_ERR_FILESIZE:
                 lErr = SETUP_ERR_FILESIZE;
            case -36:
                 lErr = SETUP_ERR_PRIVATEOBJECT;
            case -37:
                 lErr = SETUP_ERR_FILETRANSFERPROCABORT;
            case COMP_ERR_NODISKSPACE:
                 lErr = SETUP_ERR_NODISKSPACE;
            case -39:
                 lErr = SETUP_ERR_FILENOTINPACKINGLIST;
            case -41:
                 lErr = SETUP_ERR_FILENOTINLIB;
            case COMP_ERR_FILENOTINLIB:
                 lErr = SETUP_ERR_FILENOTINLIB;
            case COMP_ERR_TARGETREADONLY:
                 lErr = SETUP_ERR_TARGETREADONLY;
            case -51:
                 lErr = SETUP_ERR_SELFREGDLLDIDNT;
            default:
                 lErr = SETUP_ERR_UNKNOWN;
        endswitch;
        LoadString128(lErr, szErrLit);
        // Create a string list.
        listInfo = ListCreate(STRINGLIST);
        LoadString128(SETUP_UNABLE_TO_COPY_FILE, szMsg);
        Sprintf(szInfo, "%s: %s", szMsg, szErrFile);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, " ", AFTER);
        LoadString128(SETUP_SOURCE_DIRECTORY, szMsg);
        Sprintf(szInfo, "%s=[%s]", szMsg, SRCDIR);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_TARGET_DIRECTORY, szMsg);
        Sprintf(szInfo, "%s=[%s]", szMsg, TARGETDIR);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, " ", AFTER);
        LoadString128(SETUP_REASON, szMsg);
        Sprintf(szInfo, "%s : %s", szMsg, szErrLit);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, " ", AFTER);
        LoadString128(SETUP_CALL_CUSTOMER_SERVICE, szMsg);
        Sprintf(szInfo, "%s :", szMsg);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_WITHIN_US, szMsg);
        Sprintf(szInfo, "   %s: %s", szMsg, CUST_SUPPORT_US_800_NO_DEF);
        ListAddString(listInfo, szInfo, AFTER);
        LoadString128(SETUP_OUTSIDE_US, szMsg);
        Sprintf(szInfo, "   %s: %s", szMsg, CUST_SUPPORT_US_US_TEL_DEF);
        ListAddString(listInfo, szInfo, AFTER);
        ListAddString(listInfo, " ", AFTER);
        if ( bBack = TRUE) then
            LoadString128(SETUP_CLICK_BACK_TO_RETRY, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
        endif;
        if ( bNext = TRUE ) then
            LoadString128(SETUP_CLICK_NEXT_TO_CONTINUE, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
        endif;
        if ( bCancel = TRUE) then
            LoadString128(SETUP_CLICK_CANCEL_TO_EXIT, szInfo);
            ListAddString(listInfo, szInfo, AFTER);
        endif;
        ListAddString(listInfo, " ", AFTER);
        // Display the dialog
        MessageBeep(0);
        LoadString128(SETUP_PLEASE_READ_BEFORE, szMsg);
        lResult = SdShowInfoList (szFn, szMsg, listInfo);
        ListDestroy( listInfo );
        // Always log the information
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE );
        lTmp = ListGetFirstString(listInfo, szInfo);
        while (lTmp = 0)
            WriteLogMsg ( szInfo );
            lTmp = ListGetNextString(listInfo, szInfo);
        endwhile;
        WriteLogMsg ( " ");
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        WriteLogMsg ( " ");
        if (lResult = CANCEL) then
           ExitHandler();
        endif;
        return lResult;
end;
/*-----------------------------------------------------------------------
 *
 * Function:  CheckFileTransfer()
 *
 *  Purpose:  Called after all files have been transferred from the source
 *            to the user target disk.
 *
 *            Checks that the target file has the same size as the source
 *            file ie that an InstallShield copy has not failed without
 *            warning.
 *
 *            If an error is detected the function displays a dialog
 *            listing the errors.  The user can decide whether to continue
 *            or exit the setup.
 *
 *    Input:  listCopiedFiles
 *
 *  Returns:
 *
 * Comments:  For CDrom installs all the data files are in one big data.z
 *            pot, and we can use GetFileInfo to get original source file
 *            sizes to compare with what made it to the disc.
 *
 *            For Diskette installs this is not possible.  The original
 *            data.z file has been split into data.1, data.2, data.3 etc
 *            files each on a different diskette.
 *
 *            To get round this, we have buildd.bat run
 *            icomp data.z -l > dataz.cmp
 *            to generate a file listing the original file sizes and names.
 *            Buildd.bat adds the dataz.cmp file to the _setup.lib library.
 *
 *            When setup runs the contents of _setup.lib are automatically
 *            unpacked into the working SUPPORTDIR library, so we have
 *            piggybacked onto this process to make dataz.cmp available.
 *
\*-----------------------------------------------------------------------*/
function CheckFileTransfer()
STRING szFn, szTitle, szMsg, szLcfLine, szInfo, szTmp, szExpand,
       szSrcPath, szSrcFile, szTargetPath, szTargetFile;
LONG lCheck;
LIST listInfo, listLine, listCmp;
BOOL bListCmpValid;
begin
        szFn = "CheckFileTransfer()";
        WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
        WriteLogMsg(szFn);
        Disable (BACKBUTTON);
        listInfo = ListCreate(STRINGLIST);  // Info Dialog
        if (listInfo = LIST_NULL ) then
            LoadString128(SETUP_FAILED_TO_CREATE, szExpand);
            Sprintf(szTmp, szExpand, "listInfo");
            if (DEBUG) then
                SprintfBox(SEVERE, szFn, szTmp );
            endif;
            WriteLogMsg(szFn + " " + szTmp);
            return;
        endif;
        listCmp = ListCreate(STRINGLIST);   // Diskette files+original sizes
        if (listCmp = LIST_NULL ) then
            LoadString128(SETUP_FAILED_TO_CREATE, szExpand);
            Sprintf(szTmp, szExpand, "listCmp");
            if (DEBUG) then
                SprintfBox(SEVERE, szFn, szTmp );
            endif;
            WriteLogMsg(szFn + " " + szTmp);
            return;
        endif;
        bListCmpValid = ParseDatazCmpToList( listCmp );
        // Process the copied files list.
        lCheck = ListGetFirstString(listCopiedFiles, szLcfLine);
        while (lCheck = 0)
            // Parse line
            listLine = ListCreate(STRINGLIST);  // Parsed LcfLine
            StrGetTokens(listLine, szLcfLine, " ");
            ListGetFirstString(listLine,  szTmp);
            ParsePath(szSrcPath, szTmp, PATH);
            ParsePath(szSrcFile, szTmp, FILENAME);
            ListGetNextString(listLine,  szTmp);
            ParsePath(szTargetPath, szTmp, PATH);
            ParsePath(szTargetFile, szTmp, FILENAME);
            ListDestroy( listLine );
            if (szSrcFile != ASK_DOT_ASK &&
                szTargetFile != ASK_DOT_ASK ) then
                // CopyFile format : D:\path\srcfile  D:\path\targetfile
                if (DEBUG) then
                    ListAddString(listInfo, "Copy " + szLcfLine, AFTER);
                    WriteLogMsg(szFn + " Copy " + szLcfLine);
                endif;
                CheckSrcEqTgtFileSize( listInfo,
                                       szSrcPath ^ szSrcFile ,
                                       szTargetPath ^ szTargetFile);
            else
               // Check for 'data' starting at 0 and NOT 'data.something'
               if ( StrFind( szLcfLine, "data" ) = 0 &&
                    szTargetFile = ASK_DOT_ASK ) then
                   // CompressGet Format : data.z:filespec TARGETDIR\*.*
                   //           Diskset install from compressed split lib
                   if (DEBUG) then
                       ListAddString(listInfo, "CmpGet " + szLcfLine, AFTER);
                       WriteLogMsg(szFn + " CmpGet " + szLcfLine);
                    endif;
                   if ( bListCmpValid && !CDROM_INSTALL ) then
                       CheckCmpTgtDirectories(listInfo, listCmp, szTargetPath);
                   endif;
               else
                   if (szSrcFile = ASK_DOT_ASK &&
                       szTargetFile = ASK_DOT_ASK ) then
                       // XCopyFile Format : D:\path\*.* D:\path\targetfile\*.*
                       if (DEBUG) then
                           ListAddString(listInfo, "XCopy " + szLcfLine, AFTER);
                           WriteLogMsg(szFn + " XCopy " + szLcfLine);
                       endif;
                       CheckSrcTgtDirectories(listInfo,
                                              szSrcPath, szTargetPath);
                   else
                       if (DEBUG) then
                           SprintfBox( SEVERE, szFn,
                           "ASSERT: Unknown format [%s]", szLcfLine);
                       endif;
                   endif;
               endif;
            endif;
            lCheck = ListGetNextString(listCopiedFiles, szLcfLine);
        endwhile;
        if ( ListCount(listInfo) > 0 ) then
            // Display the dialog
            LoadString128(SETUP_FILE_XFER_DLG_TITLE, szTitle);
            LoadString128(SETUP_FILE_XFER_MSG, szMsg);
            if (SdShowInfoList (szTitle, szMsg, listInfo) = CANCEL) then
                ListDestroy( listInfo );
                ListDestroy( listCmp );
                ExitHandler();
            endif;
        endif;
        ListDestroy( listInfo );
        ListDestroy( listCmp );
end;
/*-----------------------------------------------------------------------
 *
 * Function:  ParseDatazCmpToList( listCmp )
 *
 *
 *  Purpose:  Read the \SUPPORTDIR\dataz.cmp file.
 *            Throw away header - footing records and
 *            filename only entries without a directory.
 *            These are install programs, dlls etc.
 *
 *            Parse file records:
 *            05-08-96 12:11      244 ____      186 N2000\N2000.DEF
 *
 *            Into listCmp entries:
 *            244 N2000\N2000.DEF
 *
 *    Input:  listCmp
 *
 *   Output:  listCmp  Reformatted. Uppercase strings enforced.
 *
 *   Return:  BOOL     success/failure.
 *
 * Comments:
 *
\*-----------------------------------------------------------------------*/
function ParseDatazCmpToList( listCmp )
STRING szLine, szFn, szTmp, szSrcSize, szExpand;
LONG lCheck;
LIST listWrk, listParse;
begin
#if CDROM_INSTALL
// Keep the working code set as small as poosible
        return FALSE;
#else
        szFn = "ParseDatazCmpToList()";
        listWrk = ListCreate(STRINGLIST);
        if (listWrk = LIST_NULL ) then
            LoadString128(SETUP_FAILED_TO_CREATE, szExpand);
            Sprintf(szTmp, szExpand, "listWrk");
            if (DEBUG) then
                SprintfBox(SEVERE, szFn, szTmp);
            endif;
            WriteLogMsg( szFn + " " + szTmp);
            return FALSE;
        endif;
        if ( ListReadFromFile(listWrk, SUPPORTDIR ^ DATAZ_DOT_CMP) != 0) then
            LoadString128(SETUP_LIST_NOT_READ, szExpand);
            Sprintf(szTmp, szExpand, "listWrk");
            if ( DEBUG ) then
                SprintfBox(SEVERE, szFn, szTmp);
            endif;
            WriteLogMsg(szFn + " " + szTmp);
            return FALSE;
        endif;
        lCheck = ListGetFirstString(listWrk, szLine);
        while (lCheck = 0)
            // Process only file data lines with
            // N2000\x, CB\x, PDIS08\x filenames
            if ( StrFind( szLine, ":" )  > 0 &&
                 StrFind( szLine, "\\" ) > 0 )  then
                // Parse file record
                listParse = ListCreate(STRINGLIST);
                if (listParse = LIST_NULL ) then
                    LoadString128(SETUP_FAILED_TO_CREATE, szExpand);
                    Sprintf(szTmp, szExpand, "listParse");
                    if (DEBUG) then
                        SprintfBox(SEVERE, szFn, szTmp);
                    endif;
                    WriteLogMsg(szFn + " " + szTmp);
                    return FALSE;
                endif;
                StrGetTokens(listParse, szLine, " ");
                ListSetIndex(listParse, 2);
                ListCurrentString(listParse, szSrcSize);  //Uncompressed
                ListSetIndex(listParse, 5);
                ListCurrentString(listParse, szLine);
                szTmp = szSrcSize + " " + szLine;
                StrToUpper( szLine, szTmp);
                ListAddString(listCmp, szLine, AFTER);
                ListDestroy( listParse );
            endif;
            lCheck = ListGetNextString(listWrk, szLine);
        endwhile;
        if ( ListCount(listCmp) <= 0 ) then
            LoadString128(SETUP_LIST_EMPTY, szExpand);
            Sprintf(szTmp, szExpand, "listCmp");
            if ( DEBUG ) then
                 SprintfBox(SEVERE, szFn, szTmp);
            endif;
            WriteLogMsg(szFn + " " + szTmp);
            return FALSE;
        endif;
        ListAddString(listCmp, "END_OF_LIST", AFTER);
        return TRUE;
#endif
end;
/*-----------------------------------------------------------------------
 *
 * Function:  CheckCmpTgtDirectories( listInfo, listCmp , szTargetPath)
 *
 *
 *  Purpose:  Supports diskette install, where files were transferred
 *            from split compressed library files to the target disk.
 *
 *            Given a target directory and a list of the source files
 *            and their original sizes before compression, check that
 *            the size of the target files match the original size.
 *
 *            Add error info to the listInfo string list if not.
 *
 *    Input:  listInfo
 *            listCmp
 *            szTargetPath
 *
 *   Output:  listInfo
 *
 * Comments:  If we can't do the test return if we're not debugging.
 *
\*-----------------------------------------------------------------------*/
function CheckCmpTgtDirectories( listInfo, listCmp, szTargetPath )
STRING szFn, szFqTarget, szFileName, szDir, szTmp, szExpand;
LONG lCheck, lSearch, lSrcSize, lTargetSize, lResult;
LIST listWrk;
begin
#if CDROM_INSTALL
// Keep the working code set as small as possible
#else
        szFn = "CheckCmpTgtDirectories()";
        VarSave( SRCTARGETDIR );       // Save
        // Loop thru all the files in the target directory
        lCheck = FindAllFiles(szTargetPath, ASK_DOT_ASK, szFqTarget, RESET);
        while (lCheck = 0)
            ParsePath(szFileName, szFqTarget, FILENAME);
            // In the ListCmp list we derived from the compressed library
            // the file names are (currently):
            //
            // CB\filename
            // PDIS08\filename
            // N2000\filename
            // However, the App directory name selected by the user may not
            // be N2000. If it's not CB or PDIS08, assume it's a file in
            // the N2000 directory.
            // The preceding and trailing backslashes ensure that we don't,
            // match on the 'CB' of 'N2000\CB5.DLL' and erroneously assign
            // the 'CB' directory.
            if ( StrFind( szFqTarget, "\\" + CBDIR + "\\" ) > 0 ) then
                szDir = CBDIR;
            else
                if ( StrFind( szFqTarget, "\\" + PDIS08DIR + "\\" ) > 0 ) then
                    szDir = PDIS08DIR;
                else
                    szDir = N2000;
                endif;
            endif;
            // Assemble directory-filename search key
            // enforcing u/c to ensure valid comparison
            StrToUpper( szTmp, szDir + "\\" + szFileName);
            szFileName = szTmp;
            // Search the original compressed file list from the top
            lSearch = ListGetFirstString( listCmp, szTmp);
            while (lSearch = 0)
                if ( StrFind( szTmp, szFileName )  > 0 ) then
                    lSearch = ENUM_MATCHED;  // Hit.
                else
                    lSearch = ListGetNextString( listCmp, szTmp);
                endif;
            endwhile;
            if (lSearch != ENUM_MATCHED) then
                // Because the main loop is driven off the Target directory,
                // with DEBUG on you will see a bunch of not-founds
                // This is to be expected
                // These are files in the N2000 target directory which
                // are NOT in the library, eg ARC16.DLL, ARC32.DLL,
                // .DBF's, .CDX's, .LOG's.
                // Find one that should be and isn't and win a coconut.
                if ( DEBUG ) then
                    Sprintf(szTmp,"Lib listCmp search for [%s] Returned[%i]",
                                      szFileName, lSearch);
                    ListAddString(listInfo, szTmp, AFTER);
                    WriteLogMsg(szFn + " " + szTmp);
                endif;
            else
                // Target & original file name natch.
                // Extract the original source file size
                ListCurrentString (listCmp, szTmp);
                listWrk = ListCreate(STRINGLIST);
                StrGetTokens(listWrk, szTmp, " ");
                ListGetFirstString(listWrk, szTmp);
                ListDestroy( listWrk );
                StrToNum( lSrcSize, szTmp);
                // Now get the target file size we ended up with
                lResult = GetFileInfo( szFqTarget, FILE_SIZE,
                                                       lTargetSize, szTmp );
                if (lResult != 0 ) then
                    if ( DEBUG) then
                        Sprintf(szTmp,"Lib GetFileInfo ret[%i] Tgt=[%s]",
                                                       lResult, szFqTarget);
                        ListAddString(listInfo, szTmp, AFTER);
                        ListAddString(listInfo, " ", AFTER);
                        WriteLogMsg(szFn + " " + szTmp);
                    else
                       return;
                    endif;
                endif;
                LoadString128(SETUP_CHECKING_FILE, szExpand);
                Sprintf(szTmp, szExpand, szFqTarget);
                SetStatusWindow( -1, szTmp);
                if ( lSrcSize != lTargetSize ) then
                    // Discreditable Q&D 3.0 workaround for erroneous
                    // match between d:\n2000\readme.txt (9072 bytes)
                    // and d:\n2000\mapdata\southa\readme.txt (35) bytes
                    // ie a file with the same name in two different directories
                    if ( lSrcSize = 9072 && lTargetSize = 35) then
                        WriteLogMsg("Skipped readme.txt check");
                    else
                        LoadString128(SETUP_SOURCE_FILE_NAME_SIZE, szExpand);
                        Sprintf(szTmp, szExpand, szFileName, lSrcSize);
                        ListAddString(listInfo, szTmp, AFTER);
                        WriteLogMsg(szTmp);
                        LoadString128(SETUP_TARGET_FILE_NAME_SIZE, szExpand);
                        Sprintf(szTmp, szExpand, szFqTarget, lTargetSize);
                        ListAddString(listInfo, szTmp, AFTER);
                        ListAddString(listInfo, " ", AFTER);
                        WriteLogMsg(szTmp);
                    endif;
                else
                    if ( DEBUG) then
                        Sprintf(szTmp,"Xfer Ok: %s %ld bytes",
                                                      szFileName, lSrcSize);
                        ListAddString(listInfo, szTmp, AFTER);
                        WriteLogMsg(szTmp);
                    endif;
                endif;
            endif;
            lCheck = FindAllFiles(szTargetPath, ASK_DOT_ASK, szFqTarget,
                                                                  CONTINUE);
        endwhile;
        VarRestore( SRCTARGETDIR );     // Restore
#endif
end;
/*-----------------------------------------------------------------------
 *
 * Function:  CheckSrcTgtDirectories( listInfo, szSrcPath , szTargetPath)
 *
 *
 *  Purpose:  Given two directory path names check that for each file
 *            in the first directory, the second directory contains a
 *            matching file with the same size.
 *
 *            Add error info to the listInfo string list if not.
 *
 *    Input:  listInfo
 *            szSrcPath
 *            szTargetPath
 *
 *   Output:  listInfo
 *
 * Comments:  If we can't do the test return if we're not debugging.
 *
\*-----------------------------------------------------------------------*/
function CheckSrcTgtDirectories( listInfo, szSrcPath, szTargetPath )
STRING szFqSrc, szFileName;
LONG lCheck;
begin
        lCheck = FindAllFiles(szSrcPath, ASK_DOT_ASK, szFqSrc, RESET);
        while (lCheck = 0)
            ParsePath(szFileName, szFqSrc, FILENAME);
            CheckSrcEqTgtFileSize( listInfo,
                                    szFqSrc,
                                    szTargetPath ^ szFileName);
            lCheck = FindAllFiles(szSrcPath, ASK_DOT_ASK, szFqSrc, CONTINUE);
        endwhile;
end;
/*-----------------------------------------------------------------------
 *
 * Function:  CheckSrcEqTgtFileSize( listInfo, szFqSrc , szFqTarget)
 *
 *
 *  Purpose:  Given fully qualified path names to two files check that
 *            the file sizes are identical. Add error info to the
 *            listInfo string list if not.
 *
 *    Input:  listInfo
 *            szFqSrc
 *            szFqTarget
 *
 * Comments:  If we can't do the test, return if we're not debugging.
 *
\*-----------------------------------------------------------------------*/
function CheckSrcEqTgtFileSize( listInfo, szFqSrc, szFqTarget )
LONG lResult, lSrcSize, lTargetSize;
STRING szFn, szTmp, szMsg, szExpand;
begin
        szFn = "CheckSrcEqTgtFileSize()";
        lResult = GetFileInfo( szFqSrc, FILE_SIZE, lSrcSize, szTmp );
        if (lResult != 0 ) then
            if ( DEBUG) then
                Sprintf(szTmp,"Dir GetFileInfo ret[%i] Src=../images2/[%s]",
                                               lResult, szFqSrc);
                ListAddString(listInfo, szTmp, AFTER);
                WriteLogMsg(szFn + " " + szTmp);
            else
                return;
            endif;
        endif;
        lResult = GetFileInfo( szFqTarget, FILE_SIZE, lTargetSize, szTmp );
        if (lResult != 0 ) then
            if ( DEBUG) then
                Sprintf(szTmp,"Dir GetFileInfo ret[%i] Tgt=[%s]",
                                               lResult, szFqTarget);
                ListAddString(listInfo, szTmp, AFTER);
                ListAddString(listInfo, " ", AFTER);
                WriteLogMsg(szFn + " " + szTmp);
            else
                return;
            endif;
        endif;
        LoadString128(SETUP_CHECKING_FILE, szExpand);
        Sprintf(szTmp, szExpand, szFqTarget);
        SetStatusWindow( -1, szTmp);
       if ( lSrcSize != lTargetSize ) then
            WriteLogMsg ( " ");
            WriteLogMsg ( CSTR_DASH_LINE + CSTR_DASH_LINE);
            LoadString128(SETUP_ERROR, szMsg);
            Sprintf(szTmp,"%s %s", szFn, szMsg);
            WriteLogMsg(szTmp);
            LoadString128(SETUP_SOURCE_FILE_NAME_SIZE, szExpand);
            Sprintf(szTmp, szExpand, szFqSrc, lSrcSize);
            ListAddString(listInfo, szTmp, AFTER);
            WriteLogMsg(szTmp);
            LoadString128(SETUP_TARGET_FILE_NAME_SIZE, szExpand);
            Sprintf(szTmp, szExpand, szFqTarget, lTargetSize);
            ListAddString(listInfo, szTmp, AFTER);
            ListAddString(listInfo, " ", AFTER);
            WriteLogMsg(szTmp);
       endif;
end;
/*-----------------------------------------------------------------------
 *
 * Function:  WriteLogMsg( szMsg)
 *
 *
 *  Purpose:  Log Message to setup.log
 *
 *    Input:  bLogFile
 *            hLogFile
 *            szMsg
 *
 * Comments:
 *
\*-----------------------------------------------------------------------*/
function WriteLogMsg( szMsg )
LONG lResult, lSrcSize, lTargetSize;
STRING szTmp;
begin
        if (bLogFile) then
            WriteLine(hLogFile, szMsg);
        endif;
end;
/*-----------------------------------------------------------------------
 *
 * Function:  WriteFileToLog( szPath, szFileName)
 *
 *
 *  Purpose:  Given the two components of a fully qualified file name,
 *            open it and  write it's contents to the setup log
 *
 *    Input:  szPath
 *            szFileName
 *
 * Comments:  Warren has infested the N2000.ini file with
 *            [CallinPrint]
 *            modeStr=Anincrediblylongstring538byteslong!
 *            This caused an error 401 when Getline read
 *            this record using STRING szLine plain, so
 *            szLine is now defined [MAX_MODE_STR].
 *
\*-----------------------------------------------------------------------*/
function WriteFileToLog( szPath, szFileName )
STRING szLine[MAX_MODE_STR], szExpand;
LONG lFileHandle, lFlag;
begin
        OpenFileMode ( FILE_MODE_NORMAL);
        if ( OpenFile(lFileHandle, szPath, szFileName) = 0 ) then
            WriteLogMsg( " ");
            WriteLogMsg( CSTR_DASH_LINE + CSTR_DASH_LINE);
            WriteLogMsg( " ");
            LoadString128(SETUP_FILE, szLine);
            WriteLogMsg( szLine + szPath ^ szFileName );
            WriteLogMsg( " ");
            while (lFlag = 0 )
                WriteLogMsg( szLine );
                lFlag = GetLine(lFileHandle, szLine);
            endwhile;
            CloseFile(lFileHandle);
        else
            LoadString128(SETUP_UNABLE_TO_OPEN_FILE, szExpand);
            Sprintf(szLine, szExpand, szPath ^ szFileName);
            WriteLogMsg( szLine );
        endif;
end;
/*-----------------------------------------------------------------------
 *
 * Function:  LoadString128( lResourceId, szId)
 *
 *  Purpose:  Load a string from _ISUSER.dll, for the current language.
 *
 *    Input:  LONG lResourceId
 *
 *    Output: STRING szId  (passed by reference)
 *
 * Comments:  The only reason we have this function, and it's brothers
 *            is that the IS International West compiler 3.00.096 02-18-96
 *            will not compile a rul file with > 127 calls to a function!
 *            See gotcha #39.
 *
\*-----------------------------------------------------------------------*/
function LoadString128( lResourceId, szId )
begin
        LoadString(hIsUserHandle, lResourceId + LANG, szId, MAX_SIZE);
end;
// Instruct the compiler to include only those parts of
// sddialog.rul indicated -- only the portions required
// for the Sd dialogs used in the script
#define SD_SINGLE_DIALOGS               1    // Limit to specified modules
//#define SD_REGISTERUSEREX               1
//#define SD_REGISTERUSER                 1
//#define SD_CONFIRMREGISTRATION          1
//#define SD_CONFIRMNEWDIR                1
//#define SD_ASKDESTPATH                  1
#define SD_WELCOME                      1
#define SD_SHOWINFOLIST                 1
//#define SD_SELECTFOLDER                 1
#define SD_SETUPTYPE                    1
//#define SD_SHOWANYDIALOG                1
//#define SD_DISPLAYTOPICS                1
#define SD_SHOWMSG                      1
//#define SD_ASKOPTIONSLIST               1
//#define SD_SHOWFILEMODS                 1
//#define SD_SHOWDLGEDIT1                 1
//#define SD_SHOWDLGEDIT2                 1
#define SD_SHOWDLGEDIT3                 1
#define SD_ASKOPTIONS                   1
#define SD_COMPONENTDIALOG              1
//#define SD_COMPONENTDIALOG2             1
//#define SD_COMPONENTMULT                1
#define SD_OPTIONSBUTTONS               1
//#define SD_BITMAP                       1
//#define SD_COMPONENTDIALOGADV           1
#define SD_PRODUCTNAME                  1
//#define SD_LICENSE                      1
#define SD_STARTCOPY                    1
#define SD_FINISHREBOOT                 1
#define SD_FINISH                       1
// Note the SF_FINISHREBOOT pulls in a modified SDFINBOT.RUL file
// Include Glenayre Custom dialogs in /ishield/dialogs/include
// [End Setup.rul]


Back to top | ZDS Home | This article updated September 15 2004.