Fix PATH always being modified by installer

#define Version '48'
#define VersionExtra ''

#ifndef InstallerBits
  #define InstallerBits '64'
#endif
#define InstallerBitsInt = Int(InstallerBits)

#if InstallerBitsInt == 64
  #define AppName 'VapourSynth (64-bits)'
  #define AppId 'VapourSynth'
  #define RegistryPath 'SOFTWARE\VapourSynth'
  #define SourceBinaryPath '..\msvc_project\x64\Release'
  #define WheelFilename(Version) 'VapourSynth-' + Version + '-cp37-cp37m-win_amd64.whl'
#else
  #define AppName 'VapourSynth (32-bits)'
  #define AppId 'VapourSynth-32'
  #define RegistryPath 'SOFTWARE\VapourSynth-32'
  #define SourceBinaryPath '..\msvc_project\Release'
  #define WheelFilename(Version) 'VapourSynth-' + Version + '-cp37-cp37m-win32.whl'
#endif

[Setup]
OutputDir=Compiled
OutputBaseFilename=VapourSynth{#= InstallerBits}-R{#= Version}{#= VersionExtra}
Compression=lzma2/max
SolidCompression=yes
VersionInfoDescription={#= AppName} R{#= Version}{#= VersionExtra} Installer
AppId={#= AppId}
AppName={#= AppName} R{#= Version}{#= VersionExtra}
AppVersion=R{#= Version}{#= VersionExtra}
AppVerName={#= AppName} R{#= Version}{#= VersionExtra}
AppPublisher=Fredrik Mellbin
AppPublisherURL=http://www.vapoursynth.com/
AppSupportURL=http://www.vapoursynth.com/
AppUpdatesURL=http://www.vapoursynth.com/
VersionInfoVersion=1.{#= Version}.0.0
DefaultDirName={autopf}\{#= AppId}
DefaultGroupName={#= AppName}
AllowCancelDuringInstall=no
AllowNoIcons=yes
AllowUNCPath=no
MinVersion=6.0
UsePreviousPrivileges=yes
PrivilegesRequired=admin
PrivilegesRequiredOverridesAllowed=dialog
WizardStyle=modern
FlatComponentsList=yes
ChangesEnvironment=yes
#if InstallerBitsInt == 64
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#endif

[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"

[Types]
Name: Full; Description: Full installation; Flags: iscustom

[Components]
Name: "vscore"; Description: "VapourSynth {#= InstallerBits}-bit R{#= Version}{#= VersionExtra}"; Types: Full; Check: HasPython; Flags: fixed disablenouninstallwarning
Name: "vsrepo"; Description: "VSRepo Package Manager"; Types: Full; Flags: disablenouninstallwarning
Name: "docs"; Description: "VapourSynth Documentation"; Types: Full; Flags: disablenouninstallwarning
Name: "sdk"; Description: "VapourSynth SDK"; Flags: disablenouninstallwarning; Types: Full
Name: "pismo"; Description: "Pismo PFM Runtime (required for AVFS)"; Types: Full; Flags: disablenouninstallwarning
Name: "vsruntimes"; Description: "Visual Studio 2019 Runtime"; Types: Full; Check: IsAdminInstallMode; Flags: disablenouninstallwarning

[Tasks]
Name: newvpyfile; Description: "Add 'New VapourSynth Python Script' option to shell context menu"; GroupDescription: "VapourSynth:"; Components: vscore
Name: vscorepath; Description: "Add VSPipe and AVFS to PATH"; GroupDescription: "VapourSynth:"; Components: vscore
Name: vsrepoupdate; Description: "Update VSRepo package list"; GroupDescription: "VSRepo:"; Components: vsrepo
Name: vsrepopath; Description: "Add VSRepo to PATH"; GroupDescription: "VSRepo:"; Components: vsrepo

[Run]
Filename: {code:GetPythonExecutable}; Parameters: "-m pip install ""{app}\python\{#= WheelFilename(Version)}"""; Flags: runhidden; Components: vscore
Filename: "{app}\pismo\pfm-192-vapoursynth-win.exe"; Parameters: "install"; Check: IsAdminInstallMode; Flags: runhidden; Components: pismo
Filename: {code:GetPythonExecutable}; Parameters: """{app}\vsrepo\vsrepo.py"" update"; Flags: runhidden runasoriginaluser; Components: vsrepo

[UninstallRun]
Filename: {code:GetPythonExecutable}; Parameters: "-m pip uninstall -y VapourSynth"; Flags: runhidden; Components: vscore

[Files]
;core binaries
Source: template.vpy; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: ..\dist\{#= WheelFilename(Version)}; DestDir: {app}\python; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore

Source: {#= SourceBinaryPath}\vapoursynth.dll; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vapoursynth.pdb; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\avfs.exe; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vspipe.exe; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vsvfw.dll; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vsscript.dll; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore

;vsrepo
Source: ..\vsrepo\vsrepo.py; DestDir: {app}\vsrepo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vsrepo
Source: 7z.exe; DestDir: {app}\vsrepo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vsrepo
Source: 7z.dll; DestDir: {app}\vsrepo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vsrepo

;docs
Source: ..\doc\_build\html\*; DestDir: {app}\docs; Flags: ignoreversion uninsrestartdelete restartreplace recursesubdirs; Components: docs

;sdk
Source: ..\include\VapourSynth.h; DestDir: {app}\sdk\include\vapoursynth; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\include\VSHelper.h; DestDir: {app}\sdk\include\vapoursynth; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\include\VSScript.h; DestDir: {app}\sdk\include\vapoursynth; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\Release\vsscript.lib; DestDir: {app}\sdk\lib32; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\x64\Release\vsscript.lib; DestDir: {app}\sdk\lib64; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\Release\vapoursynth.lib; DestDir: {app}\sdk\lib32; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\x64\Release\vapoursynth.lib; DestDir: {app}\sdk\lib64; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\sdk\filter_skeleton.c; DestDir: {app}\sdk\examples; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\sdk\invert_example.c; DestDir: {app}\sdk\examples; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\sdk\vsscript_example.c; DestDir: {app}\sdk\examples; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk

;bundled plugins
Source: Plugins{#= InstallerBits}\*; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\AvsCompat.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\EEDI3.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\MiscFilters.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\Morpho.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\RemoveGrainVS.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components:vscore
Source: {#= SourceBinaryPath}\Vinverse.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\VIVTC.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore

;pismo installer
Source: "pfm-192-vapoursynth-win.exe"; DestDir: {app}\pismo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: pismo

; Create the general autoload directory
[Dirs]
Name: "{app}\plugins"; Flags: uninsalwaysuninstall; Components: vscore

[Icons]
Name: {group}\VapourSynth Website; Filename: http://www.vapoursynth.com/; Components: vscore
Name: {group}\Documentation (Local); Filename: {app}\docs\index.html; Components: docs
Name: {group}\Documentation (Online); Filename: http://www.vapoursynth.com/doc/
Name: {group}\Global Autoload Directory; Filename: {app}\plugins; Check: IsAdminInstallMode; Components: vscore
Name: {group}\User Autoload Directory; Filename: %APPDATA%\VapourSynth\plugins{#= InstallerBits}; Components: vscore
Name: {group}\VapourSynth SDK; Filename: {app}\sdk; Components: sdk

[Registry]
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "Version"; ValueData: {#= Version}; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "Path"; ValueData: "{app}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "CorePlugins"; ValueData: "{app}\core\plugins"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "Plugins"; ValueData: "{app}\plugins"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VapourSynthDLL"; ValueData: "{app}\core\vapoursynth.dll"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VSScriptDLL"; ValueData: "{app}\core\vsscript.dll"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VSPipeEXE"; ValueData: "{app}\core\vspipe.exe"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VSRepoPY"; ValueData: "{app}\vsrepo\vsrepo.py"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vsrepo
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "PythonPath"; ValueData: "{code:GetPythonPath}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore

; Write compatibility values, deprecated since R46 when the installers were split
#if InstallerBitsInt == 32
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "Version"; ValueData: {#= Version}; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "Path"; ValueData: "{app}"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "CorePlugins"; ValueData: "{app}\core\plugins"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "Plugins"; ValueData: "{app}\plugins"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "VapourSynthDLL"; ValueData: "{app}\core\vapoursynth.dll"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "VSScriptDLL"; ValueData: "{app}\core\vsscript.dll"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "PythonPath"; ValueData: "{code:GetPythonPath}"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
#endif

; new vpy file shortcut task
Root: HKA; Subkey: SOFTWARE\Classes\.vpy\ShellNew; ValueType: string; ValueName: "FileName"; ValueData: "{app}\template.vpy"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: newvpyfile

; vfw
Root: HKA; Subkey: SOFTWARE\Classes\CLSID\{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}; ValueType: string; ValueName: ""; ValueData: "VapourSynth"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\CLSID\{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}\InProcServer32; ValueType: string; ValueName: ""; ValueData: "{app}\core\vsvfw.dll"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\CLSID\{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}\InProcServer32; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\Media Type\Extensions\.vpy; ValueType: string; ValueName: ""; ValueData: ""; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\Media Type\Extensions\.vpy; ValueType: string; ValueName: "Source Filter"; ValueData: "{{D3588AB0-0781-11CE-B03A-0020AF0BA770}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\.vpy; ValueType: string; ValueName: ""; ValueData: "vpyfile"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\vpyfile; ValueType: string; ValueName: ""; ValueData: "VapourSynth Python Script"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\vpyfile\DefaultIcon; ValueType: string; ValueName: ""; ValueData: "{app}\core\vsvfw.dll,0"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\AVIFile\Extensions\VPY; ValueType: string; ValueName: ""; ValueData: "{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore

; PATH
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\core"; Check: IsAdminInstallMode; Tasks: vscorepath
Root: HKCR; Subkey: "Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\core"; Check: not IsAdminInstallMode; Tasks: vscorepath
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\vsrepo"; Check: IsAdminInstallMode; Tasks: vsrepopath
Root: HKCR; Subkey: "Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\vsrepo"; Check: IsAdminInstallMode; Tasks: vsrepopath

#include "scripts\products.iss"
#include "scripts\products\stringversion.iss"
#include "scripts\products\msiproduct.iss"
#include "scripts\products\vcredist2017.iss"

[Code]

const VSRuntimeVersion = '14.23.27820';

type
  TPythonPath = record
    DisplayName: string;
    InstallPath: string;
    ExecutablePath: string;
    Bitness: Integer;
  end;

var
  RuntimesAdded: Boolean;
  PythonInstallations: array of TPythonPath;
  GlobalPythonInstallations: array of TPythonPath;
  UserPythonInstallations: array of TPythonPath;
  PythonPage: TWizardPage;
  PythonList: TNewCheckListBox;
  PythonPath: string;
  PythonExecutable: string;

function HasPython: Boolean;
var
  Counter: Integer;
begin
  Result := False;
  for Counter := 0 to GetArrayLength(PythonInstallations) - 1 do
    if PythonInstallations[Counter].Bitness = {#InstallerBitsInt} then
      Result := True;
end;

procedure GetPythonInstallations2(var DestArray: array of TPythonPath; RegRoot: Integer; RegPath: string; AssumeBitness: Integer);
var
  Names, Tags: TArrayOfString;
  Nc, Tc: Integer;
  RegPathTemp: string;
  Temp: string;
  DisplayName, InstallPath, ExecutablePath: string;
  Bitness: Integer;
begin
  if RegGetSubkeyNames(RegRoot, RegPath, Names) then
  begin
    for Nc := 0 to GetArrayLength(Names) - 1 do
    begin
      if RegGetSubkeyNames(RegRoot, RegPath + '\' + Names[Nc], Tags) then
      begin
        for Tc := 0 to GetArrayLength(Tags) - 1 do
        begin
          RegPathTemp := RegPath + '\' + Names[Nc] + '\' + Tags[Tc];
          Bitness := AssumeBitness;
          if (not RegQueryStringValue(RegRoot, RegPathTemp, 'SysVersion', Temp)) or (Temp <> '3.7') then
            continue;
          if RegQueryStringValue(RegRoot, RegPathTemp, 'SysArchitecture', Temp) then
          begin
            if Temp = '32bit' then
              Bitness := 32
            else if Temp = '64bit' then
              Bitness := 64;
          end;

          if Bitness = {#InstallerBitsInt} then
          begin
            if RegQueryStringValue(RegRoot, RegPathTemp, 'DisplayName', DisplayName)
              and RegQueryStringValue(RegRoot, RegPathTemp + '\InstallPath', '', InstallPath)
              and RegQueryStringValue(RegRoot, RegPathTemp + '\InstallPath', 'ExecutablePath', ExecutablePath)
              and (DisplayName <> '') and (InstallPath <> '') and (ExecutablePath <> '') then
            begin
               SetArrayLength(DestArray, GetArrayLength(DestArray) + 1);
               DestArray[GetArrayLength(DestArray) - 1].DisplayName := DisplayName;
               DestArray[GetArrayLength(DestArray) - 1].InstallPath := InstallPath;
               DestArray[GetArrayLength(DestArray) - 1].ExecutablePath := ExecutablePath;
               DestArray[GetArrayLength(DestArray) - 1].Bitness := Bitness;
            end;
          end;
        end;
      end;
    end;
  end;
end;

procedure GetPythonInstallations;
begin
  GetPythonInstallations2(UserPythonInstallations, HKCU, 'SOFTWARE\Python', 0);
  GetPythonInstallations2(GlobalPythonInstallations, HKLM, 'SOFTWARE\Python', {#InstallerBitsInt});
end;

procedure PopulatePythonInstallations(List: TNewCheckListBox);
var
  Counter: Integer;
  First: Boolean;
begin
  List.Items.Clear;
  First := True;
  for Counter := 0 to GetArrayLength(PythonInstallations) - 1 do
    if PythonInstallations[Counter].Bitness = {#InstallerBitsInt} then
      with PythonInstallations[Counter] do
      begin
        List.AddRadioButton(DisplayName, '(' + InstallPath + ')', 0, First, True, TObject(Counter));
        First := False;
      end;
end;

/////////////////////////////////////////////////////////////////////

procedure RemovePath(Path: string);
var
  Paths: string;
  P: Integer;
  RegPath: string;
begin
  if IsAdminInstallMode then
    RegPath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
  else
    RegPath := 'Environment';

  if not RegQueryStringValue(HKA, RegPath, 'Path', Paths) then
  begin
    Log('PATH not found');
  end
    else
  begin
    Log(Format('PATH is [%s]', [Paths]));

    P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
    if P = 0 then
    begin
      Log(Format('Path [%s] not found in PATH', [Path]));
    end
      else
    begin
      if P > 1 then P := P - 1;
      Delete(Paths, P, Length(Path) + 1);
      Log(Format('Path [%s] removed from PATH => [%s]', [Path, Paths]));

      if RegWriteStringValue(HKA, RegPath, 'Path', Paths) then
      begin
        Log('PATH written');
      end
        else
      begin
        Log('Error writing PATH');
      end;
    end;
  end;
end;

/////////////////////////////////////////////////////////////////////

function InitializeSetup: Boolean;
var
  HasOtherPython: Boolean;
  ErrCode: Integer;
begin
  RuntimesAdded := False;
  PythonList := nil;
  GetPythonInstallations;
  if IsAdminInstallMode then
  begin
    PythonInstallations := GlobalPythonInstallations;
    HasOtherPython := GetArrayLength(UserPythonInstallations) > 0;
  end
  else
  begin
    PythonInstallations := UserPythonInstallations;
    HasOtherPython := GetArrayLength(GlobalPythonInstallations) > 0;
  end;

  Result := GetArrayLength(PythonInstallations) > 0;

  if not Result and not HasOtherPython then
      MsgBox('No suitable Python 3.7 ({#InstallerBits}-bit) installation found. The installer will now exit.', mbCriticalError, MB_OK)
  else if not Result and IsAdminInstallMode then
      MsgBox('Python 3.7 ({#InstallerBits}-bit) is installed for the current user only. Run the installer again and select "Install for me only" or install Python for all users.', mbCriticalError, MB_OK)
  else if not Result and not IsAdminInstallMode then
      MsgBox('Python 3.7 ({#InstallerBits}-bit) is installed for all users. Run the installer again and select "Install for all users" or install Python for the current user only.', mbCriticalError, MB_OK);

  if not IsAdminInstallMode and not vcredist2017installed(VSRuntimeVersion) then
      if MsgBox('No recent Visual Studio 2019 Runtime installed.If you proceed with the install it is very likely the installation won''t work.'#13#10#13#10'Go to the download website now?', mbError, MB_YESNO) = IDYES then
          ShellExec('open', 'https://visualstudio.microsoft.com/downloads/?q=redistributable', '', '', SW_SHOW, ewNoWait, ErrCode);
end;

procedure WizardFormOnResize(Sender: TObject);
begin
  with PythonList do
  begin
    Parent := PythonPage.Surface;
    Left := ScaleX(0);
    Top := ScaleY(0);
    Width := PythonPage.Surface.Width - ScaleX(0) * 2;
    Height := PythonPage.Surface.Height;
  end;
end;

procedure InitializeWizard();
begin
  PythonPage := CreateCustomPage(wpSelectComponents, 'Select Python Installation', 'Select which Python installation to use');
  PythonList := TNewCheckListBox.Create(PythonPage);

  with PythonList do
  begin
    Parent := PythonPage.Surface;
    Left := ScaleX(0);
    Top := ScaleY(0);
    Width := PythonPage.Surface.Width - ScaleX(0) * 2;
    Height := PythonPage.Surface.Height;
  end;

  WizardForm.OnResize := @WizardFormOnResize;
end;

function GetPythonPath(Param: string): String;
begin
  Result := PythonPath;
end;

function GetPythonExecutable(Param: string): String;
begin
    Result := PythonExecutable;
end;

/////////////////////////////////////////////////////////////////////
function GetUninstallString: String;
var
  sUnInstPath: String;
  sUnInstallString: String;
begin
  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  sUnInstallString := '';
  RegQueryStringValue(HKA, sUnInstPath, 'UninstallString', sUnInstallString)
  Result := sUnInstallString;
end;

/////////////////////////////////////////////////////////////////////
function IsUpgrade: Boolean;
begin
  Result := (GetUninstallString() <> '');
end;

/////////////////////////////////////////////////////////////////////
function UnInstallOldVersion: Integer;
var
  sUnInstallString: String;
  iResultCode: Integer;
begin
// Return Values:
// 1 - uninstall string is empty
// 2 - error executing the UnInstallString
// 3 - successfully executed the UnInstallString

  // default return value
  Result := 0;

  // get the uninstall string of the old app
  sUnInstallString := GetUninstallString();
  if sUnInstallString <> '' then begin
    sUnInstallString := RemoveQuotes(sUnInstallString);
    if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
      Result := 3
    else
      Result := 2;
  end
  else
    Result := 1;
end;

/////////////////////////////////////////////////////////////////////
procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep=ssInstall then
  begin
    if IsUpgrade() then
      UnInstallOldVersion();
  end;
end;

/////////////////////////////////////////////////////////////////////
function NextButtonClick(CurPageID: Integer): Boolean;
var
  Counter: Integer;
  Idx: Integer;
begin
  Result := True;
  if CurPageID = PythonPage.ID then
  begin
    for Counter := 0 to PythonList.Items.Count - 1 do
    begin
      if PythonList.Checked[Counter] then
      begin
        Idx := Integer(PythonList.ItemObject[Counter]);
        with PythonInstallations[Idx] do
        begin
          PythonPath := InstallPath;
          PythonExecutable := ExecutablePath;
        end;
      end;
    end;
  end
  else if CurPageID = wpSelectComponents then
  begin
    PopulatePythonInstallations(PythonList);
  end
  else if CurPageID = wpReady then
  begin
    if WizardIsComponentSelected('vsruntimes') and not RuntimesAdded then
    begin
      vcredist2017(VSRuntimeVersion);
      RuntimesAdded := True;
    end;
    Result := NextButtonClick2(CurPageID);
  end;
end;

/////////////////////////////////////////////////////////////////////

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
  begin
    RemovePath(ExpandConstant('{app}\core'));
    RemovePath(ExpandConstant('{app}\vsrepo'));
  end;
end;

#define Version '48'
#define VersionExtra ''

#ifndef InstallerBits
  #define InstallerBits '64'
#endif
#define InstallerBitsInt = Int(InstallerBits)

#if InstallerBitsInt == 64
  #define AppName 'VapourSynth (64-bits)'
  #define AppId 'VapourSynth'
  #define RegistryPath 'SOFTWARE\VapourSynth'
  #define SourceBinaryPath '..\msvc_project\x64\Release'
  #define WheelFilename(Version) 'VapourSynth-' + Version + '-cp37-cp37m-win_amd64.whl'
#else
  #define AppName 'VapourSynth (32-bits)'
  #define AppId 'VapourSynth-32'
  #define RegistryPath 'SOFTWARE\VapourSynth-32'
  #define SourceBinaryPath '..\msvc_project\Release'
  #define WheelFilename(Version) 'VapourSynth-' + Version + '-cp37-cp37m-win32.whl'
#endif

[Setup]
OutputDir=Compiled
OutputBaseFilename=VapourSynth{#= InstallerBits}-R{#= Version}{#= VersionExtra}
Compression=lzma2/max
SolidCompression=yes
VersionInfoDescription={#= AppName} R{#= Version}{#= VersionExtra} Installer
AppId={#= AppId}
AppName={#= AppName} R{#= Version}{#= VersionExtra}
AppVersion=R{#= Version}{#= VersionExtra}
AppVerName={#= AppName} R{#= Version}{#= VersionExtra}
AppPublisher=Fredrik Mellbin
AppPublisherURL=http://www.vapoursynth.com/
AppSupportURL=http://www.vapoursynth.com/
AppUpdatesURL=http://www.vapoursynth.com/
VersionInfoVersion=1.{#= Version}.0.0
DefaultDirName={autopf}\{#= AppId}
DefaultGroupName={#= AppName}
AllowCancelDuringInstall=no
AllowNoIcons=yes
AllowUNCPath=no
MinVersion=6.0
UsePreviousPrivileges=yes
PrivilegesRequired=admin
PrivilegesRequiredOverridesAllowed=dialog
WizardStyle=modern
FlatComponentsList=yes
ChangesEnvironment=yes
#if InstallerBitsInt == 64
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
#endif

[Languages]
Name: "en"; MessagesFile: "compiler:Default.isl"

[Types]
Name: Full; Description: Full installation; Flags: iscustom

[Components]
Name: "vscore"; Description: "VapourSynth {#= InstallerBits}-bit R{#= Version}{#= VersionExtra}"; Types: Full; Check: HasPython; Flags: fixed disablenouninstallwarning
Name: "vsrepo"; Description: "VSRepo Package Manager"; Types: Full; Flags: disablenouninstallwarning
Name: "docs"; Description: "VapourSynth Documentation"; Types: Full; Flags: disablenouninstallwarning
Name: "sdk"; Description: "VapourSynth SDK"; Flags: disablenouninstallwarning; Types: Full
Name: "pismo"; Description: "Pismo PFM Runtime (required for AVFS)"; Types: Full; Flags: disablenouninstallwarning
Name: "vsruntimes"; Description: "Visual Studio 2019 Runtime"; Types: Full; Check: IsAdminInstallMode; Flags: disablenouninstallwarning

[Tasks]
Name: newvpyfile; Description: "Add 'New VapourSynth Python Script' option to shell context menu"; GroupDescription: "VapourSynth:"; Components: vscore
Name: vscorepath; Description: "Add VSPipe and AVFS to PATH"; GroupDescription: "VapourSynth:"; Components: vscore
Name: vsrepoupdate; Description: "Update VSRepo package list"; GroupDescription: "VSRepo:"; Components: vsrepo
Name: vsrepopath; Description: "Add VSRepo to PATH"; GroupDescription: "VSRepo:"; Components: vsrepo

[Run]
Filename: {code:GetPythonExecutable}; Parameters: "-m pip install ""{app}\python\{#= WheelFilename(Version)}"""; Flags: runhidden; Components: vscore
Filename: "{app}\pismo\pfm-192-vapoursynth-win.exe"; Parameters: "install"; Check: IsAdminInstallMode; Flags: runhidden; Components: pismo
Filename: {code:GetPythonExecutable}; Parameters: """{app}\vsrepo\vsrepo.py"" update"; Flags: runhidden runasoriginaluser; Components: vsrepo

[UninstallRun]
Filename: {code:GetPythonExecutable}; Parameters: "-m pip uninstall -y VapourSynth"; Flags: runhidden; Components: vscore

[Files]
;core binaries
Source: template.vpy; DestDir: {app}; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: ..\dist\{#= WheelFilename(Version)}; DestDir: {app}\python; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore

Source: {#= SourceBinaryPath}\vapoursynth.dll; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vapoursynth.pdb; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\avfs.exe; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vspipe.exe; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vsvfw.dll; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\vsscript.dll; DestDir: {app}\core; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore

;vsrepo
Source: ..\vsrepo\vsrepo.py; DestDir: {app}\vsrepo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vsrepo
Source: 7z.exe; DestDir: {app}\vsrepo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vsrepo
Source: 7z.dll; DestDir: {app}\vsrepo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vsrepo

;docs
Source: ..\doc\_build\html\*; DestDir: {app}\docs; Flags: ignoreversion uninsrestartdelete restartreplace recursesubdirs; Components: docs

;sdk
Source: ..\include\VapourSynth.h; DestDir: {app}\sdk\include\vapoursynth; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\include\VSHelper.h; DestDir: {app}\sdk\include\vapoursynth; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\include\VSScript.h; DestDir: {app}\sdk\include\vapoursynth; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\Release\vsscript.lib; DestDir: {app}\sdk\lib32; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\x64\Release\vsscript.lib; DestDir: {app}\sdk\lib64; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\Release\vapoursynth.lib; DestDir: {app}\sdk\lib32; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\msvc_project\x64\Release\vapoursynth.lib; DestDir: {app}\sdk\lib64; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\sdk\filter_skeleton.c; DestDir: {app}\sdk\examples; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\sdk\invert_example.c; DestDir: {app}\sdk\examples; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk
Source: ..\sdk\vsscript_example.c; DestDir: {app}\sdk\examples; Flags: ignoreversion uninsrestartdelete restartreplace; Components: sdk

;bundled plugins
Source: Plugins{#= InstallerBits}\*; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\AvsCompat.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\EEDI3.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\MiscFilters.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\Morpho.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\RemoveGrainVS.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components:vscore
Source: {#= SourceBinaryPath}\Vinverse.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore
Source: {#= SourceBinaryPath}\VIVTC.dll; DestDir: {app}\core\plugins; Flags: ignoreversion uninsrestartdelete restartreplace; Components: vscore

;pismo installer
Source: "pfm-192-vapoursynth-win.exe"; DestDir: {app}\pismo; Flags: ignoreversion uninsrestartdelete restartreplace; Components: pismo

; Create the general autoload directory
[Dirs]
Name: "{app}\plugins"; Flags: uninsalwaysuninstall; Components: vscore

[Icons]
Name: {group}\VapourSynth Website; Filename: http://www.vapoursynth.com/; Components: vscore
Name: {group}\Documentation (Local); Filename: {app}\docs\index.html; Components: docs
Name: {group}\Documentation (Online); Filename: http://www.vapoursynth.com/doc/
Name: {group}\Global Autoload Directory; Filename: {app}\plugins; Check: IsAdminInstallMode; Components: vscore
Name: {group}\User Autoload Directory; Filename: %APPDATA%\VapourSynth\plugins{#= InstallerBits}; Components: vscore
Name: {group}\VapourSynth SDK; Filename: {app}\sdk; Components: sdk

[Registry]
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "Version"; ValueData: {#= Version}; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "Path"; ValueData: "{app}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "CorePlugins"; ValueData: "{app}\core\plugins"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "Plugins"; ValueData: "{app}\plugins"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VapourSynthDLL"; ValueData: "{app}\core\vapoursynth.dll"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VSScriptDLL"; ValueData: "{app}\core\vsscript.dll"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VSPipeEXE"; ValueData: "{app}\core\vspipe.exe"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "VSRepoPY"; ValueData: "{app}\vsrepo\vsrepo.py"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vsrepo
Root: HKA; Subkey: {#= RegistryPath}; ValueType: string; ValueName: "PythonPath"; ValueData: "{code:GetPythonPath}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore

; Write compatibility values, deprecated since R46 when the installers were split
#if InstallerBitsInt == 32
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "Version"; ValueData: {#= Version}; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "Path"; ValueData: "{app}"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "CorePlugins"; ValueData: "{app}\core\plugins"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "Plugins"; ValueData: "{app}\plugins"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "VapourSynthDLL"; ValueData: "{app}\core\vapoursynth.dll"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "VSScriptDLL"; ValueData: "{app}\core\vsscript.dll"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKLM; Subkey: SOFTWARE\VapourSynth; ValueType: string; ValueName: "PythonPath"; ValueData: "{code:GetPythonPath}"; Check: IsAdminInstallMode; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
#endif

; new vpy file shortcut task
Root: HKA; Subkey: SOFTWARE\Classes\.vpy\ShellNew; ValueType: string; ValueName: "FileName"; ValueData: "{app}\template.vpy"; Flags: uninsdeletevalue uninsdeletekeyifempty; Tasks: newvpyfile

; vfw
Root: HKA; Subkey: SOFTWARE\Classes\CLSID\{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}; ValueType: string; ValueName: ""; ValueData: "VapourSynth"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\CLSID\{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}\InProcServer32; ValueType: string; ValueName: ""; ValueData: "{app}\core\vsvfw.dll"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\CLSID\{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}\InProcServer32; ValueType: string; ValueName: "ThreadingModel"; ValueData: "Apartment"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\Media Type\Extensions\.vpy; ValueType: string; ValueName: ""; ValueData: ""; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\Media Type\Extensions\.vpy; ValueType: string; ValueName: "Source Filter"; ValueData: "{{D3588AB0-0781-11CE-B03A-0020AF0BA770}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\.vpy; ValueType: string; ValueName: ""; ValueData: "vpyfile"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\vpyfile; ValueType: string; ValueName: ""; ValueData: "VapourSynth Python Script"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\vpyfile\DefaultIcon; ValueType: string; ValueName: ""; ValueData: "{app}\core\vsvfw.dll,0"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore
Root: HKA; Subkey: SOFTWARE\Classes\AVIFile\Extensions\VPY; ValueType: string; ValueName: ""; ValueData: "{{58F74CA0-BD0E-4664-A49B-8D10E6F0C131}"; Flags: uninsdeletevalue uninsdeletekeyifempty; Components: vscore

; PATH
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\core"; Check: IsAdminInstallMode; Components: vscore
Root: HKCR; Subkey: "Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\core"; Check: not IsAdminInstallMode; Components: vscore
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\vsrepo"; Check: IsAdminInstallMode; Components: vsrepo
Root: HKCR; Subkey: "Environment"; ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}\vsrepo"; Check: IsAdminInstallMode; Components: vsrepo

#include "scripts\products.iss"
#include "scripts\products\stringversion.iss"
#include "scripts\products\msiproduct.iss"
#include "scripts\products\vcredist2017.iss"

[Code]

const VSRuntimeVersion = '14.23.27820';

type
  TPythonPath = record
    DisplayName: string;
    InstallPath: string;
    ExecutablePath: string;
    Bitness: Integer;
  end;

var
  RuntimesAdded: Boolean;
  PythonInstallations: array of TPythonPath;
  GlobalPythonInstallations: array of TPythonPath;
  UserPythonInstallations: array of TPythonPath;
  PythonPage: TWizardPage;
  PythonList: TNewCheckListBox;
  PythonPath: string;
  PythonExecutable: string;

function HasPython: Boolean;
var
  Counter: Integer;
begin
  Result := False;
  for Counter := 0 to GetArrayLength(PythonInstallations) - 1 do
    if PythonInstallations[Counter].Bitness = {#InstallerBitsInt} then
      Result := True;
end;

procedure GetPythonInstallations2(var DestArray: array of TPythonPath; RegRoot: Integer; RegPath: string; AssumeBitness: Integer);
var
  Names, Tags: TArrayOfString;
  Nc, Tc: Integer;
  RegPathTemp: string;
  Temp: string;
  DisplayName, InstallPath, ExecutablePath: string;
  Bitness: Integer;
begin
  if RegGetSubkeyNames(RegRoot, RegPath, Names) then
  begin
    for Nc := 0 to GetArrayLength(Names) - 1 do
    begin
      if RegGetSubkeyNames(RegRoot, RegPath + '\' + Names[Nc], Tags) then
      begin
        for Tc := 0 to GetArrayLength(Tags) - 1 do
        begin
          RegPathTemp := RegPath + '\' + Names[Nc] + '\' + Tags[Tc];
          Bitness := AssumeBitness;
          if (not RegQueryStringValue(RegRoot, RegPathTemp, 'SysVersion', Temp)) or (Temp <> '3.7') then
            continue;
          if RegQueryStringValue(RegRoot, RegPathTemp, 'SysArchitecture', Temp) then
          begin
            if Temp = '32bit' then
              Bitness := 32
            else if Temp = '64bit' then
              Bitness := 64;
          end;

          if Bitness = {#InstallerBitsInt} then
          begin
            if RegQueryStringValue(RegRoot, RegPathTemp, 'DisplayName', DisplayName)
              and RegQueryStringValue(RegRoot, RegPathTemp + '\InstallPath', '', InstallPath)
              and RegQueryStringValue(RegRoot, RegPathTemp + '\InstallPath', 'ExecutablePath', ExecutablePath)
              and (DisplayName <> '') and (InstallPath <> '') and (ExecutablePath <> '') then
            begin
               SetArrayLength(DestArray, GetArrayLength(DestArray) + 1);
               DestArray[GetArrayLength(DestArray) - 1].DisplayName := DisplayName;
               DestArray[GetArrayLength(DestArray) - 1].InstallPath := InstallPath;
               DestArray[GetArrayLength(DestArray) - 1].ExecutablePath := ExecutablePath;
               DestArray[GetArrayLength(DestArray) - 1].Bitness := Bitness;
            end;
          end;
        end;
      end;
    end;
  end;
end;

procedure GetPythonInstallations;
begin
  GetPythonInstallations2(UserPythonInstallations, HKCU, 'SOFTWARE\Python', 0);
  GetPythonInstallations2(GlobalPythonInstallations, HKLM, 'SOFTWARE\Python', {#InstallerBitsInt});
end;

procedure PopulatePythonInstallations(List: TNewCheckListBox);
var
  Counter: Integer;
  First: Boolean;
begin
  List.Items.Clear;
  First := True;
  for Counter := 0 to GetArrayLength(PythonInstallations) - 1 do
    if PythonInstallations[Counter].Bitness = {#InstallerBitsInt} then
      with PythonInstallations[Counter] do
      begin
        List.AddRadioButton(DisplayName, '(' + InstallPath + ')', 0, First, True, TObject(Counter));
        First := False;
      end;
end;

/////////////////////////////////////////////////////////////////////

procedure RemovePath(Path: string);
var
  Paths: string;
  P: Integer;
  RegPath: string;
begin
  if IsAdminInstallMode then
    RegPath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
  else
    RegPath := 'Environment';

  if not RegQueryStringValue(HKA, RegPath, 'Path', Paths) then
  begin
    Log('PATH not found');
  end
    else
  begin
    Log(Format('PATH is [%s]', [Paths]));

    P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
    if P = 0 then
    begin
      Log(Format('Path [%s] not found in PATH', [Path]));
    end
      else
    begin
      if P > 1 then P := P - 1;
      Delete(Paths, P, Length(Path) + 1);
      Log(Format('Path [%s] removed from PATH => [%s]', [Path, Paths]));

      if RegWriteStringValue(HKA, RegPath, 'Path', Paths) then
      begin
        Log('PATH written');
      end
        else
      begin
        Log('Error writing PATH');
      end;
    end;
  end;
end;

/////////////////////////////////////////////////////////////////////

function InitializeSetup: Boolean;
var
  HasOtherPython: Boolean;
  ErrCode: Integer;
begin
  RuntimesAdded := False;
  PythonList := nil;
  GetPythonInstallations;
  if IsAdminInstallMode then
  begin
    PythonInstallations := GlobalPythonInstallations;
    HasOtherPython := GetArrayLength(UserPythonInstallations) > 0;
  end
  else
  begin
    PythonInstallations := UserPythonInstallations;
    HasOtherPython := GetArrayLength(GlobalPythonInstallations) > 0;
  end;

  Result := GetArrayLength(PythonInstallations) > 0;

  if not Result and not HasOtherPython then
      MsgBox('No suitable Python 3.7 ({#InstallerBits}-bit) installation found. The installer will now exit.', mbCriticalError, MB_OK)
  else if not Result and IsAdminInstallMode then
      MsgBox('Python 3.7 ({#InstallerBits}-bit) is installed for the current user only. Run the installer again and select "Install for me only" or install Python for all users.', mbCriticalError, MB_OK)
  else if not Result and not IsAdminInstallMode then
      MsgBox('Python 3.7 ({#InstallerBits}-bit) is installed for all users. Run the installer again and select "Install for all users" or install Python for the current user only.', mbCriticalError, MB_OK);

  if not IsAdminInstallMode and not vcredist2017installed(VSRuntimeVersion) then
      if MsgBox('No recent Visual Studio 2019 Runtime installed.If you proceed with the install it is very likely the installation won''t work.'#13#10#13#10'Go to the download website now?', mbError, MB_YESNO) = IDYES then
          ShellExec('open', 'https://visualstudio.microsoft.com/downloads/?q=redistributable', '', '', SW_SHOW, ewNoWait, ErrCode);
end;

procedure WizardFormOnResize(Sender: TObject);
begin
  with PythonList do
  begin
    Parent := PythonPage.Surface;
    Left := ScaleX(0);
    Top := ScaleY(0);
    Width := PythonPage.Surface.Width - ScaleX(0) * 2;
    Height := PythonPage.Surface.Height;
  end;
end;

procedure InitializeWizard();
begin
  PythonPage := CreateCustomPage(wpSelectComponents, 'Select Python Installation', 'Select which Python installation to use');
  PythonList := TNewCheckListBox.Create(PythonPage);

  with PythonList do
  begin
    Parent := PythonPage.Surface;
    Left := ScaleX(0);
    Top := ScaleY(0);
    Width := PythonPage.Surface.Width - ScaleX(0) * 2;
    Height := PythonPage.Surface.Height;
  end;

  WizardForm.OnResize := @WizardFormOnResize;
end;

function GetPythonPath(Param: string): String;
begin
  Result := PythonPath;
end;

function GetPythonExecutable(Param: string): String;
begin
    Result := PythonExecutable;
end;

/////////////////////////////////////////////////////////////////////
function GetUninstallString: String;
var
  sUnInstPath: String;
  sUnInstallString: String;
begin
  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  sUnInstallString := '';
  RegQueryStringValue(HKA, sUnInstPath, 'UninstallString', sUnInstallString)
  Result := sUnInstallString;
end;

/////////////////////////////////////////////////////////////////////
function IsUpgrade: Boolean;
begin
  Result := (GetUninstallString() <> '');
end;

/////////////////////////////////////////////////////////////////////
function UnInstallOldVersion: Integer;
var
  sUnInstallString: String;
  iResultCode: Integer;
begin
// Return Values:
// 1 - uninstall string is empty
// 2 - error executing the UnInstallString
// 3 - successfully executed the UnInstallString

  // default return value
  Result := 0;

  // get the uninstall string of the old app
  sUnInstallString := GetUninstallString();
  if sUnInstallString <> '' then begin
    sUnInstallString := RemoveQuotes(sUnInstallString);
    if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
      Result := 3
    else
      Result := 2;
  end
  else
    Result := 1;
end;

/////////////////////////////////////////////////////////////////////
procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep=ssInstall then
  begin
    if IsUpgrade() then
      UnInstallOldVersion();
  end;
end;

/////////////////////////////////////////////////////////////////////
function NextButtonClick(CurPageID: Integer): Boolean;
var
  Counter: Integer;
  Idx: Integer;
begin
  Result := True;
  if CurPageID = PythonPage.ID then
  begin
    for Counter := 0 to PythonList.Items.Count - 1 do
    begin
      if PythonList.Checked[Counter] then
      begin
        Idx := Integer(PythonList.ItemObject[Counter]);
        with PythonInstallations[Idx] do
        begin
          PythonPath := InstallPath;
          PythonExecutable := ExecutablePath;
        end;
      end;
    end;
  end
  else if CurPageID = wpSelectComponents then
  begin
    PopulatePythonInstallations(PythonList);
  end
  else if CurPageID = wpReady then
  begin
    if WizardIsComponentSelected('vsruntimes') and not RuntimesAdded then
    begin
      vcredist2017(VSRuntimeVersion);
      RuntimesAdded := True;
    end;
    Result := NextButtonClick2(CurPageID);
  end;
end;

/////////////////////////////////////////////////////////////////////

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
  begin
    RemovePath(ExpandConstant('{app}\core'));
    RemovePath(ExpandConstant('{app}\vsrepo'));
  end;
end;


Added missing thirdparty files in installer script.

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "Hopsan"
#define MyAppPublisher "Linkping University"
#define MyAppURL "http://www.iei.liu.se/flumes/hopsanng"
#define MyAppExeName "HopsanGUI.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{02E63B1C-D08C-4377-952C-A5E22EE620DC}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
OutputBaseFilename=Hopsan-{#MyAppVersion}-win32-installer
SetupIconFile=HopsanGUI\hopsan.ico
Compression=lzma
SolidCompression=yes
WizardImageFile="installation_large.bmp"
WizardSmallImageFile="installation_small.bmp"
WizardImageStretch=yes
ChangesAssociations=yes
UninstallDisplayName={#MyAppName}
UninstallDisplayIcon=HopsanGUI\hopsan.ico
DirExistsWarning=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[messages]
DirExists=The folder:%n%n%1%n%nalready exists. All previous contents will be removed! Would you like to install to that folder anyway?

[CustomMessages]
ProjectHomepage=Project &Homepage
UserGuide=User &Guide

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatehmf"; Description: "Associate .hmf files with Hopsan"; Flags: dontinheritcheck

[InstallDelete]
Type: filesandordirs; Name: "{app}"

[Dirs]
;Create installation directories
Name: "{app}\bin"
Name: "{app}\componentLibraries"
Name: "{app}\componentLibraries\defaultLibrary"
Name: "{app}\doc"
Name: "{app}\HopsanCore"
Name: "{app}\models"
Name: "{app}\ThirdParty"
Name: "{app}\ThirdParty\7z"
Name: "{userdocs}\Hopsan"
Name: "{userdocs}\Hopsan\Models"
Name: "{userdocs}\Hopsan\Scripts"

[Files]
;Hopsan files
Source: "C:\temp_release\bin\*";                                DestDir: "{app}\bin";                               Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\componentLibraries\defaultLibrary\*";  DestDir: "{app}\componentLibraries\defaultLibrary"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\HopsanCore\*";                         DestDir: "{app}\HopsanCore";                        Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\hopsandefaults";                       DestDir: "{app}";                                   Flags: ignoreversion
Source: "C:\temp_release\Hopsan-release-notes.txt";             DestDir: "{app}";                                   Flags: ignoreversion
Source: "C:\temp_release\HopsanCoreSourceCode.zip";             DestDir: "{app}";                                   Flags: ignoreversion skipifsourcedoesntexist
Source: "C:\temp_release\componentLibrariesSourceCode.zip";     DestDir: "{app}";                                   Flags: ignoreversion skipifsourcedoesntexist
Source: "C:\temp_release\doc\*";                  	            DestDir: "{app}\doc";                               Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\models\*";               	            DestDir: "{app}\Models";                            Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\ThirdParty\7z\*";                      DestDir: "{app}\ThirdParty\7z";                     Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\scripts\*";              	            DestDir: "{userdocs}\Hopsan\Scripts";               Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"
Name: "{group}\{cm:UserGuide,{#MyAppName}}"; Filename: "{app}\doc\user\html\index.html"
Name: "{group}\{cm:ProjectHomepage,{#MyAppName}}"; Filename: "{#MyAppURL}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"; Tasks: quicklaunchicon

[Run]
Filename: "{app}\bin\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, "&", "&&")}}"; Flags: nowait postinstall
Filename: "{app}\Hopsan-release-notes.txt"; Description: "View release notes"; Flags: nowait postinstall shellexec

[Registry]
Root: HKCR; Subkey: ".hmf"; ValueType: string; ValueName: ""; ValueData: "HopsanModelFile"; Flags: uninsdeletevalue; Tasks: associatehmf
Root: HKCR; Subkey: "HopsanModelFile"; ValueType: string; ValueName: ""; ValueData: "Hopsan Model File"; Flags: uninsdeletekey; Tasks: associatehmf
Root: HKCR; Subkey: "HopsanModelFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\bin\HopsanGUI.exe,1"; Tasks: associatehmf
Root: HKCR; Subkey: "HopsanModelFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\bin\HopsanGUI.exe"" ""%1"""; Tasks: associatehmf

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "Hopsan"
#define MyAppPublisher "Linkping University"
#define MyAppURL "http://www.iei.liu.se/flumes/hopsanng"
#define MyAppExeName "HopsanGUI.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{02E63B1C-D08C-4377-952C-A5E22EE620DC}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
OutputBaseFilename=Hopsan-{#MyAppVersion}-win32-installer
SetupIconFile=HopsanGUI\hopsan.ico
Compression=lzma
SolidCompression=yes
WizardImageFile="installation_large.bmp"
WizardSmallImageFile="installation_small.bmp"
WizardImageStretch=yes
ChangesAssociations=yes
UninstallDisplayName={#MyAppName}
UninstallDisplayIcon=HopsanGUI\hopsan.ico
DirExistsWarning=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[messages]
DirExists=The folder:%n%n%1%n%nalready exists. All previous contents will be removed! Would you like to install to that folder anyway?

[CustomMessages]
ProjectHomepage=Project &Homepage
UserGuide=User &Guide

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 0,6.1
Name: "associatehmf"; Description: "Associate .hmf files with Hopsan"; Flags: dontinheritcheck

[InstallDelete]
Type: filesandordirs; Name: "{app}"

[Dirs]
;Create installation directories
Name: "{app}\bin"
Name: "{app}\componentLibraries"
Name: "{app}\componentLibraries\defaultLibrary"
Name: "{app}\doc"
Name: "{app}\HopsanCore"
Name: "{app}\models"
Name: "{userdocs}\Hopsan"
Name: "{userdocs}\Hopsan\Models"
Name: "{userdocs}\Hopsan\Scripts"

[Files]
;Hopsan files
Source: "C:\temp_release\bin\*";                                DestDir: "{app}\bin";                               Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\componentLibraries\defaultLibrary\*";  DestDir: "{app}\componentLibraries\defaultLibrary"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\HopsanCore\*";                         DestDir: "{app}\HopsanCore";                        Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\hopsandefaults";                       DestDir: "{app}";                                   Flags: ignoreversion
Source: "C:\temp_release\Hopsan-release-notes.txt";             DestDir: "{app}";                                   Flags: ignoreversion
Source: "C:\temp_release\HopsanCoreSourceCode.zip";             DestDir: "{app}";                                   Flags: ignoreversion skipifsourcedoesntexist
Source: "C:\temp_release\componentLibrariesSourceCode.zip";     DestDir: "{app}";                                   Flags: ignoreversion skipifsourcedoesntexist
Source: "C:\temp_release\doc\*";                  	            DestDir: "{app}\doc";                               Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\models\*";               	            DestDir: "{app}\Models";                            Flags: ignoreversion recursesubdirs createallsubdirs
Source: "C:\temp_release\scripts\*";              	            DestDir: "{userdocs}\Hopsan\Scripts";               Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"
Name: "{group}\{cm:UserGuide,{#MyAppName}}"; Filename: "{app}\doc\user\html\index.html"
Name: "{group}\{cm:ProjectHomepage,{#MyAppName}}"; Filename: "{#MyAppURL}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"; Tasks: quicklaunchicon

[Run]
Filename: "{app}\bin\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, "&", "&&")}}"; Flags: nowait postinstall
Filename: "{app}\Hopsan-release-notes.txt"; Description: "View release notes"; Flags: nowait postinstall shellexec

[Registry]
Root: HKCR; Subkey: ".hmf"; ValueType: string; ValueName: ""; ValueData: "HopsanModelFile"; Flags: uninsdeletevalue; Tasks: associatehmf
Root: HKCR; Subkey: "HopsanModelFile"; ValueType: string; ValueName: ""; ValueData: "Hopsan Model File"; Flags: uninsdeletekey; Tasks: associatehmf
Root: HKCR; Subkey: "HopsanModelFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\bin\HopsanGUI.exe,1"; Tasks: associatehmf
Root: HKCR; Subkey: "HopsanModelFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\bin\HopsanGUI.exe"" ""%1"""; Tasks: associatehmf


win32 installer: remove old code, correct the ghc version in some of the error messages

darcs-hash:20080624191819-21862-8cef691b8a972900dbc1ed5af7a022fe553da035.gz

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
AppName=Gtk2Hs
AppId=Gtk2Hs
AppVerName=Gtk2Hs 0.9.13
AppVersion=0.9.13
AppPublisher=The Gtk2Hs Team
AppPublisherURL=http://haskell.org/gtk2hs/
AppSupportURL=http://haskell.org/gtk2hs/
AppUpdatesURL=http://haskell.org/gtk2hs/

DefaultDirName={pf}\Gtk2Hs
OutputBaseFilename=gtk2hs-0.9.13

VersionInfoVersion=0.9.13
VersionInfoCopyright=Copyright (C) 2001-2008 The Gtk2Hs Team

Compression=lzma/max
SolidCompression=yes

ChangesEnvironment=yes

PrivilegesRequired=none

[Components]
Name: "clibs";   Description: "Required C libraries"; Types: full compact custom; Flags: fixed
Name: "gtk2hs";  Description: "Gtk2Hs libraries for GHC 6.8.3"; Check: UseWithGhcVersion('6.8.3'); Types: full compact custom; Flags: fixed
Name: "docs";    Description: "API reference documentation"; Types: full
Name: "demos";   Description: "Source files for the Gtk2Hs demo programs"; Types: full

[Files]
Source: "gtk2hs-clibs-0.9.13\*";              DestDir: "{app}";       Components: clibs;   Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-0.9.13-ghc-6.8.3\*";          DestDir: "{app}";       Components: gtk2hs;  Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: AfterPkgInstall;
Source: "gtk2hs-demo-0.9.13\*";               DestDir: "{app}\demos"; Components: demos;   Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-docs-0.9.13\*";               DestDir: "{app}\docs";  Components: docs;    Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "COPYING.txt";                        DestDir: "{app}";                            Flags: ignoreversion;
Source: "AUTHORS.txt";                        DestDir: "{app}";                            Flags: ignoreversion;

[Registry]
Root: HKCU; Subkey: "Environment"; ValueName: "Path"; ValueType: "string"; ValueData: "{app}\bin;{olddata}"; Check: NotOnPathAlready(); Flags: preservestringtype;

[Run]
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glib.package.conf""";       StatusMsg: "Registering glib package...";       Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\cairo.package.conf""";      StatusMsg: "Registering cairo package...";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\svgcairo.package.conf""";   StatusMsg: "Registering svgcairo package...";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtk.package.conf""";        StatusMsg: "Registering gtk package...";        Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glade.package.conf""";      StatusMsg: "Registering glade package...";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\soegtk.package.conf""";     StatusMsg: "Registering soegtk package...";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtkglext.package.conf""";   StatusMsg: "Registering gtkglext package...";   Flags: runhidden
;Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\sourceview.package.conf"""; StatusMsg: "Registering sourceview package..."; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gnomevfs.package.conf""";   StatusMsg: "Registering gnomevfs package...";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gstreamer.package.conf""";  StatusMsg: "Registering gstreamer package...";  Flags: runhidden

[UninstallRun]
Filename: "{code:ghcpkg}"; Parameters: "unregister gstreamer-0.9.13";  RunOnceId: "gstreamer"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gnomevfs-0.9.13";   RunOnceId: "gnomevfs";  Flags: runhidden
;Filename: "{code:ghcpkg}"; Parameters: "unregister sourceview-0.9.13"; RunOnceId: "gtkglext";  Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtkglext-0.9.13";   RunOnceId: "gtkglext";  Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister soegtk-0.9.13";     RunOnceId: "soegtk";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glade-0.9.13";      RunOnceId: "glade";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtk-0.9.13";        RunOnceId: "gtk";       Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister svgcairo-0.9.13";   RunOnceId: "svgcairo";  Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister cairo-0.9.13";      RunOnceId: "cairo";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glib-0.9.13";       RunOnceId: "glib";      Flags: runhidden

[Code]
var
  GhcInstallDir: String;
  GhcInstallVersion: String;

  CheckingPage: TOutputProgressWizardPage;
  ErrorReportPage: TOutputMsgMemoWizardPage;
  ErrorContinueBox: TCheckBox;
  InstallationErrorCaption: String;
  InstallationErrorMessage: String;
  InstallationErrorDetail: String;

function ghcpkg(Param: String): String;
begin
  Result := AddBackslash(GhcInstallDir) + 'bin\ghc-pkg.exe';
end;

function NotOnPathAlready(): Boolean;
var
  BinDir, Path: String;
begin
  Log('Checking if Gtk2Hs\bin dir is already on the %PATH%');
  if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) then
  begin // Successfully read the value
    Log('HKCU\Environment\PATH = ' + Path);
    BinDir := ExpandConstant('{app}\bin');
    Log('Looking for Gtk2Hs\bin dir in %PATH%: ' + BinDir + ' in ' + Path);
    if Pos(LowerCase(BinDir), Lowercase(Path)) = 0 then
    begin
      Log('Did not find Gtk2Hs\bin dir in %PATH% so will add it');
      Result := True;
    end
    else
    begin
      Log('Found Gtk2Hs bin dir in %PATH% so will not add it again');
      Result := False;
    end
  end
  else // The key probably doesn't exist
  begin
    Log('Could not access HKCU\Environment\PATH so assume it is ok to add it');
    Result := True;
  end;
end;

{ exec a program and return its output }
function ExecOutput(const Filename, Params: String): String;
var
  TmpFile: String;
  ResultCode: Integer;
begin
  Result := '';
  TmpFile := GenerateUniqueName(ExpandConstant('{tmp}'), 'tmp');
  if Exec(ExpandConstant('{cmd}'), '/C ' + Filename + ' ' + Params + ' > ' + TmpFile,
     '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
    if ResultCode = 0 then
      if LoadStringFromFile(TmpFile, Result) then
      begin
        Result := Trim(Result);
        Log('ExecOutput: running ' + Filename + ' succeded with output: ' + Result);
      end
      else
        Log('ExecOutput: running ' + Filename + ' succeded, but the temp file was not created')
    else
      Log('ExecOutput: running ' + Filename + ' failed, code: ' + IntToStr(ResultCode))
  else
    { That's really odd, we should always be able to exec the command interpreter! }
    Log('ExecOutput: cannot exec command interpreter, code: ' + IntToStr(ResultCode) + ', message: ' + SysErrorMessage(ResultCode));
end;

function CheckGhcVersionIsOk(const Path: String; var Version: String):Boolean;
begin
  Version := ExecOutput(AddBackslash(Path) + 'bin\ghc.exe', '--numeric-version');
  GhcInstallVersion := Version;

  Result := (Version = '6.8.3');
end;

var
  DetectValidGhcInstallationCached: Boolean;
  DetectValidGhcInstallationResult: Boolean;

function DetectValidGhcInstallation(): Boolean;
var
  HaveSomeGHCInstalled: Boolean;
  GHCVersion: String;

begin
if DetectValidGhcInstallationCached then
  Result := DetectValidGhcInstallationResult
else
begin
  Result := False;

  { first check for ghc on the path, then look in the registry }

  begin
    GhcInstallDir := ExecOutput('ghc.exe', '--print-libdir');
    if GhcInstallDir <> '' then
    begin
      StringChange(GhcInstallDir, '/', '\');
      Log('DetectValidGhcInstallation: found ghc on %PATH% with libdir: ' + GhcInstallDir);
      HaveSomeGHCInstalled := True;
      Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
    end;
  end;

  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.8.3', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.8.3', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
  if Result then
    Log('DetectValidGhcInstallation: correct version of ghc found at: ' + GhcInstallDir)
  else if HaveSomeGHCInstalled and (GHCVersion <> '') then
  begin
    Log('DetectValidGhcInstallation: incorrect ghc version installed: ' + GHCVersion);
    InstallationErrorCaption := 'The version of GHC currently installed is not suitable.';
    InstallationErrorMessage := 'This version of Gtk2Hs requires GHC version 6.8.3.' #13#10 #13#10
                                'Setup found GHC version ' + GHCVersion + ' installed in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled and (GhcInstallDir <> '') then
  begin
    Log('DetectValidGhcInstallation: some non-working version of ghc appears to be installed at: ' + GhcInstallDir);
    InstallationErrorCaption := 'GHC does not seem to be working.';
    InstallationErrorMessage := 'GHC does not appear to be installed correctly, try reinstalling GHC version 6.8.3' #13#10 #13#10
                                'Setup found what appears to be a non-working installation of GHC in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled then
  begin
    Log('DetectValidGhcInstallation: corrupted ghc installation detected, probably messed up registry keys');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'GHC does not appear to be installed (or the installation is corrupted), please install GHC version 6.8.3';
  end
  else
  begin
    Log('DetectValidGhcInstallation: no installation of ghc detected');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'Gtk2Hs requires GHC to be installed first, please install GHC version 6.8.3' #13#10 #13#10
                                'If you installed GHC manually then make sure ghc.exe is on the path.';
  end;

  If not Result then
  begin
    Log(InstallationErrorCaption);
    Log(InstallationErrorMessage);
  end;

  DetectValidGhcInstallationResult := Result;
  DetectValidGhcInstallationCached := True;
end;
end;

function UseWithGhcVersion(const GhcVersion: String): Boolean;
begin
  DetectValidGhcInstallation();
  Result := (GhcVersion = GhcInstallVersion);
end;

var
  ChecksOk : Boolean;

function ShouldSkipPage(CurPageID: Integer): Boolean;
begin
  Log('ShouldSkipPage: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = ErrorReportPage.ID then
  begin
    if ChecksOk then
      Log('ChecksOk = True')
    else
      Log('ChecksOk = False');
    Result := ChecksOk
  end
  else
    Result := False;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  DllFiles, Offenders: array of String;
  Path, Offender: String;
  N, M, NumFiles: Integer;
begin
  Log('NextButtonClick: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = wpWelcome then
  begin
    if not DetectValidGhcInstallation() then
      ChecksOk := False
    else
    begin
      DllFiles := [
        'gspawn-win32-helper-console.exe',
        'gspawn-win32-helper.exe',
        'gst-inspect-0.10.exe',
        'gst-launch-0.10.exe',
        'charset.dll',
        'iconv.dll',
        'intl.dll',
        'jpeg62.dll',
        'libORBit-2-0.dll',
        'libORBit-imodule-2-0.dll',
        'libORBitCosNaming-2-0.dll',
        'libatk-1.0-0.dll',
        'libcairo-2.dll',
        'libgconf-2-4.dll',
        'libgdk-win32-2.0-0.dll',
        'libgdk_pixbuf-2.0-0.dll',
        'libgdkglext-win32-1.0-0.dll',
        'libgio-2.0-0.dll',
        'libglade-2.0-0.dll',
        'libglib-2.0-0.dll',
        'libgmodule-2.0-0.dll',
        'libgnomevfs-2-0.dll',
        'libgobject-2.0-0.dll',
        'libgstaudio-0.10.dll',
        'libgstbase-0.10.dll',
        'libgstcdda-0.10.dll',
        'libgstcontroller-0.10.dll',
        'libgstdataprotocol-0.10.dll',
        'libgstfft-0.10.dll',
        'libgstinterfaces-0.10.dll',
        'libgstnet-0.10.dll',
        'libgstnetbuffer-0.10.dll',
        'libgstpbutils-0.10.dll',
        'libgstreamer-0.10.dll',
        'libgstriff-0.10.dll',
        'libgstrtp-0.10.dll',
        'libgstrtsp-0.10.dll',
        'libgstsdp-0.10.dll',
        'libgsttag-0.10.dll',
        'libgstvideo-0.10.dll',
        'libgthread-2.0-0.dll',
        'libgtk-win32-2.0-0.dll',
        'libgtkglext-win32-1.0-0.dll',
        'libpango-1.0-0.dll',
        'libpangocairo-1.0-0.dll',
        'libpangoft2-1.0-0.dll',
        'libpangowin32-1.0-0.dll',
        'libpng12-0.dll',
        'librsvg-2-2.dll',
        'libtiff3.dll',
        'libxml2.dll',
        'zlib1.dll'
        ];
      NumFiles := GetArrayLength(DllFiles);
      SetArrayLength(Offenders, NumFiles);

      RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', Path);
      Path := GetSystemDir() + ';' + GetWinDir() + ';' + Path;

      Log('Checking DLL path: ' + Path);

      CheckingPage.SetText('Checking Gtk+ DLL search path...', '');
      CheckingPage.SetProgress(0, NumFiles);
      CheckingPage.Show;
      try
        M := 0;
        for N := 0 to NumFiles - 1 do
        begin
          Offender := FileSearch(DllFiles[N], Path);
          if Offender <> '' then
          begin
            Log('Found clashing dll/exe file at ' + Offender);
            Offenders[M] := Offender;
            M := M + 1;
          end
          CheckingPage.SetProgress(N+1, NumFiles);
          Sleep(10);
        end;
        if M > 0 then
        begin
          ChecksOk := False;
          InstallationErrorCaption := 'Setup found clashing DLL files on the search path.';
          InstallationErrorMessage := 'Please read the following description of the problem and how to fix it:';

          InstallationErrorDetail :=
              'For Haskell Gtk programs to work properly, the Gtk+ DLL files need to be in the DLL '
            + 'search path. However, the following DLLs files that Gtk+ uses are already on the '
            + 'system-wide search path:' #13#10

          for N := 0 to M - 1 do
            InstallationErrorDetail := InstallationErrorDetail + #13#10 + Offenders[N];

          InstallationErrorDetail := InstallationErrorDetail + #13#10 #13#10
              'The problem is that when you run a Haskell Gtk program these DLLs would get used '
            + 'rather than the proper versions of the Gtk+ DLLs installed by this setup program.'
            + 'This would almost certainly cause all your Haskell Gtk programs to not work.' #13#10 #13#10
              'You can fix this problem in a couple of ways:' #13#10 #13#10
              '1) You could uninstall or delete the program that installed the offending files '
            + '(eg it might be that you have an old version of Gtk+ installed).' #13#10 #13#10
              '2) You could modify the system-wide "Path" envrionment variable and remove the '
            + 'offending directory from it.'
        end
        else
          ChecksOk := True;
      finally
        CheckingPage.Hide;
      end;
    end;
  end
  Result := True;
end;

procedure PackageFileVarSubstitute(const PackageFile: String);
var
  PackageFileContent: String;
  PackageLibDir: String;
begin
  PackageLibDir := ExpandConstant('{app}');
  StringChange(PackageLibDir, '\', '/');

  LoadStringFromFile(PackageFile, PackageFileContent);

  StringChange(PackageFileContent, '${pkglibdir}', PackageLibDir);
  StringChange(PackageFileContent, '${GTK_BASEPATH}', PackageLibDir);

  SaveStringToFile(PackageFile, PackageFileContent, False);
  Log('Expanding variables in ' + PackageFile);
end;

procedure AfterPkgInstall;
begin
  if (ExtractFileExt(CurrentFileName) = '.conf') then
    PackageFileVarSubstitute(ExpandConstant(CurrentFileName));
end;

// Detect when the ready info page gets displayed
function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
                         MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo,
                         MemoTasksInfo: String): String;
begin
		Result := MemoDirInfo + NewLine
		        + NewLine
		        +	'Using GHC ' + GhcInstallVersion + ' installed at:' + NewLine
		        + Space + GhcInstallDir + NewLine
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  BinDir, Path: String;
begin
  if (CurUninstallStep = usPostUninstall)
     and (RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path)) then
  begin
    BinDir := ExpandConstant('{app}\bin');
    if Pos(LowerCase(BinDir) + ';', Lowercase(Path)) <> 0 then
    begin
      StringChange(Path, BinDir + ';', '');
      RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path);
    end;
  end;
end;

procedure ErrorReportPageNotify(Sender: TWizardPage);
begin
  WizardForm.NextButton.Enabled := ErrorContinueBox.Checked;
  ErrorReportPage.Description := InstallationErrorCaption;
  ErrorReportPage.SubCaptionLabel.Caption := InstallationErrorMessage;
  if InstallationErrorDetail = '' then
    ErrorReportPage.SubCaptionLabel.Height := 50
  else
  begin
    ErrorReportPage.RichEditViewer.RTFText := InstallationErrorDetail
    ErrorReportPage.RichEditViewer.Show();
    ErrorContinueBox.Show();
  end;
end;

procedure ErrorContinueClicked(Sender: TObject);
begin
  WizardForm.NextButton.Enabled := ErrorContinueBox.Checked;
end;

procedure InitializeWizard();
begin
  CheckingPage := CreateOutputProgressPage('Checking requirements', 'Checking for GHC and Gtk+');

  ErrorReportPage := CreateOutputMsgMemoPage(wpWelcome, 'Setup cannot continue', 'GHC or Gtk+ problem', 'Your installation of GHC or Gtk+ is messed up!', 'Foo bar!');
  ErrorReportPage.RichEditViewer.Height := ErrorReportPage.RichEditViewer.Height - ScaleY(22);
  ErrorReportPage.OnActivate := @ErrorReportPageNotify;
  ErrorReportPage.RichEditViewer.Hide();

  ErrorContinueBox := TCheckBox.Create(ErrorReportPage);
  ErrorContinueBox.Parent := ErrorReportPage.Surface;
  ErrorContinueBox.Top := ErrorReportPage.RichEditViewer.Height + ErrorReportPage.RichEditViewer.Top + ScaleY(8);
  ErrorContinueBox.Width  := ErrorReportPage.RichEditViewer.Width;
  ErrorContinueBox.Caption := 'Continue anyway. (You will have to fix the DLL search path problem yourself later.)';
  ErrorContinueBox.Checked := False;
  ErrorContinueBox.OnClick := @ErrorContinueClicked;
end;

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
AppName=Gtk2Hs
AppId=Gtk2Hs
AppVerName=Gtk2Hs 0.9.13
AppVersion=0.9.13
AppPublisher=The Gtk2Hs Team
AppPublisherURL=http://haskell.org/gtk2hs/
AppSupportURL=http://haskell.org/gtk2hs/
AppUpdatesURL=http://haskell.org/gtk2hs/

DefaultDirName={pf}\Gtk2Hs
OutputBaseFilename=gtk2hs-0.9.13

VersionInfoVersion=0.9.13
VersionInfoCopyright=Copyright (C) 2001-2008 The Gtk2Hs Team

Compression=lzma/max
SolidCompression=yes

ChangesEnvironment=yes

PrivilegesRequired=none

[Components]
Name: "clibs";   Description: "Required C libraries"; Types: full compact custom; Flags: fixed
Name: "gtk2hs";  Description: "Gtk2Hs libraries for GHC 6.8.3"; Check: UseWithGhcVersion('6.8.3'); Types: full compact custom; Flags: fixed
Name: "docs";    Description: "API reference documentation"; Types: full
Name: "demos";   Description: "Source files for the Gtk2Hs demo programs"; Types: full

[Files]
Source: "gtk2hs-clibs-0.9.13\*";              DestDir: "{app}";       Components: clibs;   Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-0.9.13-ghc-6.8.3\*";          DestDir: "{app}";       Components: gtk2hs;  Flags: ignoreversion recursesubdirs createallsubdirs; AfterInstall: AfterPkgInstall;
Source: "gtk2hs-demo-0.9.13\*";               DestDir: "{app}\demos"; Components: demos;   Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "gtk2hs-docs-0.9.13\*";               DestDir: "{app}\docs";  Components: docs;    Flags: ignoreversion recursesubdirs createallsubdirs;
Source: "COPYING.txt";                        DestDir: "{app}";                            Flags: ignoreversion;
Source: "AUTHORS.txt";                        DestDir: "{app}";                            Flags: ignoreversion;

[Registry]
Root: HKCU; Subkey: "Environment"; ValueName: "Path"; ValueType: "string"; ValueData: "{app}\bin;{olddata}"; Check: NotOnPathAlready(); Flags: preservestringtype;

[Run]
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glib.package.conf""";       StatusMsg: "Registering glib package...";       Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\cairo.package.conf""";      StatusMsg: "Registering cairo package...";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\svgcairo.package.conf""";   StatusMsg: "Registering svgcairo package...";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtk.package.conf""";        StatusMsg: "Registering gtk package...";        Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\glade.package.conf""";      StatusMsg: "Registering glade package...";      Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\soegtk.package.conf""";     StatusMsg: "Registering soegtk package...";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gtkglext.package.conf""";   StatusMsg: "Registering gtkglext package...";   Flags: runhidden
;Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\sourceview.package.conf"""; StatusMsg: "Registering sourceview package..."; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gnomevfs.package.conf""";   StatusMsg: "Registering gnomevfs package...";   Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "update ""{app}\gstreamer.package.conf""";  StatusMsg: "Registering gstreamer package...";  Flags: runhidden

[UninstallRun]
Filename: "{code:ghcpkg}"; Parameters: "unregister gstreamer-0.9.13";  RunOnceId: "gstreamer"; Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gnomevfs-0.9.13";   RunOnceId: "gnomevfs";  Flags: runhidden
;Filename: "{code:ghcpkg}"; Parameters: "unregister sourceview-0.9.13"; RunOnceId: "gtkglext";  Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtkglext-0.9.13";   RunOnceId: "gtkglext";  Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister soegtk-0.9.13";     RunOnceId: "soegtk";    Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glade-0.9.13";      RunOnceId: "glade";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister gtk-0.9.13";        RunOnceId: "gtk";       Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister svgcairo-0.9.13";   RunOnceId: "svgcairo";  Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister cairo-0.9.13";      RunOnceId: "cairo";     Flags: runhidden
Filename: "{code:ghcpkg}"; Parameters: "unregister glib-0.9.13";       RunOnceId: "glib";      Flags: runhidden

[Code]
var
  GhcInstallDir: String;
  GhcInstallVersion: String;

  CheckingPage: TOutputProgressWizardPage;
  ErrorReportPage: TOutputMsgMemoWizardPage;
  ErrorContinueBox: TCheckBox;
  InstallationErrorCaption: String;
  InstallationErrorMessage: String;
  InstallationErrorDetail: String;

function ghcpkg(Param: String): String;
begin
  Result := AddBackslash(GhcInstallDir) + 'bin\ghc-pkg.exe';
end;

function NotOnPathAlready(): Boolean;
var
  BinDir, Path: String;
begin
  Log('Checking if Gtk2Hs\bin dir is already on the %PATH%');
  if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) then
  begin // Successfully read the value
    Log('HKCU\Environment\PATH = ' + Path);
    BinDir := ExpandConstant('{app}\bin');
    Log('Looking for Gtk2Hs\bin dir in %PATH%: ' + BinDir + ' in ' + Path);
    if Pos(LowerCase(BinDir), Lowercase(Path)) = 0 then
    begin
      Log('Did not find Gtk2Hs\bin dir in %PATH% so will add it');
      Result := True;
    end
    else
    begin
      Log('Found Gtk2Hs bin dir in %PATH% so will not add it again');
      Result := False;
    end
  end
  else // The key probably doesn't exist
  begin
    Log('Could not access HKCU\Environment\PATH so assume it is ok to add it');
    Result := True;
  end;
end;

{ exec a program and return its output }
function ExecOutput(const Filename, Params: String): String;
var
  TmpFile: String;
  ResultCode: Integer;
begin
  Result := '';
  TmpFile := GenerateUniqueName(ExpandConstant('{tmp}'), 'tmp');
  if Exec(ExpandConstant('{cmd}'), '/C ' + Filename + ' ' + Params + ' > ' + TmpFile,
     '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
    if ResultCode = 0 then
      if LoadStringFromFile(TmpFile, Result) then
      begin
        Result := Trim(Result);
        Log('ExecOutput: running ' + Filename + ' succeded with output: ' + Result);
      end
      else
        Log('ExecOutput: running ' + Filename + ' succeded, but the temp file was not created')
    else
      Log('ExecOutput: running ' + Filename + ' failed, code: ' + IntToStr(ResultCode))
  else
    { That's really odd, we should always be able to exec the command interpreter! }
    Log('ExecOutput: cannot exec command interpreter, code: ' + IntToStr(ResultCode) + ', message: ' + SysErrorMessage(ResultCode));
end;

function CheckGhcVersionIsOk(const Path: String; var Version: String):Boolean;
begin
  Version := ExecOutput(AddBackslash(Path) + 'bin\ghc.exe', '--numeric-version');
  GhcInstallVersion := Version;

  Result := (Version = '6.8.3');
end;

var
  DetectValidGhcInstallationCached: Boolean;
  DetectValidGhcInstallationResult: Boolean;

function DetectValidGhcInstallation(): Boolean;
var
  HaveSomeGHCInstalled: Boolean;
  GHCVersion: String;

begin
if DetectValidGhcInstallationCached then
  Result := DetectValidGhcInstallationResult
else
begin
  Result := False;

  { first check for ghc on the path, then look in the registry }

  begin
    GhcInstallDir := ExecOutput('ghc.exe', '--print-libdir');
    if GhcInstallDir <> '' then
    begin
      StringChange(GhcInstallDir, '/', '\');
      Log('DetectValidGhcInstallation: found ghc on %PATH% with libdir: ' + GhcInstallDir);
      HaveSomeGHCInstalled := True;
      Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
    end;
  end;

  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.6.1', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
{-
  if (Result = False) and RegKeyExists(HKEY_CURRENT_USER, 'Software\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKCU\Software\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Haskell\GHC\ghc-6.4.2', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
-}
  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.6.1', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
{-
  if (Result = False) and RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC') then
  begin
    Log('DetectValidGhcInstallation: found HKLM\SOFTWARE\Haskell\GHC');
    HaveSomeGHCInstalled := True;
    RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Haskell\GHC\ghc-6.4.2', 'InstallDir', GhcInstallDir);
    Result := CheckGhcVersionIsOk(GhcInstallDir, GHCVersion);
  end;
-}
  if Result then
    Log('DetectValidGhcInstallation: correct version of ghc found at: ' + GhcInstallDir)
  else if HaveSomeGHCInstalled and (GHCVersion <> '') then
  begin
    Log('DetectValidGhcInstallation: incorrect ghc version installed: ' + GHCVersion);
    InstallationErrorCaption := 'The version of GHC currently installed is not suitable.';
    InstallationErrorMessage := 'This version of Gtk2Hs requires GHC version 6.6.1.' #13#10 #13#10
                                'Setup found GHC version ' + GHCVersion + ' installed in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled and (GhcInstallDir <> '') then
  begin
    Log('DetectValidGhcInstallation: some non-working version of ghc appears to be installed at: ' + GhcInstallDir);
    InstallationErrorCaption := 'GHC does not seem to be working.';
    InstallationErrorMessage := 'GHC does not appear to be installed correctly, try reinstalling GHC version 6.6.1' #13#10 #13#10
                                'Setup found what appears to be a non-working installation of GHC in the folder:' #13#10
                              + GhcInstallDir
  end
  else if HaveSomeGHCInstalled then
  begin
    Log('DetectValidGhcInstallation: corrupted ghc installation detected, probably messed up registry keys');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'GHC does not appear to be installed (or the installation is corrupted), please install GHC version 6.6.1';
  end
  else
  begin
    Log('DetectValidGhcInstallation: no installation of ghc detected');
    InstallationErrorCaption := 'Setup did not find GHC on your computer.';
    InstallationErrorMessage := 'Gtk2Hs requires GHC to be installed first, please install GHC version 6.6.1' #13#10 #13#10
                                'If you installed GHC manually then make sure ghc.exe is on the path.';
  end;

  If not Result then
  begin
    Log(InstallationErrorCaption);
    Log(InstallationErrorMessage);
  end;

  DetectValidGhcInstallationResult := Result;
  DetectValidGhcInstallationCached := True;
end;
end;

function UseWithGhcVersion(const GhcVersion: String): Boolean;
begin
  DetectValidGhcInstallation();
  Result := (GhcVersion = GhcInstallVersion);
end;

var
  ChecksOk : Boolean;

function ShouldSkipPage(CurPageID: Integer): Boolean;
begin
  Log('ShouldSkipPage: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = ErrorReportPage.ID then
  begin
    if ChecksOk then
      Log('ChecksOk = True')
    else
      Log('ChecksOk = False');
    Result := ChecksOk
  end
  else
    Result := False;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  DllFiles, Offenders: array of String;
  Path, Offender: String;
  N, M, NumFiles: Integer;
begin
  Log('NextButtonClick: CurPageID = ' + IntToStr(CurPageID));
  if CurPageID = wpWelcome then
  begin
    if not DetectValidGhcInstallation() then
      ChecksOk := False
    else
    begin
      DllFiles := [
        'gspawn-win32-helper-console.exe',
        'gspawn-win32-helper.exe',
        'gst-inspect-0.10.exe',
        'gst-launch-0.10.exe',
        'charset.dll',
        'iconv.dll',
        'intl.dll',
        'jpeg62.dll',
        'libORBit-2-0.dll',
        'libORBit-imodule-2-0.dll',
        'libORBitCosNaming-2-0.dll',
        'libatk-1.0-0.dll',
        'libcairo-2.dll',
        'libgconf-2-4.dll',
        'libgdk-win32-2.0-0.dll',
        'libgdk_pixbuf-2.0-0.dll',
        'libgdkglext-win32-1.0-0.dll',
        'libgio-2.0-0.dll',
        'libglade-2.0-0.dll',
        'libglib-2.0-0.dll',
        'libgmodule-2.0-0.dll',
        'libgnomevfs-2-0.dll',
        'libgobject-2.0-0.dll',
        'libgstaudio-0.10.dll',
        'libgstbase-0.10.dll',
        'libgstcdda-0.10.dll',
        'libgstcontroller-0.10.dll',
        'libgstdataprotocol-0.10.dll',
        'libgstfft-0.10.dll',
        'libgstinterfaces-0.10.dll',
        'libgstnet-0.10.dll',
        'libgstnetbuffer-0.10.dll',
        'libgstpbutils-0.10.dll',
        'libgstreamer-0.10.dll',
        'libgstriff-0.10.dll',
        'libgstrtp-0.10.dll',
        'libgstrtsp-0.10.dll',
        'libgstsdp-0.10.dll',
        'libgsttag-0.10.dll',
        'libgstvideo-0.10.dll',
        'libgthread-2.0-0.dll',
        'libgtk-win32-2.0-0.dll',
        'libgtkglext-win32-1.0-0.dll',
        'libpango-1.0-0.dll',
        'libpangocairo-1.0-0.dll',
        'libpangoft2-1.0-0.dll',
        'libpangowin32-1.0-0.dll',
        'libpng12-0.dll',
        'librsvg-2-2.dll',
        'libtiff3.dll',
        'libxml2.dll',
        'zlib1.dll'
        ];
      NumFiles := GetArrayLength(DllFiles);
      SetArrayLength(Offenders, NumFiles);

      RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', Path);
      Path := GetSystemDir() + ';' + GetWinDir() + ';' + Path;

      Log('Checking DLL path: ' + Path);

      CheckingPage.SetText('Checking Gtk+ DLL search path...', '');
      CheckingPage.SetProgress(0, NumFiles);
      CheckingPage.Show;
      try
        M := 0;
        for N := 0 to NumFiles - 1 do
        begin
          Offender := FileSearch(DllFiles[N], Path);
          if Offender <> '' then
          begin
            Log('Found clashing dll/exe file at ' + Offender);
            Offenders[M] := Offender;
            M := M + 1;
          end
          CheckingPage.SetProgress(N+1, NumFiles);
          Sleep(10);
        end;
        if M > 0 then
        begin
          ChecksOk := False;
          InstallationErrorCaption := 'Setup found clashing DLL files on the search path.';
          InstallationErrorMessage := 'Please read the following description of the problem and how to fix it:';

          InstallationErrorDetail :=
              'For Haskell Gtk programs to work properly, the Gtk+ DLL files need to be in the DLL '
            + 'search path. However, the following DLLs files that Gtk+ uses are already on the '
            + 'system-wide search path:' #13#10

          for N := 0 to M - 1 do
            InstallationErrorDetail := InstallationErrorDetail + #13#10 + Offenders[N];

          InstallationErrorDetail := InstallationErrorDetail + #13#10 #13#10
              'The problem is that when you run a Haskell Gtk program these DLLs would get used '
            + 'rather than the proper versions of the Gtk+ DLLs installed by this setup program.'
            + 'This would almost certainly cause all your Haskell Gtk programs to not work.' #13#10 #13#10
              'You can fix this problem in a couple of ways:' #13#10 #13#10
              '1) You could uninstall or delete the program that installed the offending files '
            + '(eg it might be that you have an old version of Gtk+ installed).' #13#10 #13#10
              '2) You could modify the system-wide "Path" envrionment variable and remove the '
            + 'offending directory from it.'
        end
        else
          ChecksOk := True;
      finally
        CheckingPage.Hide;
      end;
    end;
  end
  Result := True;
end;

procedure PackageFileVarSubstitute(const PackageFile: String);
var
  PackageFileContent: String;
  PackageLibDir: String;
begin
  PackageLibDir := ExpandConstant('{app}');
  StringChange(PackageLibDir, '\', '/');

  LoadStringFromFile(PackageFile, PackageFileContent);

  StringChange(PackageFileContent, '${pkglibdir}', PackageLibDir);
  StringChange(PackageFileContent, '${GTK_BASEPATH}', PackageLibDir);

  SaveStringToFile(PackageFile, PackageFileContent, False);
  Log('Expanding variables in ' + PackageFile);
end;

procedure AfterPkgInstall;
begin
  if (ExtractFileExt(CurrentFileName) = '.conf') then
    PackageFileVarSubstitute(ExpandConstant(CurrentFileName));
end;

// Detect when the ready info page gets displayed
function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
                         MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo,
                         MemoTasksInfo: String): String;
begin
		Result := MemoDirInfo + NewLine
		        + NewLine
		        +	'Using GHC ' + GhcInstallVersion + ' installed at:' + NewLine
		        + Space + GhcInstallDir + NewLine
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  BinDir, Path: String;
begin
  if (CurUninstallStep = usPostUninstall)
     and (RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path)) then
  begin
    BinDir := ExpandConstant('{app}\bin');
    if Pos(LowerCase(BinDir) + ';', Lowercase(Path)) <> 0 then
    begin
      StringChange(Path, BinDir + ';', '');
      RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path);
    end;
  end;
end;

procedure ErrorReportPageNotify(Sender: TWizardPage);
begin
  WizardForm.NextButton.Enabled := ErrorContinueBox.Checked;
  ErrorReportPage.Description := InstallationErrorCaption;
  ErrorReportPage.SubCaptionLabel.Caption := InstallationErrorMessage;
  if InstallationErrorDetail = '' then
    ErrorReportPage.SubCaptionLabel.Height := 50
  else
  begin
    ErrorReportPage.RichEditViewer.RTFText := InstallationErrorDetail
    ErrorReportPage.RichEditViewer.Show();
    ErrorContinueBox.Show();
  end;
end;

procedure ErrorContinueClicked(Sender: TObject);
begin
  WizardForm.NextButton.Enabled := ErrorContinueBox.Checked;
end;

procedure InitializeWizard();
begin
  CheckingPage := CreateOutputProgressPage('Checking requirements', 'Checking for GHC and Gtk+');

  ErrorReportPage := CreateOutputMsgMemoPage(wpWelcome, 'Setup cannot continue', 'GHC or Gtk+ problem', 'Your installation of GHC or Gtk+ is messed up!', 'Foo bar!');
  ErrorReportPage.RichEditViewer.Height := ErrorReportPage.RichEditViewer.Height - ScaleY(22);
  ErrorReportPage.OnActivate := @ErrorReportPageNotify;
  ErrorReportPage.RichEditViewer.Hide();

  ErrorContinueBox := TCheckBox.Create(ErrorReportPage);
  ErrorContinueBox.Parent := ErrorReportPage.Surface;
  ErrorContinueBox.Top := ErrorReportPage.RichEditViewer.Height + ErrorReportPage.RichEditViewer.Top + ScaleY(8);
  ErrorContinueBox.Width  := ErrorReportPage.RichEditViewer.Width;
  ErrorContinueBox.Caption := 'Continue anyway. (You will have to fix the DLL search path problem yourself later.)';
  ErrorContinueBox.Checked := False;
  ErrorContinueBox.OnClick := @ErrorContinueClicked;
end;