; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! ; Non-commercial use only #define MyAppName "SteamlessController" #define MyAppVersion "1.25" #define ViGEmSetup "ViGEmBus_1.22.0_x64_x86_arm64.exe" #define MyAppPublisher "Dylan Deverill" #define MyAppURL "https://github.com/ddeverill/SteamlessController" #define MyAppExeName "SteamlessController.exe" ; Where the packaged binaries come from, spelled once so the version check ; below and the [Files] entries cannot drift apart. #define BuildDir "build\release\Release\" ; Refuse to build an installer around a stale executable. ; ; The trap this exists for: the Visual Studio generator is multi-config, so ; CMAKE_BUILD_TYPE does nothing and "cmake --build build/release" quietly ; produces DEBUG. The Release directory packaged here keeps whatever was in it ; from the last correct build, which can be weeks old. Setup then stamps ; MyAppVersion on the metadata and installs an executable that reports its own ; older version — an installer that says one thing while Add/Remove Programs, ; the log banner and the features present all say another. ; ; That shipped once, to the #95 reporter, and cost a round trip to work out. ; Nothing about it was visible: setup succeeded, the version in Add/Remove ; Programs was right, and only the app's own log gave it away. ; ; Build it properly with: cmake --build --preset release ; (the preset carries "configuration": "Release"; the bare path form does not) ; Note the "../": [Files] resolves against SourceDir, but the preprocessor ; resolves against this script's own directory. #define ExeVersion GetStringFileInfo("../" + BuildDir + MyAppExeName, "FileVersion") #if ExeVersion != MyAppVersion ; #error takes its line literally and evaluates nothing, so the versions ; are printed by #pragma message, which does. #pragma message "Packaged " + MyAppExeName + " is version " + ExeVersion + " but this script builds " + MyAppVersion #error The packaged executable is stale - see the message above. Run: cmake --build --preset release #endif [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={{4E6FF864-969A-44C6-B4AC-848F22D5EF57} AppName={#MyAppName} AppVersion={#MyAppVersion} ;AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={autopf}\{#MyAppName} UninstallDisplayIcon={app}\{#MyAppExeName} SourceDir=.. ; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run ; on anything but x64 and Windows 11 on Arm. ArchitecturesAllowed=x64compatible ; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the ; install be done in "64-bit mode" on x64 or Windows 11 on Arm, ; meaning it should use the native 64-bit Program Files directory and ; the 64-bit view of the registry. ArchitecturesInstallIn64BitMode=x64compatible DisableProgramGroupPage=yes ; Uncomment the following line to run in non administrative install mode (install for current user only). ;PrivilegesRequired=lowest OutputBaseFilename=SteamlessController-Setup SetupIconFile=resources\SteamControllerON.ico SolidCompression=yes WizardStyle=modern [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: "{#BuildDir}{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion Source: "{#BuildDir}SteamlessDeviceCycle.exe"; DestDir: "{app}"; Flags: ignoreversion ; Setup's driver check, and the thing to ask a user to run when a controller ; never reaches a game. Installed rather than temporary for the second reason. Source: "{#BuildDir}ViGEmBusProbe.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "resources\{#ViGEmSetup}"; DestDir: "{tmp}"; Flags: deleteafterinstall [Icons] Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon [Run] ; ViGEmBus uses MSI-style silent switches; /install is rejected by the 1.22.0 bootstrapper. Filename: "{tmp}\{#ViGEmSetup}"; Parameters: "/qn /norestart"; StatusMsg: "Installing ViGEmBus driver..."; Flags: waituntilterminated ; Register the elevated on-demand device-cycle task while the installer is ; already elevated, so users never see a UAC prompt from the app itself. Filename: "{app}\SteamlessDeviceCycle.exe"; Parameters: "--register"; StatusMsg: "Registering controller helper task..."; Flags: waituntilterminated runhidden Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent [UninstallRun] Filename: "{app}\SteamlessDeviceCycle.exe"; Parameters: "--unregister"; RunOnceId: "RemoveCycleTask"; Flags: waituntilterminated runhidden [Code] // Did ViGEmBus actually install? Established the way the app establishes it at // runtime — by enumerating the ViGEm bus interface — because every cheaper // answer has been measured and found to lie: // // * the bundle exits 0 when it finds a related product already registered // and decides there is nothing for it to do // * the registry records the product whether or not a driver ever landed // * the driver's own version is no guide either: 1.22.0 ships a binary still // stamped 1.21.442.0 // // Reported in #68: with another program's rebranded ViGEm already present, // setup looked entirely successful and left the machine with no working bus. // The user found out when they clicked Enable Steamless Mode. function ViGEmBusPresent(): Boolean; var ResultCode: Integer; begin // Assume the best when the check itself cannot run. A missing or broken // probe is our bug, and telling every user their driver failed on account of // it would be worse than saying nothing at all. Result := True; if not Exec(ExpandConstant('{app}\ViGEmBusProbe.exe'), '', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then begin Log('ViGEmBus verification: probe could not be run'); Exit; end; Log('ViGEmBus verification: probe exit code ' + IntToStr(ResultCode)); Result := ResultCode = 0; end; procedure CurStepChanged(CurStep: TSetupStep); var WarningText: String; begin // ssPostInstall runs after the [Run] entries above, so the driver installer // has finished by the time this fires. Worth stating because it is // load-bearing: were that ordering ever wrong, every healthy install would // warn. That is a loud failure rather than a quiet one, which is why the // check sits here rather than behind a wrapper built to guarantee ordering. if CurStep <> ssPostInstall then Exit; if ViGEmBusPresent() then Exit; WarningText := 'SteamlessController is installed, but the ViGEmBus driver is not.' + #13#10 + #13#10 + 'That driver is what presents your controller to games. Setup ran its ' + 'installer, but no virtual gamepad bus is present afterwards. Usually this ' + 'means another program has already registered a version of it — Oculus and ' + 'Virtual Desktop both ship their own copy — which stops the bundled ' + 'installer from doing anything.' + #13#10 + #13#10 + 'To finish:' + #13#10 + #13#10 + ' 1. Download ViGEmBus from' + #13#10 + ' https://github.com/nefarius/ViGEmBus/releases/latest' + #13#10 + ' 2. Run it, choosing Repair if it offers that rather than a fresh install' + #13#10 + ' 3. Start SteamlessController — it picks the driver up on its own,' + #13#10 + ' with no reinstall and no restart needed' + #13#10 + #13#10 + 'To check again later, run ViGEmBusProbe.exe from the installation folder.'; // An unattended install must not stop on a dialog nobody is there to close. // The Inno log carries the same finding either way, and so will the app's // event log the first time somebody enables Steamless Mode. if WizardSilent() then Log('ViGEmBus verification: no bus present after install (silent, not shown)') else MsgBox(WarningText, mbInformation, MB_OK); end;