Fix WorkingDir.

Fix WorkingDir.

; RailsInstaller - InnoSetup Script
; Adaptation from RubyInstaller installer script
;

; PRE-CHECK
; Verify that RubyPath is defined by ISCC using
; /d command line arguments.
;
; Usage:
;  iscc rubyinstaller.iss /dInstallerVersion=0.1.0
;                         /dStagePath=stage
;                         /dRubyPath=Ruby/1.8.7
;                         [/dInstVersion=26-OCT-2009]

; Full example:
; iscc resouces\railsinstaller\railsinstaller.iss \
;       /dInstallerVersion=0.1.0 \
;       /dStagePath=stage \
;       /dRubyPath=Ruby1.8.7 \
;       /opkg
;       /frailsinstaller-0.1.0.exe

#if Defined(InstallerVersion) == 0
  #error Please provide a InstallerVersion definition using a /d parameter.
#endif

#if Defined(StagePath) == 0
  #error Please provide a StagePath value to the Ruby files using a /d parameter.
#endif

#if Defined(RubyPath) == 0
  #error Please provide a RubyPath value to the Ruby files using a /d parameter.
#else
  #if FileExists(StagePath + '/' + RubyPath + '\bin\ruby.exe') == 0
    #error No Ruby installation (bin\ruby.exe) found inside defined RubyPath. Please verify.
  #endif
#endif

#if Defined(InstVersion) == 0
  #define InstVersion GetDateTimeString('dd-mmm-yy"T"hhnn', '', '')
#endif

; Build Installer details using above values
#define InstallerName "RailsInstaller"
#define InstallerNameWithVersion InstallerName + " " + InstallerVersion
#define InstallerPublisher "RailsInstaller Team"
#define InstallerHomepage "http://www.railsinstaller.org/"

