Files
SteamlessController/resources/InnoInstallerScript.iss
T
Dylan Deverill bd4206e891 Find far more games, and let a user name one we missed
The per-game picker drew on two sources: a Start Menu shortcut walk and the
installed MSIX package list. Issue #83 is a user whose app never appeared in
it, asking to just point at an .exe. Two structural gaps sat behind that.

Launcher-installed games were missing or wrong. Epic shortcuts a game with a
.url carrying a com.epicgames.launcher:// URI, and the walk keeps only .url
files pointing at Steam, so Epic games were dropped outright — Among Us was
invisible on the machine this was written on. Battle.net and Ubisoft write
.lnk files targeting the launcher executable with a per-game argument, and
IShellLink::GetPath returns only the executable, so a whole Blizzard library
collapsed into one wrongly-named "Battle.net Launcher.exe" entry.

LauncherGames reads what each launcher records about itself instead: Epic's
.item manifests, GOG's and Ubisoft's registry keys, Battle.net's uninstall
entries. Those name an install directory rather than an executable, which is
the better identity anyway and gets a fourth game-id shape, "dir:". A game
folder holds its launcher, its anti-cheat service and the game itself, and
which of them reaches the foreground varies by title and by moment — the same
reasoning SteamAppLocator already documents for Steam, and MatchProfile
resolves these by longest path prefix for the same reason.

Nothing without an installer entry could be listed at all, which is the long
tail the issue is really about. The picker now offers the applications with a
window open right now, and a file browser, both producing an ordinary exe-path
id that needed no new plumbing. The running list is the more useful of the two:
it asks the user to know nothing about where a game was installed.

Also moves the enumeration off the UI thread. It measured 1.4s warm and 5s
cold before this change added five sources to it, and OpenRemapWindow was
calling it synchronously — the window did not appear until it finished.

Fixes #83
2026-08-31 01:33:18 -07:00

141 lines
6.6 KiB
Plaintext

; 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.19"
#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"
[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: "build\release\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "build\release\Release\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: "build\release\Release\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;