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

#define MyAppName "vJoy Device Driver"
#define MyShortAppName "vJoy"
#define MyAppPublisher "Shaul Eizikovich"
#define MyAppURL "http://vjoystick.sourceforge.net"
#define AppGUID "{{8E31F76F-74C3-47F1-9550-E041EEDC5FBB}"

#define vJoyInstx86 ".\objfre_wxp_x86\i386"
#define vJoyInstx64 ".\objfre_wlh_amd64\amd64"
#define vJoyLibx86 "..\lib\objfre_wxp_x86\i386"
#define vJoyLibx64 "..\lib\objfre_wlh_amd64\amd64"
#define vJoyConfDirx86 "..\apps\vJoyConf\Release"
#define vJoyConfDirx64 "..\apps\vJoyConf\x64\Release"
#define vJoyFeederDirx86 "..\apps\vJoyFeeder\Release"
#define vJoyFeederDirx64 "..\apps\vJoyFeeder\x64\Release"
#define vJoyIconFile "..\apps\vJoyInstall\GTK\Joystick-icon.ico"

; Get product version from the driver (vJoy.sys)
#define SrcDrv vJoyInstx64+"\vJoy.sys"
#define FileVerStr GetFileVersion(SrcDrv)
#define StripBuild(str VerStr) Copy(VerStr, 1)
#define DrvVerStr StripBuild(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={code:GetAppId}
AppName={#MyAppName}
;AppVersion={#MyAppVersion}
AppVersion={#DrvVerStr}
VersionInfoVersion={#DrvVerStr}
AppVerName= {#MyAppName} {#DrvVerStr}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
CreateAppDir=true
OutputDir=.
OutputBaseFilename=vJoyInstallerMerged
SetupIconFile=..\apps\vJoyInstall\GTK\install.ico
Compression=lzma/Max
SolidCompression=true
DefaultDirName={pf}\{#MyShortAppName}
DefaultGroupName={#MyShortAppName}
VersionInfoCompany=Shaul Eizikovich
AppCopyright=Copyright (c) 2005-2014 by Shaul Eizikovich
MinVersion = 5.1.2600sp1
SignTool=Sig sign /a /v /s PrivateCertStore  $f
SignedUninstaller=yes
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyMemo=true
DisableFinishedPage=true
PrivilegesRequired=admin
ArchitecturesInstallIn64BitMode=x64
SetupLogging=true
UsePreviousLanguage=no
;[Languages]
;Name: "english"; MessagesFile: "compiler:Default.isl"
;Name: "brazilianportuguese"; MessagesFile: "compiler:Languages\BrazilianPortuguese.isl"
;Name: "hebrew"; MessagesFile: "compiler:Languages\Hebrew.isl"


[Files]
;Driver
Source: "{#vJoyInstx86}\*"; DestDir: "{app}"; Flags: promptifolder; Check: IsX86
Source: "{#vJoyInstx64}\*"; DestDir: "{app}"; Flags: promptifolder; Check: IsX64
; DLLs used by applications
Source: "{#vJoyLibx86}\*.dll"; DestDir: "{app}"; Check: IsX86
Source: "{#vJoyLibx86}\*.dll"; DestDir: "{app}\Feeder"
Source: "{#vJoyLibx64}\*.dll"; DestDir: "{app}"; Check: IsX64
;Configuration application
Source: "{#vJoyConfDirx86}\vJoyConf.exe"; DestDir: "{app}"; Components: Apps\vJoyConf; Check: IsX86
Source: "{#vJoyConfDirx64}\vJoyConf.exe"; DestDir: "{app}"; Components: Apps\vJoyConf; Check: IsX64
;Demo feeder
Source: "{#vJoyFeederDirx86}\vJoyFeeder.exe"; DestDir: "{app}\Feeder"; Components: Apps\vJoyFeeder
Source: "{#vJoyFeederDirx86}\..\LBIndustrialCtrls.dll"; DestDir: "{app}\Feeder"; Components: Apps\vJoyFeeder
;Visual Stidio 2008 distributables
Source: "vcredist_x86.exe"; DestDir: "{app}"; Flags: deleteafterinstall; Components: Apps\vJoyFeeder or Apps\vJoyConf
Source: "vcredist_x64.exe"; DestDir: "{app}"; Flags: deleteafterinstall; Components: Apps\vJoyConf; Check: IsX64

[Icons]
Name: "{group}\Uninstall vJoy"; Filename: "{uninstallexe}"
Name: "{group}\Configure vJoy"; Filename: "{app}\vJoyConf.exe"; Components: Apps\vJoyConf
Name: "{group}\vJoy Feeder (Demo)"; Filename: "{app}\Feeder\vJoyFeeder.exe"; Components: Apps\vJoyFeeder
Name: "{group}\vJoy Home"; Filename: "http://vjoystick.sourceforge.net"; Comment: "vJoy site"; IconFilename:  "{#vJoyIconFile}"
Name: "{group}\vJoy SDK"; Filename: "http://vjoystick.sourceforge.net/redirect_download_vJoy2SDK.php"; Comment: "vJoy SDK"; IconFilename:  "{#vJoyIconFile}"

[Run]
Filename: "{app}\vJoyInstall.exe"; Parameters: "I"; WorkingDir: "{app}"; Flags: waituntilterminated RunHidden; StatusMsg: "Installing vJoy device (May take up to 5 minutes)"; Check: not DelayedRestart
;Install Visual Studio 2008 distributables
Filename: "{app}\vcredist_x86.exe"; Parameters: "/q"; Flags: waituntilterminated skipifdoesntexist runhidden; StatusMsg: "Installing Visual Studio 2008 Distributables (x86)"; Components: Apps\vJoyFeeder
Filename: "{app}\vcredist_x64.exe"; Parameters: "/q"; Flags: waituntilterminated skipifdoesntexist runhidden; StatusMsg: "Installing Visual Studio 2008 Distributables (x64)"; Components: Apps\vJoyConf; Check: IsX64

[UninstallRun]
Filename: {app}\vJoyInstall.exe; Parameters: U; StatusMsg: "Uninstalling vJoy device"; Flags: waituntilterminated RunHidden; WorkingDir: {app}; 

[Types]
Name: "Custom"; Description: "Select companion applications"; Flags: iscustom

[Components]
Name: "Apps"; Description: "Companion Applications"; Types: Custom
Name: "Apps\vJoyFeeder"; Description: "Demo vJoy Feeder application"; Types: Custom; Flags: checkablealone
Name: "Apps\vJoyConf"; Description: "vJoy Configuration application"; Types: Custom; Flags: checkablealone

[Code]
const
	(* Messages *)
		ManuallySetTestMode = 'You cannot install vJoy on this system.'#13#13'To enable the system open a command prompt (DOS) window as Administrator.'#13'In this window type the following line:'#13#13'BCDEDIT -SET TESTSIGNING ON'#13#13'Then Restart your computer.';
		InstallGood					= 'vJoy installed successfully';
		InstallBad					= 'vJoy failed to install';
		TestModeChanged			=	'Would you like to reset your computer back to TestSigning mode OFF?'#13'This will take effect only after you restart your computer';

  (* Constants related to registry *)
    GUID_WINDOWS_BOOTMGR      = '{9DEA862C-5CDD-4E70-ACC1-F32B344D4795}';
    DefaultObjec              = '23000003';
    AllowPrereleaseSignatures = '16000049';
    BCDRoot                   = 'BCD00000000';
    UninstKey 								= 'Software\Microsoft\Windows\CurrentVersion\Uninstall\';
  	ValOrig 									= 'OrigTestMode';


    (* Constants related to two-phase installation *)
    RunOnceName = 'vJoy Setup restart';
    RunOnceKey  = 'Software\Microsoft\Windows\CurrentVersion\RunOnce';
    Ph2Flag			=	'PH2';
    Ph2Param    = ' /'+Ph2Flag+'=1';
    QuitMessageReboot = 'The installer will now set your computer to TestSigning mode. You will need to restart your computer to complete that installation.'#13#13'After restarting your computer, Setup will continue next time an administrator logs in.';
    WaitingForRestart = 'You should now restat your computer.'#13#13'Press OK then restart your computer manually'#13'Press Cancel to cancel installation';
    ErrorRunOnce      = 'Failed to update RunOnce registry entry';
    ErrorSetTestMode  = 'Failed to set computer to TestSigning Mode';
    
    (* Constants related to installation under SPP *)
    SppFlag		= 'SPP';
    SppParam	= ' /'+SppFlag+'=1';
    AppIdFlag	= 'ID';
    AppIdParam	= ' /'+AppIdFlag;

var
		SkipToPh2: 		boolean; (* True is installer resumes installation after Set Test mode & restart*)
		OrigTestMode:	Boolean; (* Value of the original Test Mode *)
		CalledBySpp:	Boolean; (* True is installer called by SPP Installer *)
		DldRestart:		Boolean; (* True if delayed restart requested *)

(* Forward Function declarations - Start *)
procedure CurStepChanged(CurStep: TSetupStep); Forward;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); Forward;
function GetOrigTestMode(): Boolean; Forward;
function InitializeSetup(): Boolean; Forward;
function IsTestMode(): Boolean; Forward;
function IsVjoyInstalled(): Boolean; Forward;
function IsX64: Boolean; Forward;
function IsX86: Boolean; Forward;
function PrepareToInstall(var NeedsRestart: Boolean): String; Forward;
function ShouldSkipPage(PageID: Integer): Boolean; Forward;
function SetTestMode(value: Boolean): Boolean; Forward;
function SetTestModeOff(): Boolean; Forward;
function SetTestModeOn(): Boolean; Forward;
function GetAppId(Param: String): String; Forward;
function DelayedRestart(): Boolean; Forward;
(* Forward Function declarations - End *)

(* Helper Functions *)
function IsX64: Boolean;
begin
  Result := ProcessorArchitecture = paX64;
end;

function IsX86: Boolean;
begin
  Result := ProcessorArchitecture = paX86;
end;

// Test if vJoy is installed
function IsVjoyInstalled(): Boolean;
var
  Name, RegValVjoy, Path: String;
  Len: Longint;
  Res: Boolean;

begin
  RegValVjoy := 'SYSTEM\CurrentControlSet\Enum\Root\HIDCLASS\0000';
  Name := 'Service';
  Res := RegQueryStringValue(HKEY_LOCAL_MACHINE, RegValVjoy, Name, Path);
  if Res then Len := Length(Path) else Len := 0;
  if (Len > 0) then Result := true  else    Result := false;
end;

(*
  Check if computer in testsigning mode
  How: 
  Go to the BCD in the registry and look for the Boot manager entry
  Inside, get the GUID of the defoalt loader.
  Inside the default loader, get the value of the testsigning mode
  Note: If does not exist = testsigning is off
  Return:
  True if testsigning is ON
  False if testsigning is OFF
*)
function IsTestMode(): Boolean;
var
  RegValDeflt, RegValTestsig, Name, Path, msg : String;
  tmp: AnsiString;
  Res: Boolean;
begin
  Log('IsTestMode: Start');

  RegValDeflt := BCDRoot + '\Objects\' + GUID_WINDOWS_BOOTMGR + '\Elements\' + DefaultObjec;
  Name := 'Element'
  
  // Get pointer to default loader
  Res := RegQueryStringValue(HKEY_LOCAL_MACHINE, RegValDeflt, Name, Path);
  if not Res then
    begin
    (* Debug start  *)
      msg := 'IsTestMode: Cannot find value for ' + RegValDeflt + '\' + Name;
      Log(msg);
     (*Debug end*)
      Result := False;
      exit;
    end; 
  (* Debug start *)
   msg := 'IsTestMode: Got value for ' + RegValDeflt + '\' + Name +': ' + Path;
   Log(msg);
  (*  Debug end*)
   
  // Get testsigning value
  RegValTestsig := BCDRoot + '\Objects\' + Path + '\Elements\' + AllowPrereleaseSignatures;
  tmp := '#0';
  Res := RegQueryBinaryValue(HKEY_LOCAL_MACHINE, RegValTestsig, Name, tmp);
 (* Debug start   *)
 if Res then
 begin
  if tmp <> #0 then
   begin
   msg := 'IsTestMode: RegQueryBinaryValue for ' + RegValTestsig + '\' + NAME + ': Test mode ON ('+ tmp +')';
   Log(msg);
   end
  else
   begin
   msg := 'IsTestMode: RegQueryBinaryValue for ' + RegValTestsig + '\' + NAME + ': Test mode OFF ('+ tmp +')';
   Log(msg);
   end;
  end
 else
  begin
  msg := 'IsTestMode: RegQueryBinaryValue for ' + RegValTestsig + '\' + NAME + ': failed';
  Log(msg);
  end;
 
(*  Debug end *)
  if tmp = #1 then
    Result := True
  else
    Result := False;
end; // End Function IsTestMode

(*
	InitializeSetup() is an event function that is 
	Called during Setup's initialization.
	Setup aborted if function returns False. 
	
	In this case:
  Always Returns True (never abort installation)
  
  Test command-line parameters
  If parameter PH2 exists then go directly to PrepareToInstall() (set SkipToPh2)
  
  If this is a call made while the computer is waiting for manual restart
  (RunOnce parameter is set) then reset status (Delete RunOnce+Reset Test mode) and continue.
*)
function InitializeSetup(): Boolean;

begin

	//MsgBox('Inside InitializeSetup()' , mbInformation, MB_OK)
	Log('InitializeSetup()');
  // Command-line parameters 
  SkipToPh2 := ExpandConstant('{param:'+Ph2Flag+'|0}') = '1';
  
  // Get Original Test Mode value
  OrigTestMode := GetOrigTestMode();
  
  CalledBySpp := ExpandConstant('{param:'+SppFlag+'}') = '1';
  
  // Waiting for manual restart?
  if (not SkipToPh2) and RegValueExists(HKLM, RunOnceKey, RunOnceName) then
  begin	// Waiting
  	Log('InitializeSetup(): Delete RunOnce+Reset Test mode');
		RegDeleteValue(HKLM, RunOnceKey, RunOnceName);
    SetTestModeOff;  	
  end;	// Waiting
  
  Result := True; 
end;

(* Pre & Post-install operations *)
procedure CurStepChanged(CurStep: TSetupStep);
begin
//	if  CurStep=ssInstall then
//		MsgBox('CurStepChanged(ssInstall)' , mbInformation, MB_OK);	
//	if  CurStep=ssPostInstall then
//		MsgBox('CurStepChanged(ssPostInstall)' , mbInformation, MB_OK);
//	if  CurStep=ssDone then
//		MsgBox('CurStepChanged(ssDone)' , mbInformation, MB_OK);
	if  (CurStep=ssPostInstall) and (not CalledBySpp) then
	begin // Post install actions - check if vJoy is now installed
		if IsVjoyInstalled then
			MsgBox(InstallGood , mbInformation, MB_OK)
		else
			MsgBox(InstallBad , mbError, MB_OK)
	end; // Post install actions
end;

(* Pre & Post-uninstall operations *)
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
	if CurUninstallStep = usPostUninstall then
		DelTree( ExpandConstant('{app}'), True, True, True);
end;

(* 
    Called by the installer just before installation
    In phase 2 it does nothing.
    
    If must set test mode (x64 machine that is not in test mode) then:
      Set  NeedsRestart to true
      Set Test mode
      Create RunOnce entry
      Return "Restart?" dialog box
    Else
      Set  NeedsRestart to false
*)
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  RunOnceData: String;
  
begin
// Default
NeedsRestart := False;
DldRestart := False;


// Test if two-phase installation needed
	Log('Test if two-phase installation needed');
  if (not (ProcessorArchitecture = paX64)) or (IsTestMode) or SkipToPh2 then
  begin
   Log('No NeedsRestart - exit PrepareToInstall()');
   NeedsRestart := False;
   exit;
  end;
 
 // Yes.
 /// Change BCD to test mode
 Log('Set Test Mode');
 //MsgBox('Set Test Mode' , mbInformation, MB_OK)
 
 if not SetTestMode(true) then
  begin // Failed
   Log('Set Test Mode - Failed');
   NeedsRestart := false;
   Result := ErrorSetTestMode;
   exit;
 end;

 if CalledBySpp then 
 begin
  Log('PrepareToInstall: Set Delayed Restart');
 	DldRestart := true;
 	 NeedsRestart := true;
	 Result := QuitMessageReboot;
 	exit;
 end; 

 /// set RunOnce registry entry
 Log('set RunOnce registry entry');
 RunOnceData := ExpandConstant('{srcexe}') + Ph2Param;
 Log('RunOnceName := ' + RunOnceName + '; RunOnceKey := ' + RunOnceKey + '; RunOnceData := ' + RunOnceData);
 if  not RegWriteStringValue(HKLM, RunOnceKey, RunOnceName, RunOnceData) then
 begin // Failed
 	 Log('RegWriteStringValue Failed');
   NeedsRestart := false;
   Result := ErrorRunOnce;
   exit;
 end;
 
// OK
 Log('PrepareToInstall going out successfully');
 Result := QuitMessageReboot;
 NeedsRestart := true;
end;

(*
  Called before every wizard page.
  Pages skipped if installer called with parameter PH2 
*)
function ShouldSkipPage(PageID: Integer): Boolean;
begin
  Result := SkipToPh2;
end;

(*
  Set Testsigning mode On/Off acording to value of variable 'value'
  Executed only for x64 - else NOP (return FALSE)
  Return TRUE is succeeded
  Return FALSE if failed or if already was in the required state
*)
function SetTestMode(value: Boolean): Boolean;
var
  ResultCode: Integer;
  params: String;
  
Begin
   
  if not ProcessorArchitecture = paX64 then
  begin
   Result := false;
   //exit;
  end; // Not x64
  
  if (IsTestMode = value) then
  begin
   Result := false;
   exit;
  end; //  already was in the required state
   
   // Execute BCDEdit shell command 
   if value then
    Params := ' -set TESTSIGNING ON'
   else
    Params := ' -set TESTSIGNING OFF';
       
   Exec('Bcdedit.exe',Params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
   Result := True;
end; // End Function SetTestMode

function SetTestModeOn(): Boolean;
begin
  result := SetTestMode(True);
end;

function SetTestModeOff(): Boolean;
begin
  result := SetTestMode(False);
end;

(*
  Record the original value of TestSigning in the registry for usage by uninstall
  Do NOT override original value
  Assuming that is called by the function that actually perform the operation so no testing.
  Set boolean Value:  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{XXX}_is1\OrigTestMode
  {XXX} stands for the AppID
*)
Procedure RegWriteOrigTestMode(TestModeSet: Boolean);
var
	UninstKeyVjoy: String;

Begin  
  	
  UninstKeyVjoy := UninstKey + GetAppId('') + '_is1';

	Log('RegWriteOrigTestMode(): UninstKeyVjoy='+UninstKeyVjoy+'\'+ValOrig);
  // Test if Value exists
  if not RegValueExists(HKEY_LOCAL_MACHINE, UninstKeyVjoy, ValOrig) then
  begin // Set the Value because it does not exist
  	Log('Set the Value because it does not exist');
  	if TestModeSet then
    	RegWriteBinaryValue(HKEY_LOCAL_MACHINE, UninstKeyVjoy, ValOrig, #1)
  	else
    	RegWriteBinaryValue(HKEY_LOCAL_MACHINE, UninstKeyVjoy, ValOrig, #0);
  end; // Set the key because it does not exist
End;

(* 
    This is called just on the way out of the wizard 
    The app Uninstall registry entry already exists
*)
procedure DeinitializeSetup();
begin
  (* 
  	Write the original value of Test Mode to the Registry
  	If this is the second step it must be '0' 
  	Otherwise we don't care.
  *)
  //MsgBox('Inside DeinitializeSetup()' , mbInformation, MB_OK) 
	  RegWriteOrigTestMode(OrigTestMode);
	  
end;


(*
	Get the original value of Test Mode - call it on InitializeSetup()
	
	There are 4 cases:
	1. Check if this is Phase2. If it is then the original value was False.
	2. If this is not Phase2, this might be repetition of the first phase: 
	   The RunOnce registry entry is set to /PH2=1 so the original value is False.
	3. This can be a new installation over an already installed vJoy:
	   check the value in the registry (inside the appropriate Uninstall key). 
	4. If this value does not exist check the current test mode.
	
  Keep the value for use in the ssPostInstall step.	
*)
function GetOrigTestMode(): Boolean;
var
	data: String;
	UninstKeyVjoy: String;
	Adata: AnsiString;
	
begin
	(* Case 1 *)
	if ExpandConstant('{param:'+Ph2Flag+'|0}') = '1' then
		begin	// Case 1
			Log('GetOrigTestMode(): Case 1');
			Result := false;
			exit;
		end;	// Case 1
	
	(* Case 2 *)
	if RegQueryStringValue(HKLM, RunOnceKey, RunOnceName, data) then
		begin // Case 1/2
			Log('GetOrigTestMode(): RunOnce String is: '+ data);
			if Pos(Ph2Param,data) > 0  then 
			begin	// Found PH2
				Log('GetOrigTestMode(): "PH2" was found in RunOnce key');
				Result := false;
				exit;
			end		// Found PH2
		end;	// Case 1/2
		
	(* Case 3 *)
  UninstKeyVjoy := UninstKey + GetAppId('')  + '_is1';
  if RegQueryBinaryValue(HKEY_LOCAL_MACHINE, UninstKeyVjoy, ValOrig, Adata) then
  	begin // Case 3
  	if Adata = #1 then 
  		Result := true
  	else
  		Result := false;
  		Log('GetOrigTestMode(): Found entry in '+ UninstKeyVjoy);
  	exit;
  	end;	// Case 3
  	
  	(* Case 4 *)
		Log('GetOrigTestMode(): call IsTestMode()'); 
  	Result := IsTestMode;

end;

(*
	Called when starting uninstall
	
	Check if need to Revert Test Mode to OFF
*)
function InitializeUninstall(): Boolean;
var
	UninstKeyVjoy: String;
	Adata: AnsiString;
	
begin
  Result := true				// Default
  OrigTestMode := true; // Default
	UninstKeyVjoy := UninstKey + GetAppId('') + '_is1';

  if RegQueryBinaryValue(HKEY_LOCAL_MACHINE, UninstKeyVjoy, ValOrig, Adata) then
  	begin 
  		Log('InitializeUninstall(): Found entry in '+ UninstKeyVjoy);
  		if Adata = #0 then 
  			OrigTestMode := false; 		
  	end;	
end;

(*
	Need restart if the user choses to reset Test Mode
*)
function UninstallNeedRestart(): Boolean;
var
	ResetTestMode: Integer;
	
begin
	Result := False; // Defaul
	
	if (OrigTestMode = false) and  IsTestMode then
	begin // Test Mode Changed
		ResetTestMode := MsgBox(TestModeChanged, mbConfirmation, MB_YESNO);
		if ResetTestMode = IDYES then
		begin	// Reset Test Mode
			SetTestModeOff();
			result := True;
		end;	// Reset Test Mode
	end;  // Test Mode Changed
end;

(*
  Get AppID from command line parameter ID (/ID=ABC)
  If this parameter is missing use AppGUID constant
*)
function GetAppId(Param: String): String;	
begin
 Result := ExpandConstant('{param:'+AppIdFlag+'}');
  if Length(Result)=0 then
    Result :=  expandconstant('{#AppGUID}');;

end;

function DelayedRestart(): Boolean;
begin
	Result:= DldRestart;
end;


(*
			If delayed restart needed then change exit value
*)
function GetCustomSetupExitCode: Integer;
begin
	if DldRestart then Result := 8;
end;