#define CurrentYear GetDateTimeString('yyyy', '', '')

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications!
AppId={{613C3EA5-1248-4E35-B61A-6D0B31BBC0DB}
AppName={#InstallerName}
AppVerName={#InstallerNameWithVersion}
AppPublisher={#InstallerPublisher}
AppPublisherURL={#InstallerHomepage}
AppVersion={#InstallerVersion}
DefaultGroupName={#InstallerName}
DefaultDirName={sd}\RailsInstaller
DisableProgramGroupPage=true
LicenseFile=LICENSE.txt
Compression=lzma2/ultra64
SolidCompression=true
AlwaysShowComponentsList=false
DisableReadyPage=true
InternalCompressLevel=ultra64
VersionInfoCompany={#InstallerPublisher}
VersionInfoCopyright=(c) {#CurrentYear} {#InstallerPublisher}
VersionInfoDescription=Rails development environment installer for Windows
VersionInfoTextVersion={#InstallerVersion}
VersionInfoVersion={#InstallerVersion}
UninstallDisplayIcon={app}\bin\ruby.exe
WizardImageFile={#ResourcesPath}\images\RailsInstallerWizardImage.bmp
WizardSmallImageFile={#ResourcesPath}\images\RailsInstallerWizardImageSmall.bmp
PrivilegesRequired=lowest
ChangesAssociations=yes
ChangesEnvironment=yes

#if Defined(SignPackage) == 1
SignTool=risigntool sign /a /d $q{#InstallerNameWithVersion}$q /du $q{#InstallerHomepage}$q /t $qhttp://timestamp.comodoca.com/authenticode$q $f
#endif

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

[Messages]
en.InstallingLabel=Installing [name], this will take a few minutes...
en.WelcomeLabel1=Welcome to [name]!
en.WelcomeLabel2=This will install [name/ver] on your computer which includes Ruby 1.8.7, Git, Sqlite3, DevKit, and Rails 3.  Please close any console applications before continuing.
en.WizardLicense={#InstallerName} License Agreement
en.LicenseLabel=
en.LicenseLabel3=Please read the following License Agreements and accept the terms before continuing the installation.
en.LicenseAccepted=I &accept all of the Licenses
en.LicenseNotAccepted=I &decline any of the Licenses
en.WizardSelectDir=Installation Destination and Optional Tasks
en.SelectDirDesc=This is the location that Ruby, DevKit, Git, Rails and Sqlite will be installed to.
en.SelectDirLabel3=[name] will be installed into the following folder. Click Install to continue or click Browse to use a different one.
en.SelectDirBrowseLabel=Please avoid any folder name that contains spaces (e.g. Program Files).
en.DiskSpaceMBLabel=Required free disk space: ~[mb] MB

[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: {#StagePath}\{#RubyPath}\*; DestDir: {app}\{#RubyPath}; Excludes: "devkit.*, operating_system.*"; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\Git\*; DestDir: {app}\Git; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\DevKit\*; DestDir: {app}\DevKit; Excludes: "config.yml"; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\DevKit\config.yml; DestDir: {app}\DevKit; AfterInstall: UpdateDevKitConfig('{app}\{#RubyPath}', '{app}\DevKit\config.yml')
Source: {#StagePath}\Sites\*; DestDir: {sd}\Sites; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\pkg\vcredist_x86.exe; DestDir: {tmp}; Flags: deleteafterinstall
Source: setup_environment.bat; DestDir: {app}\{#RubyPath}

[Registry]
; FIXME: Proper registry keys for RailsInstaller (admin)
;Root: HKLM; Subkey: Software\RailsInstaller; ValueType: string; ValueName: ; ValueData: ; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsAdmin

; FIXME: Proper registry key for RailsInstaller (user)
;Root: HKCU; Subkey: Software\RailsInstaller; ValueType: string; ValueName: ; ValueData: ; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsNotAdmin

[Icons]
Name: {group}\Interactive Ruby; Filename: {app}\{#RubyPath}\bin\irb.bat; IconFilename: {app}\{#RubyPath}\bin\ruby.exe; Flags: createonlyiffileexists
Name: {group}\RubyGems Documentation Server; Filename: {app}\{#RubyPath}\bin\gem.bat; Parameters: server; IconFilename: {app}\{#RubyPath}\bin\ruby.exe; Flags: createonlyiffileexists runminimized
Name: {group}\Command Prompt with Ruby and Rails; Filename: {sys}\cmd.exe; Parameters: /E:ON /K {app}\{#RubyPath}\setup_environment.bat; WorkingDir: {sd}\Sites; IconFilename: {sys}\cmd.exe; Flags: createonlyiffileexists
; {%HOMEPATH%}
Name: {group}\{cm:UninstallProgram,{#InstallerName}}; Filename: {uninstallexe}

[Run]
Filename: "{app}\{#RubyPath}\bin\ruby.exe"; Parameters: "dk.rb install --force"; WorkingDir: "{app}\DevKit"; Flags: runhidden
Filename: "{tmp}\vcredist_x86.exe"; Parameters: "/q"; WorkingDir: "{tmp}"; Flags: runhidden

[Code]
#include "util.iss"
#include "railsinstaller_gui.iss"

function GetInstallDate(Param: String): String;
begin
  Result := GetDateTimeString('yyyymmdd', #0 , #0);
end;

procedure CurStepChanged(const CurStep: TSetupStep);
begin

  // TODO move into ssPostInstall just after install completes?
  if CurStep = ssInstall then
  begin
    if UsingWinNT then
    begin
      Log(Format('Selected Tasks - Path: %d', [PathChkBox.State]));

      if IsModifyPath then
        ModifyPath([ExpandConstant('{app}') + '\{#RubyPath}\bin']);
        ModifyPath([ExpandConstant('{app}') + '\Git\cmd']);

    end else
      MsgBox('Looks like you''ve got on older, unsupported Windows version.' #13 +
             'Proceeding with a reduced feature set installation.',
             mbInformation, MB_OK);
  end;
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
begin
  {* store install choices so we can use during uninstall *}
  if IsModifyPath then
    SetPreviousData(PreviousDataKey, 'PathModified', 'yes');

  SetPreviousData(PreviousDataKey, 'RailsInstallerId', '{#InstallerVersion}');
end;

procedure CurUninstallStepChanged(const CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
  begin
    if UsingWinNT then
    begin
      if GetPreviousData('PathModified', 'no') = 'yes' then
        ModifyPath([ExpandConstant('{app}') + '\{#RubyPath}\bin']);
        ModifyPath([ExpandConstant('{app}') + '\Git\cmd']);
    end;
  end;
end;

procedure UpdateDevKitConfig(RubyPath: string; FileName: string);
var
  S: String;
begin
  // Make YAML happy :-)
  S := ExpandConstant(RubyPath);
  StringChangeEx(S, '\', '/', True);

  // Update DevKit config.yml with the installation path
  SaveStringToFile(ExpandConstant(FileName), '- ' + S, False);
end;

; RailsInstaller - InnoSetup Script
; Adaptation from RubyInstaller installer script
;

; PRE-CHECK
; Verify that RubyPath is defined by ISCC using
; /d command line arguments.
;
; Usage:
;  iscc rubyinstaller.iss /dInstallerVersion=0.1.0
;                         /dStagePath=stage
;                         /dRubyPath=Ruby/1.8.7
;                         [/dInstVersion=26-OCT-2009]

; Full example:
; iscc resouces\railsinstaller\railsinstaller.iss \
;       /dInstallerVersion=0.1.0 \
;       /dStagePath=stage \
;       /dRubyPath=Ruby1.8.7 \
;       /opkg
;       /frailsinstaller-0.1.0.exe

#if Defined(InstallerVersion) == 0
  #error Please provide a InstallerVersion definition using a /d parameter.
#endif

#if Defined(StagePath) == 0
  #error Please provide a StagePath value to the Ruby files using a /d parameter.
#endif

#if Defined(RubyPath) == 0
  #error Please provide a RubyPath value to the Ruby files using a /d parameter.
#else
  #if FileExists(StagePath + '/' + RubyPath + '\bin\ruby.exe') == 0
    #error No Ruby installation (bin\ruby.exe) found inside defined RubyPath. Please verify.
  #endif
#endif

#if Defined(InstVersion) == 0
  #define InstVersion GetDateTimeString('dd-mmm-yy"T"hhnn', '', '')
#endif

; Build Installer details using above values
#define InstallerName "RailsInstaller"
#define InstallerNameWithVersion InstallerName + " " + InstallerVersion
#define InstallerPublisher "RailsInstaller Team"
#define InstallerHomepage "http://www.railsinstaller.org/"

#define CurrentYear GetDateTimeString('yyyy', '', '')

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications!
AppId={{613C3EA5-1248-4E35-B61A-6D0B31BBC0DB}
AppName={#InstallerName}
AppVerName={#InstallerNameWithVersion}
AppPublisher={#InstallerPublisher}
AppPublisherURL={#InstallerHomepage}
AppVersion={#InstallerVersion}
DefaultGroupName={#InstallerName}
DefaultDirName={sd}\RailsInstaller
DisableProgramGroupPage=true
LicenseFile=LICENSE.txt
Compression=lzma2/ultra64
SolidCompression=true
AlwaysShowComponentsList=false
DisableReadyPage=true
InternalCompressLevel=ultra64
VersionInfoCompany={#InstallerPublisher}
VersionInfoCopyright=(c) {#CurrentYear} {#InstallerPublisher}
VersionInfoDescription=Rails development environment installer for Windows
VersionInfoTextVersion={#InstallerVersion}
VersionInfoVersion={#InstallerVersion}
UninstallDisplayIcon={app}\bin\ruby.exe
WizardImageFile={#ResourcesPath}\images\RailsInstallerWizardImage.bmp
WizardSmallImageFile={#ResourcesPath}\images\RailsInstallerWizardImageSmall.bmp
PrivilegesRequired=lowest
ChangesAssociations=yes
ChangesEnvironment=yes

#if Defined(SignPackage) == 1
SignTool=risigntool sign /a /d $q{#InstallerNameWithVersion}$q /du $q{#InstallerHomepage}$q /t $qhttp://timestamp.comodoca.com/authenticode$q $f
#endif

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

[Messages]
en.InstallingLabel=Installing [name], this will take a few minutes...
en.WelcomeLabel1=Welcome to [name]!
en.WelcomeLabel2=This will install [name/ver] on your computer which includes Ruby 1.8.7, Git, Sqlite3, DevKit, and Rails 3.  Please close any console applications before continuing.
en.WizardLicense={#InstallerName} License Agreement
en.LicenseLabel=
en.LicenseLabel3=Please read the following License Agreements and accept the terms before continuing the installation.
en.LicenseAccepted=I &accept all of the Licenses
en.LicenseNotAccepted=I &decline any of the Licenses
en.WizardSelectDir=Installation Destination and Optional Tasks
en.SelectDirDesc=This is the location that Ruby, DevKit, Git, Rails and Sqlite will be installed to.
en.SelectDirLabel3=[name] will be installed into the following folder. Click Install to continue or click Browse to use a different one.
en.SelectDirBrowseLabel=Please avoid any folder name that contains spaces (e.g. Program Files).
en.DiskSpaceMBLabel=Required free disk space: ~[mb] MB

[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: {#StagePath}\{#RubyPath}\*; DestDir: {app}\{#RubyPath}; Excludes: "devkit.*, operating_system.*"; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\Git\*; DestDir: {app}\Git; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\DevKit\*; DestDir: {app}\DevKit; Excludes: "config.yml"; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\DevKit\config.yml; DestDir: {app}\DevKit; AfterInstall: UpdateDevKitConfig('{app}\{#RubyPath}', '{app}\DevKit\config.yml')
Source: {#StagePath}\Sites\*; DestDir: {sd}\Sites; Flags: recursesubdirs createallsubdirs
Source: {#StagePath}\pkg\vcredist_x86.exe; DestDir: {tmp}; Flags: deleteafterinstall
Source: setup_environment.bat; DestDir: {app}\{#RubyPath}

[Registry]
; FIXME: Proper registry keys for RailsInstaller (admin)
;Root: HKLM; Subkey: Software\RailsInstaller; ValueType: string; ValueName: ; ValueData: ; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsAdmin

; FIXME: Proper registry key for RailsInstaller (user)
;Root: HKCU; Subkey: Software\RailsInstaller; ValueType: string; ValueName: ; ValueData: ; Flags: uninsdeletevalue uninsdeletekeyifempty; Check: IsNotAdmin

[Icons]
Name: {group}\Interactive Ruby; Filename: {app}\{#RubyPath}\bin\irb.bat; IconFilename: {app}\{#RubyPath}\bin\ruby.exe; Flags: createonlyiffileexists
Name: {group}\RubyGems Documentation Server; Filename: {app}\{#RubyPath}\bin\gem.bat; Parameters: server; IconFilename: {app}\{#RubyPath}\bin\ruby.exe; Flags: createonlyiffileexists runminimized
Name: {group}\Command Prompt with Ruby and Rails; Filename: {sys}\cmd.exe; Parameters: /E:ON /K {app}\{#RubyPath}\setup_environment.bat; WorkingDir: {sd}\Sites; IconFilename: {sys}\cmd.exe; Flags: createonlyiffileexists
; {%HOMEPATH%}
Name: {group}\{cm:UninstallProgram,{#InstallerName}}; Filename: {uninstallexe}

[Run]
Filename: "{app}\{#RubyPath}\bin\ruby.exe"; Parameters: "dk.rb install --force"; WorkingDir: "{app}\DevKit"; Flags: runhidden
Filename: "{tmp}\vcredist_x86.exe"; Parameters: "/q"; WorkingDir: "{app}\scripts"; Flags: runhidden

[Code]
#include "util.iss"
#include "railsinstaller_gui.iss"

function GetInstallDate(Param: String): String;
begin
  Result := GetDateTimeString('yyyymmdd', #0 , #0);
end;

procedure CurStepChanged(const CurStep: TSetupStep);
begin

  // TODO move into ssPostInstall just after install completes?
  if CurStep = ssInstall then
  begin
    if UsingWinNT then
    begin
      Log(Format('Selected Tasks - Path: %d', [PathChkBox.State]));

      if IsModifyPath then
        ModifyPath([ExpandConstant('{app}') + '\{#RubyPath}\bin']);
        ModifyPath([ExpandConstant('{app}') + '\Git\cmd']);

    end else
      MsgBox('Looks like you''ve got on older, unsupported Windows version.' #13 +
             'Proceeding with a reduced feature set installation.',
             mbInformation, MB_OK);
  end;
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
begin
  {* store install choices so we can use during uninstall *}
  if IsModifyPath then
    SetPreviousData(PreviousDataKey, 'PathModified', 'yes');

  SetPreviousData(PreviousDataKey, 'RailsInstallerId', '{#InstallerVersion}');
end;

procedure CurUninstallStepChanged(const CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
  begin
    if UsingWinNT then
    begin
      if GetPreviousData('PathModified', 'no') = 'yes' then
        ModifyPath([ExpandConstant('{app}') + '\{#RubyPath}\bin']);
        ModifyPath([ExpandConstant('{app}') + '\Git\cmd']);
    end;
  end;
end;

procedure UpdateDevKitConfig(RubyPath: string; FileName: string);
var
  S: String;
begin
  // Make YAML happy :-)
  S := ExpandConstant(RubyPath);
  StringChangeEx(S, '\', '/', True);

  // Update DevKit config.yml with the installation path
  SaveStringToFile(ExpandConstant(FileName), '- ' + S, False);
end;


make the win32 installer not require admin rights

; 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;

; 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

[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;


Fix album cover functionality on systems with UAC enabled: save htdocs to write enabled directory.

#define SourceDir "..\"
#define AppName "AIMP Control Plugin"
#define SrcApp "temp_build\Release\aimp_control_plugin.dll"
; use absolute path as GetFileVersion() argument since there is problem with relative path when we execute this script from command line.
#define FileVerStr GetFileVersion(SourcePath + "\" + SourceDir + SrcApp)
#define StripBuild(VerStr) Copy(VerStr, 1, RPos(".", VerStr)-1)
#define AppVerStr StripBuild(FileVerStr)
#define PluginWorkDirectoryName "Control Plugin"
#define AimpPluginsDirectoryName "Plugins"
#define FreeImage_VERSION GetEnv('FreeImage_VERSION')

[Setup]

SourceDir={#SourceDir}
AppName={#AppName}
AppVersion={#AppVerStr}
AppVerName={#AppName} {#AppVerStr}
UninstallDisplayName={#AppName} {#AppVerStr}
UninstallFilesDir={app}\{#PluginWorkDirectoryName}
VersionInfoVersion={#FileVerStr}
VersionInfoTextVersion={#AppVerStr}
VersionInfoDescription=AIMP player plugin. Provides network access to AIMP player.
VersionInfoProductName={#AppName}
VersionInfoProductVersion={#FileVerStr}
OutputBaseFilename=aimp_control_plugin-{#FileVerStr}-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={{F171581D-00CD-4E77-8982-B1B68FDCAAFA}
AppPublisher=Alexey Ivanov
AppCopyright=Copyright  2013 Alexey Ivanov
AppPublisherURL=http://code.google.com/p/aimp-control-plugin/
AppSupportURL=http://code.google.com/p/aimp-control-plugin/w/list
AppUpdatesURL=http://code.google.com/p/aimp-control-plugin/downloads/list

DefaultDirName={#AimpPluginsDirectoryName}
DefaultGroupName={#AppName}
DisableProgramGroupPage=true
OutputDir=temp_build\Release\distrib\
SetupIconFile=inno_setup_data\icons\icon_aimp3_32x32.ico
Compression=lzma/ultra
SolidCompression=true
InternalCompressLevel=ultra
PrivilegesRequired=admin
DirExistsWarning=no
AllowUNCPath=false
AppendDefaultDirName=true

[Languages]
; Language name is used on Donation page as {language} constant.
Name: en; MessagesFile: inno_setup_data\English.isl; LicenseFile: Lisense-English.txt;
Name: ru; MessagesFile: inno_setup_data\Russian.isl; LicenseFile: Lisense-Russian.txt;

[Files]
Source: "{#SrcApp}"; DestDir: "{app}"; Flags: ignoreversion
Source: "temp_build\Release\htdocs\*"; DestDir: "{code:GetBrowserScriptsDir}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: "inno_setup_data\default_settings.dat"; DestDir: "{code:GetPluginSettingsDir}"; DestName: "settings.dat"; Flags: onlyifdoesntexist; AfterInstall: AfterInstallSettingsFile( ExpandConstant('{code:GetPluginSettingsDir}\settings.dat') )
Source: "3rd_party\FreeImage\{#FreeImage_VERSION}\Dist\FreeImage.dll"; DestDir: "{code:GetFreeImageDLLsDstDir}"; Flags: ignoreversion
Source: "3rd_party\FreeImage\{#FreeImage_VERSION}\Wrapper\FreeImagePlus\dist\FreeImagePlus.dll"; DestDir: "{code:GetFreeImageDLLsDstDir}"; Flags: ignoreversion

[Registry]
; etc.

[Code]
var
  BrowserScriptsDirPage: TInputDirWizardPage;
  AimpVersionSelectionPage: TInputOptionWizardPage;
  NetworkSetupPage: TInputOptionWizardPage;
  AfterInstallPage: TOutputMsgMemoWizardPage;
  AllowNetworkAccess: Boolean;
  SettingsFileDestination: String;
  Port: Integer;

procedure InitDonationPage; forward;
procedure InitializeWizard;
begin
  { Set AIMP player version, used to determine default path of Aimp's Plugins directory. }
  AimpVersionSelectionPage := CreateInputOptionPage(wpLicense,
                                                    ExpandConstant('{cm:AimpVerionSelectionMsg1}'),
                                                    ExpandConstant('{cm:AimpVerionSelectionMsg2}'),
                                                    ExpandConstant('{cm:AimpVerionSelectionMsg3}'),
                                                    True,
                                                    False
                                                    );
  AimpVersionSelectionPage.Add('AIMP2');
  AimpVersionSelectionPage.Add('AIMP3');

  AimpVersionSelectionPage.SelectedValueIndex := 1; { default is AIMP3 }

  BrowserScriptsDirPage := CreateInputDirPage(wpSelectDir,
                                              ExpandConstant('{cm:BrowserScriptsDirSelectionMsg1}'),
                                              ExpandConstant('{cm:BrowserScriptsDirSelectionMsg2}'),
                                              ExpandConstant('{cm:BrowserScriptsDirSelectionMsg3}'),
                                              False,
                                              ''
                                              );
  BrowserScriptsDirPage.Add('');

  InitDonationPage();

  NetworkSetupPage := CreateInputOptionPage(BrowserScriptsDirPage.ID,
                                            ExpandConstant('{cm:NetworkSetupTitle}'),
                                            ExpandConstant('{cm:NetworkSetupDescription}'),
                                            ExpandConstant('{cm:NetworkSetupSubDescription}'),
                                            False, False);

  NetworkSetupPage.Add(ExpandConstant('{cm:NetworkSetupCheckBox}'));

  AfterInstallPage := CreateOutputMsgMemoPage(wpInfoAfter,
   SetupMessage(msgWizardInfoAfter),
   SetupMessage(msgInfoAfterLabel),
   SetupMessage(msgInfoAfterClickLabel),
   '' // memo text will be set later by AfterInstallPage.RichEditViewer.RTFText.
  );

  Port := 3333;
end;

procedure TerminateAimpExecutionIfNeedeed(); forward;
function GetInfoAfterMemoText() : String; forward;
function DefaultWritableBrowserScriptsDir(): String; forward;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = wpSelectDir then
    begin
    WizardForm.DirEdit.Text := ExpandConstant('{pf}\{code:GetAimpPlayerAppDirName}\{code:AimpPluginsDirName}')

    TerminateAimpExecutionIfNeedeed(); { must do this after WizardForm.DirEdit.Text set. }
    end
  else if CurPageID = BrowserScriptsDirPage.ID then
    begin
    SettingsFileDestination := ExpandConstant('{code:GetPluginSettingsDir}\settings.dat');

    { Set default values, using settings that were stored last time if possible }
    BrowserScriptsDirPage.Values[0] := DefaultWritableBrowserScriptsDir();
    end
  else if CurPageID = wpReady then
     begin
     AllowNetworkAccess := NetworkSetupPage.Values[0];
     AfterInstallPage.RichEditViewer.RTFText := GetInfoAfterMemoText();
     end
end;

function DefaultWritableBrowserScriptsDir(): String;
begin
  //GetPreviousData( 'BrowserScriptsDir',
  //                  ExpandConstant('{code:GetPluginWorkDir}\htdocs')
  //                );

  Result := ExpandConstant('{userappdata}\{code:GetAimpPlayerAppDataDirName}\{#PluginWorkDirectoryName}\htdocs');
end;

function IsAimp2(): Boolean; forward;

function GetAimpPlayerAppDataDirName(): String;
begin
  { Return the name of AppData directory for selected AIMP player version }
  if IsAimp2() then
	  Result := 'AIMP'
  else
	  Result := 'AIMP3';
end;

function GetBrowserScriptsDir(Param: String): String;
begin
  { Return the selected BrowserScriptsDir }
  Result := BrowserScriptsDirPage.Values[0];
end;

function IsAimp2(): Boolean;
begin
   Result := AimpVersionSelectionPage.SelectedValueIndex = 0;
end;

function GetPluginWorkDir(Param: String): String;
begin
  Result := ExpandConstant('{app}\{#PluginWorkDirectoryName}')
end;

function GetPluginSettingsDir(Param: String): String;
begin
  Result := ExpandConstant('{app}\{#PluginWorkDirectoryName}')
end;

function GetFreeImageDLLsDstDir(Param: String): String;
begin
  Result := ExpandConstant('{app}\{#PluginWorkDirectoryName}')
end;

function AimpPluginsDirName(Param: String): String;
begin
  { Return name of AIMP's Plugins directory. }
  Result := ExpandConstant('{#AimpPluginsDirectoryName}')
  { We do not distinguish between AIMP2/3 Plugins directory names
    since supporting of this difference is hard
    while Windows is case insensitive.
  if IsAimp2() then
    Result := 'PlugIns'
  else
    Result := 'Plugins';
  }
end;

function GetAimpPlayerAppDirName(Param: String): String;
begin
  { Return the name of program directory for selected AIMP player version }
  if IsAimp2() then
    Result := 'AIMP2'
  else
    Result := 'AIMP3';
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
begin
  SetPreviousData(PreviousDataKey, 'BrowserScriptsDir', BrowserScriptsDirPage.Values[0]);
end;

procedure AfterInstallSettingsFile(Path: String);
var
  // See http://msdn.microsoft.com/en-us/library/ms757878(VS.85).aspx for details about
  XMLDoc : Variant;       // IXMLDOMDocument
  DocRootNodes : Variant; // IXMLDOMNodeList
  DocRootNode : Variant;  // IXMLDOMNode
begin
  //MsgBox('Try to load the XML file: ' + Path, mbInformation, mb_Ok);

  { Load the XML File }
  XMLDoc := CreateOleObject('MSXML2.DOMDocument');
  XMLDoc.async := False;
  XMLDoc.resolveExternals := False;
  XMLDoc.setProperty('SelectionLanguage', 'XPath');
  XMLDoc.load(Path);
  if XMLDoc.parseError.errorCode <> 0 then
    RaiseException('Error in xml file "' + Path + '" on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' + IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason);

  //MsgBox('Loaded the XML file.', mbInformation, mb_Ok);

  { Modify the XML document }
  DocRootNodes := XMLDoc.selectNodes('//httpserver/document_root');
  //MsgBox('Mathing nodes: ' + IntToStr(DocRootNodes.length), mbInformation, mb_Ok);
  DocRootNode := DocRootNodes.item(0);
  DocRootNode.Text := GetBrowserScriptsDir('');

  if (AllowNetworkAccess) then
    XMLDoc.selectNodes('//httpserver/ip_to_bind').item(0).Text := '';

  Port := XMLDoc.selectNodes('//httpserver/port').item(0).Text;

  { Save the XML document }
  XMLDoc.Save(Path);
  //MsgBox('Saved the modified XML as ''' + Path + '''.', mbInformation, mb_Ok);
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
begin
  { Fill the 'Ready Memo' with the normal settings and the custom settings }
  S := '';
  S := S + MemoDirInfo + NewLine;
  S := S + ExpandConstant('{cm:BrowserScriptsDirDestination}') + NewLine + Space + GetBrowserScriptsDir('') + NewLine;
  S := S + ExpandConstant('{cm:SettingsFileDestination}') + NewLine + Space + SettingsFileDestination + NewLine;
  Result := S;
end;

function GetAimpHWND(): HWND;
begin
  Result := FindWindowByClassName('AIMP2_RemoteInfo');
end;

function IsAimpLaunched(): boolean;
begin
  Result := GetAimpHWND() <> 0;
end;

const
  WM_AIMP_COMMAND = 1024 + $75;
  WM_AIMP_CALLFUNC = 3;
  AIMP_QUIT = 10;
procedure KillAimp();
begin
  SendMessage(GetAimpHWND(), WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_QUIT);
end;

procedure TerminateAimpExecutionIfNeedeed();
begin
  if IsAimpLaunched() then
    if MsgBox(ExpandConstant('{cm:AimpApplicationTerminateQuery}'),
          mbConfirmation,
          MB_YESNO) = IDYES then
      KillAimp();
end;

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

function GetBrowserScriptsStringDirectlyFromRegistry(): String;
var
  sUnInstPath: String;
  sBrowserScriptsString: String;
begin
  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  sBrowserScriptsString := '';
  if not RegQueryStringValue(HKLM, sUnInstPath, 'Inno Setup CodeFile: BrowserScriptsDir', sBrowserScriptsString) then
    RegQueryStringValue(HKCU, sUnInstPath, 'Inno Setup CodeFile: BrowserScriptsDir', sBrowserScriptsString);

  Result := sBrowserScriptsString;
end;

function GetBrowserScriptsString(): String;
begin
  Result := GetPreviousData('BrowserScriptsDir', '');
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;

function RemoveInstalledBrowserScripts(): Integer;
var
  sBrowserScriptsDir: String;
begin
// Return Values:
// 1 - Browser scripts dir string is empty
// 2 - error executing the DelTree
// 3 - successfully executed the DelTree

  // default return value
  Result := 0;

  // get the uninstall string of the old app.
  sBrowserScriptsDir := GetBrowserScriptsString();
  if sBrowserScriptsDir <> '' then begin
    sBrowserScriptsDir := RemoveQuotes(sBrowserScriptsDir);
    if DelTree(sBrowserScriptsDir, True, True, True) then
      Result := 3
    else
      Result := 2;
  end else
    Result := 1;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep=ssInstall) then
  begin
    if (IsUpgrade()) then
    begin
      // Do not uninstall previous version till it will be possible remain settings file.
      // Instead old remove browser scripts.
      // UnInstallOldVersion();
      RemoveInstalledBrowserScripts();
    end;
  end;
end;

procedure DonationButtonOnClick(Sender: TObject);
var
  ErrorCode: Integer;
  URL: String;
begin
  URL := 'http://www.a0ivanov.ru/donate_' + ExpandConstant('{language}') + '.html';

  if not ShellExec('', URL, '', '', SW_SHOW, ewNoWait, ErrorCode) then
  begin
    // handle failure if necessary
  end;
end;

procedure InitDonationPage;
var
  Page: TWizardPage;
  Button: TNewButton;
  Label1: TLabel;
begin
  Page := CreateCustomPage(wpInfoAfter, ExpandConstant('{cm:DonatePageTitle}'), ExpandConstant('{cm:DonateMsg}')); // cm:DonatePageTitle2

  Label1 := TLabel.Create(Page);
  Label1.AutoSize := False;
  Label1.Top := ScaleY(15);
  Label1.Left := ScaleX(0);
  Label1.Width := Page.SurfaceWidth;
  Label1.Alignment := taCenter;
  Label1.Caption := ExpandConstant('{cm:DonateMsg}');
  Label1.WordWrap := True;
  Label1.Parent := Page.Surface;

  Button := TNewButton.Create(Page);
  Button.Width := ScaleX(80);
  Button.Height := ScaleY(26);
  Button.Caption := ExpandConstant('{cm:DonateButton}');
  Button.OnClick := @DonationButtonOnClick;
  Button.Parent := Page.Surface;
  Button.Top := Label1.Top + Label1.Height + ScaleY(8);
  Button.Left := Label1.Left + (Label1.Width - Button.Width) / 2;
end;

procedure GetIpAddresses(Addresses : TStringList); forward;
function GetInfoAfterMemoText(): String;
var
  SL : TStringList;
  I: Integer;
  Msg: String;
begin
  if (AllowNetworkAccess) then
     begin
     Msg := ExpandConstant('{cm:InfoAfterPageMemoTextRemoteBegin}');
     SL := TStringList.Create;
     GetIpAddresses(SL);
     For I := 0 to SL.Count - 1 do
       begin
        Msg := Msg + #13#10 + FmtMessage('http://%1:%2/index.htm', [SL.Strings[I], IntToStr(Port)]);
       end;
     SL.Free;
     Msg := Msg + #13#10 + ExpandConstant('{cm:InfoAfterPageMemoTextRemoteEnd}');
     end
   else
     Msg := ExpandConstant('{cm:InfoAfterPageMemoTextLocal}');
   Result := Msg;
end;

const
 ERROR_INSUFFICIENT_BUFFER = 122;

function GetIpAddrTable( pIpAddrTable: Array of Byte;
  var pdwSize: Cardinal; bOrder: WordBool ): DWORD;
external 'GetIpAddrTable@IpHlpApi.dll stdcall';

procedure GetIpAddresses(Addresses : TStringList);
var
 Size : Cardinal;
 Buffer : Array of Byte;
 IpAddr : String;
 AddrCount : Integer;
 I, J : Integer;
begin
  // Find Size
  if GetIpAddrTable(Buffer,Size,False) = ERROR_INSUFFICIENT_BUFFER then
  begin
     // Allocate Buffer with large enough size
     SetLength(Buffer,Size);
     // Get List of IP Addresses into Buffer
     if GetIpAddrTable(Buffer,Size,True) = 0 then
     begin
       // Find out how many addresses will be returned.
       AddrCount := (Buffer[1] * 256) + Buffer[0];
       // Loop through addresses.
       For I := 0 to AddrCount -1 do
       begin
         IpAddr := '';
         // Loop through each byte of the address
         For J := 0 to 3 do
         begin
           if J > 0 then
             IpAddr := IpAddr + '.';
           // Navigagte through record structure to find correct byte of Addr
           IpAddr := IpAddr + IntToStr(Buffer[I*24+J+4]);
         end;
         Addresses.Add(IpAddr);
       end;
     end;
  end;
end;

#define SourceDir "..\"
#define AppName "AIMP Control Plugin"
#define SrcApp "temp_build\Release\aimp_control_plugin.dll"
; use absolute path as GetFileVersion() argument since there is problem with relative path when we execute this script from command line.
#define FileVerStr GetFileVersion(SourcePath + "\" + SourceDir + SrcApp)
#define StripBuild(VerStr) Copy(VerStr, 1, RPos(".", VerStr)-1)
#define AppVerStr StripBuild(FileVerStr)
#define PluginWorkDirectoryName "Control Plugin"
#define AimpPluginsDirectoryName "Plugins"
#define FreeImage_VERSION GetEnv('FreeImage_VERSION')

[Setup]

SourceDir={#SourceDir}
AppName={#AppName}
AppVersion={#AppVerStr}
AppVerName={#AppName} {#AppVerStr}
UninstallDisplayName={#AppName} {#AppVerStr}
UninstallFilesDir={app}\{#PluginWorkDirectoryName}
VersionInfoVersion={#FileVerStr}
VersionInfoTextVersion={#AppVerStr}
VersionInfoDescription=AIMP player plugin. Provides network access to AIMP player.
VersionInfoProductName={#AppName}
VersionInfoProductVersion={#FileVerStr}
OutputBaseFilename=aimp_control_plugin-{#FileVerStr}-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={{F171581D-00CD-4E77-8982-B1B68FDCAAFA}
AppPublisher=Alexey Ivanov
AppCopyright=Copyright  2013 Alexey Ivanov
AppPublisherURL=http://code.google.com/p/aimp-control-plugin/
AppSupportURL=http://code.google.com/p/aimp-control-plugin/w/list
AppUpdatesURL=http://code.google.com/p/aimp-control-plugin/downloads/list

DefaultDirName={#AimpPluginsDirectoryName}
DefaultGroupName={#AppName}
DisableProgramGroupPage=true
OutputDir=temp_build\Release\distrib\
SetupIconFile=inno_setup_data\icons\icon_aimp3_32x32.ico
Compression=lzma/ultra
SolidCompression=true
InternalCompressLevel=ultra
PrivilegesRequired=admin
DirExistsWarning=no
AllowUNCPath=false
AppendDefaultDirName=true

[Languages]
; Language name is used on Donation page as {language} constant.
Name: en; MessagesFile: inno_setup_data\English.isl; LicenseFile: Lisense-English.txt;
Name: ru; MessagesFile: inno_setup_data\Russian.isl; LicenseFile: Lisense-Russian.txt;

[Files]
Source: "{#SrcApp}"; DestDir: "{app}"; Flags: ignoreversion
Source: "temp_build\Release\htdocs\*"; DestDir: "{code:GetBrowserScriptsDir}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
Source: "inno_setup_data\default_settings.dat"; DestDir: "{code:GetPluginSettingsDir}"; DestName: "settings.dat"; Flags: onlyifdoesntexist; AfterInstall: AfterInstallSettingsFile( ExpandConstant('{code:GetPluginSettingsDir}\settings.dat') )
Source: "3rd_party\FreeImage\{#FreeImage_VERSION}\Dist\FreeImage.dll"; DestDir: "{code:GetFreeImageDLLsDstDir}"; Flags: ignoreversion
Source: "3rd_party\FreeImage\{#FreeImage_VERSION}\Wrapper\FreeImagePlus\dist\FreeImagePlus.dll"; DestDir: "{code:GetFreeImageDLLsDstDir}"; Flags: ignoreversion

[Registry]
; etc.

[Code]
var
  BrowserScriptsDirPage: TInputDirWizardPage;
  AimpVersionSelectionPage: TInputOptionWizardPage;
  NetworkSetupPage: TInputOptionWizardPage;
  AfterInstallPage: TOutputMsgMemoWizardPage;
  AllowNetworkAccess: Boolean;
  SettingsFileDestination: String;
  Port: Integer;

procedure InitDonationPage; forward;
procedure InitializeWizard;
begin
  { Set AIMP player version, used to determine default path of Aimp's Plugins directory. }
  AimpVersionSelectionPage := CreateInputOptionPage(wpLicense,
                                                    ExpandConstant('{cm:AimpVerionSelectionMsg1}'),
                                                    ExpandConstant('{cm:AimpVerionSelectionMsg2}'),
                                                    ExpandConstant('{cm:AimpVerionSelectionMsg3}'),
                                                    True,
                                                    False
                                                    );
  AimpVersionSelectionPage.Add('AIMP2');
  AimpVersionSelectionPage.Add('AIMP3');

  AimpVersionSelectionPage.SelectedValueIndex := 1; { default is AIMP3 }

  BrowserScriptsDirPage := CreateInputDirPage(wpSelectDir,
                                              ExpandConstant('{cm:BrowserScriptsDirSelectionMsg1}'),
                                              ExpandConstant('{cm:BrowserScriptsDirSelectionMsg2}'),
                                              ExpandConstant('{cm:BrowserScriptsDirSelectionMsg3}'),
                                              False,
                                              ''
                                              );
  BrowserScriptsDirPage.Add('');

  InitDonationPage();

  NetworkSetupPage := CreateInputOptionPage(BrowserScriptsDirPage.ID,
                                            ExpandConstant('{cm:NetworkSetupTitle}'),
                                            ExpandConstant('{cm:NetworkSetupDescription}'),
                                            ExpandConstant('{cm:NetworkSetupSubDescription}'),
                                            False, False);

  NetworkSetupPage.Add(ExpandConstant('{cm:NetworkSetupCheckBox}'));

  AfterInstallPage := CreateOutputMsgMemoPage(wpInfoAfter,
   SetupMessage(msgWizardInfoAfter),
   SetupMessage(msgInfoAfterLabel),
   SetupMessage(msgInfoAfterClickLabel),
   '' // memo text will be set later by AfterInstallPage.RichEditViewer.RTFText.
  );

  Port := 3333;
end;

procedure TerminateAimpExecutionIfNeedeed(); forward;
function GetInfoAfterMemoText() : String; forward;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = wpSelectDir then
    begin
    WizardForm.DirEdit.Text := ExpandConstant('{pf}\{code:GetAimpPlayerAppDirName}\{code:AimpPluginsDirName}')

    TerminateAimpExecutionIfNeedeed(); { must do this after WizardForm.DirEdit.Text set. }
    end
  else if CurPageID = BrowserScriptsDirPage.ID then
    begin
    SettingsFileDestination := ExpandConstant('{code:GetPluginSettingsDir}\settings.dat');

    { Set default values, using settings that were stored last time if possible }
    BrowserScriptsDirPage.Values[0] := GetPreviousData( 'BrowserScriptsDir',
                                                         ExpandConstant('{code:GetPluginWorkDir}\htdocs')
                                                       );
    end
  else if CurPageID = wpReady then
     begin
     AllowNetworkAccess := NetworkSetupPage.Values[0];
     AfterInstallPage.RichEditViewer.RTFText := GetInfoAfterMemoText();
     end
end;

function GetBrowserScriptsDir(Param: String): String;
begin
  { Return the selected BrowserScriptsDir }
  Result := BrowserScriptsDirPage.Values[0];
end;

function IsAimp2(): Boolean;
begin
   Result := AimpVersionSelectionPage.SelectedValueIndex = 0;
end;

function GetPluginWorkDir(Param: String): String;
begin
  Result := ExpandConstant('{app}\{#PluginWorkDirectoryName}')
end;

function GetPluginSettingsDir(Param: String): String;
begin
  Result := ExpandConstant('{app}\{#PluginWorkDirectoryName}')
end;

function GetFreeImageDLLsDstDir(Param: String): String;
begin
  Result := ExpandConstant('{app}\{#PluginWorkDirectoryName}')
end;

function AimpPluginsDirName(Param: String): String;
begin
  { Return name of AIMP's Plugins directory. }
  Result := ExpandConstant('{#AimpPluginsDirectoryName}')
  { We do not distinguish between AIMP2/3 Plugins directory names
    since supporting of this difference is hard
    while Windows is case insensitive.
  if IsAimp2() then
    Result := 'PlugIns'
  else
    Result := 'Plugins';
  }
end;

function GetAimpPlayerAppDirName(Param: String): String;
begin
  { Return the name of program directory for selected AIMP player version }
  if IsAimp2() then
    Result := 'AIMP2'
  else
    Result := 'AIMP3';
end;

procedure RegisterPreviousData(PreviousDataKey: Integer);
begin
  SetPreviousData(PreviousDataKey, 'BrowserScriptsDir', BrowserScriptsDirPage.Values[0]);
end;

procedure AfterInstallSettingsFile(Path: String);
var
  // See http://msdn.microsoft.com/en-us/library/ms757878(VS.85).aspx for details about
  XMLDoc : Variant;       // IXMLDOMDocument
  DocRootNodes : Variant; // IXMLDOMNodeList
  DocRootNode : Variant;  // IXMLDOMNode
begin
  //MsgBox('Try to load the XML file: ' + Path, mbInformation, mb_Ok);

  { Load the XML File }
  XMLDoc := CreateOleObject('MSXML2.DOMDocument');
  XMLDoc.async := False;
  XMLDoc.resolveExternals := False;
  XMLDoc.setProperty('SelectionLanguage', 'XPath');
  XMLDoc.load(Path);
  if XMLDoc.parseError.errorCode <> 0 then
    RaiseException('Error in xml file "' + Path + '" on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' + IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason);

  //MsgBox('Loaded the XML file.', mbInformation, mb_Ok);

  { Modify the XML document }
  DocRootNodes := XMLDoc.selectNodes('//httpserver/document_root');
  //MsgBox('Mathing nodes: ' + IntToStr(DocRootNodes.length), mbInformation, mb_Ok);
  DocRootNode := DocRootNodes.item(0);
  DocRootNode.Text := GetBrowserScriptsDir('');

  if (AllowNetworkAccess) then
    XMLDoc.selectNodes('//httpserver/ip_to_bind').item(0).Text := '';

  Port := XMLDoc.selectNodes('//httpserver/port').item(0).Text;

  { Save the XML document }
  XMLDoc.Save(Path);
  //MsgBox('Saved the modified XML as ''' + Path + '''.', mbInformation, mb_Ok);
end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
begin
  { Fill the 'Ready Memo' with the normal settings and the custom settings }
  S := '';
  S := S + MemoDirInfo + NewLine;
  S := S + ExpandConstant('{cm:BrowserScriptsDirDestination}') + NewLine + Space + GetBrowserScriptsDir('') + NewLine;
  S := S + ExpandConstant('{cm:SettingsFileDestination}') + NewLine + Space + SettingsFileDestination + NewLine;
  Result := S;
end;

function GetAimpHWND(): HWND;
begin
  Result := FindWindowByClassName('AIMP2_RemoteInfo');
end;

function IsAimpLaunched(): boolean;
begin
  Result := GetAimpHWND() <> 0;
end;

const
  WM_AIMP_COMMAND = 1024 + $75;
  WM_AIMP_CALLFUNC = 3;
  AIMP_QUIT = 10;
procedure KillAimp();
begin
  SendMessage(GetAimpHWND(), WM_AIMP_COMMAND, WM_AIMP_CALLFUNC, AIMP_QUIT);
end;

procedure TerminateAimpExecutionIfNeedeed();
begin
  if IsAimpLaunched() then
    if MsgBox(ExpandConstant('{cm:AimpApplicationTerminateQuery}'),
          mbConfirmation,
          MB_YESNO) = IDYES then
      KillAimp();
end;

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

function GetBrowserScriptsStringDirectlyFromRegistry(): String;
var
  sUnInstPath: String;
  sBrowserScriptsString: String;
begin
  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  sBrowserScriptsString := '';
  if not RegQueryStringValue(HKLM, sUnInstPath, 'Inno Setup CodeFile: BrowserScriptsDir', sBrowserScriptsString) then
    RegQueryStringValue(HKCU, sUnInstPath, 'Inno Setup CodeFile: BrowserScriptsDir', sBrowserScriptsString);

  Result := sBrowserScriptsString;
end;

function GetBrowserScriptsString(): String;
begin
  Result := GetPreviousData('BrowserScriptsDir', '');
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;

function RemoveInstalledBrowserScripts(): Integer;
var
  sBrowserScriptsDir: String;
begin
// Return Values:
// 1 - Browser scripts dir string is empty
// 2 - error executing the DelTree
// 3 - successfully executed the DelTree

  // default return value
  Result := 0;

  // get the uninstall string of the old app
  sBrowserScriptsDir := GetBrowserScriptsString();
  if sBrowserScriptsDir <> '' then begin
    sBrowserScriptsDir := RemoveQuotes(sBrowserScriptsDir);
    if DelTree(sBrowserScriptsDir, True, True, True) then
      Result := 3
    else
      Result := 2;
  end else
    Result := 1;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep=ssInstall) then
  begin
    if (IsUpgrade()) then
    begin
      // Do not uninstall previous version till it will be posssible remain settings file.
      // Instead old remove browser scripts.
      // UnInstallOldVersion();
      RemoveInstalledBrowserScripts();
    end;
  end;
end;

procedure DonationButtonOnClick(Sender: TObject);
var
  ErrorCode: Integer;
  URL: String;
begin
  URL := 'http://www.a0ivanov.ru/donate_' + ExpandConstant('{language}') + '.html';

  if not ShellExec('', URL, '', '', SW_SHOW, ewNoWait, ErrorCode) then
  begin
    // handle failure if necessary
  end;
end;

procedure InitDonationPage;
var
  Page: TWizardPage;
  Button: TNewButton;
  Label1: TLabel;
begin
  Page := CreateCustomPage(wpInfoAfter, ExpandConstant('{cm:DonatePageTitle}'), ExpandConstant('{cm:DonateMsg}')); // cm:DonatePageTitle2

  Label1 := TLabel.Create(Page);
  Label1.AutoSize := False;
  Label1.Top := ScaleY(15);
  Label1.Left := ScaleX(0);
  Label1.Width := Page.SurfaceWidth;
  Label1.Alignment := taCenter;
  Label1.Caption := ExpandConstant('{cm:DonateMsg}');
  Label1.WordWrap := True;
  Label1.Parent := Page.Surface;

  Button := TNewButton.Create(Page);
  Button.Width := ScaleX(80);
  Button.Height := ScaleY(26);
  Button.Caption := ExpandConstant('{cm:DonateButton}');
  Button.OnClick := @DonationButtonOnClick;
  Button.Parent := Page.Surface;
  Button.Top := Label1.Top + Label1.Height + ScaleY(8);
  Button.Left := Label1.Left + (Label1.Width - Button.Width) / 2;
end;

procedure GetIpAddresses(Addresses : TStringList); forward;
function GetInfoAfterMemoText(): String;
var
  SL : TStringList;
  I: Integer;
  Msg: String;
begin
  if (AllowNetworkAccess) then
     begin
     Msg := ExpandConstant('{cm:InfoAfterPageMemoTextRemoteBegin}');
     SL := TStringList.Create;
     GetIpAddresses(SL);
     For I := 0 to SL.Count - 1 do
       begin
        Msg := Msg + #13#10 + FmtMessage('http://%1:%2/index.htm', [SL.Strings[I], IntToStr(Port)]);
       end;
     SL.Free;
     Msg := Msg + #13#10 + ExpandConstant('{cm:InfoAfterPageMemoTextRemoteEnd}');
     end
   else
     Msg := ExpandConstant('{cm:InfoAfterPageMemoTextLocal}');
   Result := Msg;
end;

const
 ERROR_INSUFFICIENT_BUFFER = 122;

function GetIpAddrTable( pIpAddrTable: Array of Byte;
  var pdwSize: Cardinal; bOrder: WordBool ): DWORD;
external 'GetIpAddrTable@IpHlpApi.dll stdcall';

procedure GetIpAddresses(Addresses : TStringList);
var
 Size : Cardinal;
 Buffer : Array of Byte;
 IpAddr : String;
 AddrCount : Integer;
 I, J : Integer;
begin
  // Find Size
  if GetIpAddrTable(Buffer,Size,False) = ERROR_INSUFFICIENT_BUFFER then
  begin
     // Allocate Buffer with large enough size
     SetLength(Buffer,Size);
     // Get List of IP Addresses into Buffer
     if GetIpAddrTable(Buffer,Size,True) = 0 then
     begin
       // Find out how many addresses will be returned.
       AddrCount := (Buffer[1] * 256) + Buffer[0];
       // Loop through addresses.
       For I := 0 to AddrCount -1 do
       begin
         IpAddr := '';
         // Loop through each byte of the address
         For J := 0 to 3 do
         begin
           if J > 0 then
             IpAddr := IpAddr + '.';
           // Navigagte through record structure to find correct byte of Addr
           IpAddr := IpAddr + IntToStr(Buffer[I*24+J+4]);
         end;
         Addresses.Add(IpAddr);
       end;
     end;
  end;
end;


Corrected syntax.

Corrected syntax.

; You will need to adjust the source paths (i.e. those currently beginning
; 'd:\program files\topographica') to wherever is the copy of Topographica
; that's being used to create the setup file.

[Setup]
AppName=Topographica
AppVerName=Topographica 0.8.3alpha
AppPublisher=Topographica development team
AppPublisherURL=http://www.topographica.org
AppSupportURL=http://www.topographica.org
AppUpdatesURL=http://www.topographica.org
ChangesAssociations=Yes
DefaultDirName={pf}\Topographica
DefaultGroupName=Topographica
LicenseFile=D:\Program Files\topographica\COPYING.txt
InfoBeforeFile=D:\Program Files\topographica\README.txt
OutputBaseFilename=setup
;; set to none when testing or you will be here a long time
Compression=lzma
SolidCompression=yes

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

[Tasks]
Name: desktopicon; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"

[Files]
Source: "D:\Program Files\topographica\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\Topographica"; Filename: "{app}\topographica.bat"; WorkingDir: "{app}"; Parameters: "-g"; IconFilename: "{app}\topographica.ico"
Name: "{group}\Uninstall Topographica"; Filename: "{uninstallexe}"
Name: "{userdesktop}\Topographica"; Filename: "{app}\topographica.bat"; Parameters: "-g"; Tasks: desktopicon; IconFilename: "{app}\topographica.ico"

[Registry]
; ".myp" is the extension we're associating. "MyProgramFile" is the internal name for the file type as stored
; in the registry. Make sure you use a unique name for this so you don't inadvertently overwrite another
; application's registry key.
Root: HKCR; Subkey: ".ty"; ValueType: string; ValueName: ""; ValueData: "TopographicaScript"; Flags: uninsdeletevalue

;"My Program File" is the name for the file type as shown in Explorer.
Root: HKCR; Subkey: "TopographicaScript"; ValueType: string; ValueName: ""; ValueData: "Topographica Script"; Flags: uninsdeletekey

;"DefaultIcon" is the registry key that specifies the filename containing the icon to associate with the file type.
Root: HKCR; Subkey: "TopographicaScript\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\topographica.ico"

;"shell\open\command" is the registry key that specifies the program to execute when a file of the type is double-clicked in Explorer.
;The surrounding quotes are in the command line so it handles long filenames correctly.
Root: HKCR; Subkey: "TopographicaScript\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\topographica.bat"" -g ""%1"""

[Run]
; Run the python script to create 'topographica' python script and topographica.bat
Filename: "{app}\python_topo\python.exe"; Parameters: "setup.py ""{app}"""; WorkingDir: "{app}"; Flags: runhidden

; User gets to choose to run Topographica after installation
Filename: "{app}\topographica.bat"; Parameters: "-g"; Description: "{cm:LaunchProgram,Topographica}"; Flags: shellexec postinstall skipifsilent

[UninstallDelete]
; Files that get created outside this installation script (e.g. by setup.py)
; that we don't want to leave around after uninstallation.
;
; User-created files do not get removed.
; (CEBHACKALERT this includes leaving behind .pyc files.)
Type: filesandordirs; Name: "{app}\python_topo"
Type: files; Name: "{app}\topographica"
Type: files; Name: "{app}\topographica.bat"

; You will need to adjust the source paths (i.e. those currently beginning
; 'd:\program files\topographica') to wherever is the copy of Topographica
; that's being used to create the setup file.

[Setup]
AppName=Topographica
AppVerName=Topographica 0.8.3alpha
AppPublisher=Topographica development team
AppPublisherURL=http://www.topographica.org
AppSupportURL=http://www.topographica.org
AppUpdatesURL=http://www.topographica.org
ChangesAssociations=Yes
DefaultDirName={pf}\Topographica
DefaultGroupName=Topographica
LicenseFile=D:\Program Files\topographica\COPYING.txt
InfoBeforeFile=D:\Program Files\topographica\README.txt
OutputBaseFilename=setup
;; set to none when testing or you will be here a long time
Compression=lzma
SolidCompression=yes

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

[Tasks]
Name: desktopicon; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"

[Files]
Source: "D:\Program Files\topographica\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\Topographica"; Filename: "{app}\topographica.bat"; WorkingDir: "{app}"; Parameters: "-g"; IconFilename: "{app}\topographica.ico"
Name: "{group}\Uninstall Topographica"; Filename: "{uninstallexe}"
Name: "{userdesktop}\Topographica"; Filename: "{app}\topographica.bat"; Parameters: "-g"; Tasks: desktopicon; IconFilename: "{app}\topographica.ico"

[Registry]
; ".myp" is the extension we're associating. "MyProgramFile" is the internal name for the file type as stored
; in the registry. Make sure you use a unique name for this so you don't inadvertently overwrite another
; application's registry key.
Root: HKCR; Subkey: ".ty"; ValueType: string; ValueName: ""; ValueData: "TopographicaScript"; Flags: uninsdeletevalue

;"My Program File" is the name for the file type as shown in Explorer.
Root: HKCR; Subkey: "TopographicaScript"; ValueType: string; ValueName: ""; ValueData: "Topographica Script"; Flags: uninsdeletekey

;"DefaultIcon" is the registry key that specifies the filename containing the icon to associate with the file type.
Root: HKCR; Subkey: "TopographicaScript\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\topographica.ico"

;"shell\open\command" is the registry key that specifies the program to execute when a file of the type is double-clicked in Explorer.
;The surrounding quotes are in the command line so it handles long filenames correctly.
Root: HKCR; Subkey: "TopographicaScript\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\topographica.bat"" -g ""%1"""

[Run]
; Run the python script to create 'topographica' python script and topographica.bat
Filename: "{app}\python_topo\python.exe"; Parameters: """{app}\setup.py"" ""{app}"""; WorkingDir: "{app}"; Flags: runhidden

; User gets to choose to run Topographica after installation
Filename: "{app}\topographica.bat"; Parameters: "-g"; Description: "{cm:LaunchProgram,Topographica}"; Flags: shellexec postinstall skipifsilent

[UninstallDelete]
; Files that get created outside this installation script (e.g. by setup.py)
; that we don't want to leave around after uninstallation.
;
; User-created files do not get removed.
; (CEBHACKALERT this includes leaving behind .pyc files.)
Type: filesandordirs; Name: "{app}\python_topo"
Type: files; Name: "{app}\topographica"
Type: files; Name: "{app}\topographica.bat"