diff --git a/DS4Windows/App.xaml.cs b/DS4Windows/App.xaml.cs index dacfcfb5..7c88e336 100644 --- a/DS4Windows/App.xaml.cs +++ b/DS4Windows/App.xaml.cs @@ -582,7 +582,7 @@ bool Probe(int timeoutMilliseconds, out string lastProbeFailure) { if (!portable.InspectOwnedProcess(out bool running, out string failure) || !running) throw new DS4Windows.PortableBrokerStartupException(failure ?? - "The portable VIIPER process stopped before it was ready. Check that USB/IP 0.9.7.7 is installed and available, then restart DS4Windows."); + "The portable VIIPER process stopped before it was ready. Check that USB/IP 0.9.8.1 is installed and available, then restart DS4Windows."); return DS4Windows.ViiperSetupManager.ProbeServer( DS4Windows.ViiperSetupManager.ApiHost, diff --git a/DS4Windows/DS4Control/Viiper/ViiperOutDevice.cs b/DS4Windows/DS4Control/Viiper/ViiperOutDevice.cs index 01294515..6c742435 100644 --- a/DS4Windows/DS4Control/Viiper/ViiperOutDevice.cs +++ b/DS4Windows/DS4Control/Viiper/ViiperOutDevice.cs @@ -11705,7 +11705,7 @@ internal static bool IsDs4WindowsOwnedLocalPort( return false; } - // usbip-win2 0.9.7.7 has no attach-time serial override. Its + // The published usbip-win2 0.9.8.1 serial override is broken. Our // safe ownership identity is the exact VIIPER-only localhost // server port plus the remote bus/device tuple. If a newer port // listing does expose a serial, reject every foreign token. @@ -11791,12 +11791,24 @@ private static bool TryParseSerial(string block, out string serial) continue; } + // 0.9.8.1 prints "-> serial: value", including an empty value + // when no override is supplied. Keep the older quoted form + // for upgrade cleanup, and never treat a foreign plain value + // as an absent serial. + const string plainPrefix = "-> serial:"; + if (line.StartsWith(plainPrefix, StringComparison.OrdinalIgnoreCase)) + { + serial = line.Substring(plainPrefix.Length).Trim(); + return serial.Length > 0; + } + int openingQuote = line.IndexOf('\''); int closingQuote = openingQuote >= 0 ? line.IndexOf('\'', openingQuote + 1) : -1; if (openingQuote < 0 || closingQuote <= openingQuote + 1) { - return false; + serial = line.Substring("-> serial".Length).Trim(); + return serial.Length > 0 && serial != "''"; } serial = line.Substring(openingQuote + 1, diff --git a/DS4Windows/DS4Control/Viiper/ViiperSetupManager.cs b/DS4Windows/DS4Control/Viiper/ViiperSetupManager.cs index 50946cd0..3720a0d2 100644 --- a/DS4Windows/DS4Control/Viiper/ViiperSetupManager.cs +++ b/DS4Windows/DS4Control/Viiper/ViiperSetupManager.cs @@ -106,8 +106,8 @@ public string DisplayText if (!UsbipInstalled) { return string.IsNullOrWhiteSpace(UsbipVersion) - ? "usbip-win2 0.9.7.7 is missing" - : $"usbip-win2 {UsbipVersion} must be replaced with supported 0.9.7.7"; + ? "usbip-win2 0.9.8.1 is missing" + : $"usbip-win2 {UsbipVersion} must be replaced with supported 0.9.8.1"; } if (!UsbipExecutableSafe) @@ -156,7 +156,7 @@ public static class ViiperSetupManager { public const string ApiHost = "127.0.0.1"; public const int ApiPort = 3242; - internal static readonly Version RequiredUsbipVersion = new Version(0, 9, 7, 7); + internal static readonly Version RequiredUsbipVersion = new Version(0, 9, 8, 1); private const string InstallerScriptName = "install-viiper-backend.ps1"; private const string InstallerHostArgument = @@ -169,7 +169,7 @@ public static class ViiperSetupManager BundledViiperName + ".sha256"; internal const string SupportedViiperSha256 = "9392A49E619E1D9B7956EA4BEDAFD2E74CBF5065C011554E80F756D1524F892B"; - private const string BundledUsbipName = "USBip-0.9.7.7-x64.exe"; + private const string BundledUsbipName = "USBip-0.9.8.1-x64.exe"; private const string BundledHidHideName = "HidHide_1.5.230_x64.exe"; private const string BundledFakerInputName = @@ -198,11 +198,13 @@ public static class ViiperSetupManager private const string UsbipUdeServiceName = "usbip2_ude"; private const string UsbipFilterServiceName = "usbip2_filter"; internal const string SupportedUsbipUdeSha256 = - "51DB440065393E588A6B2585508C50EB3E1510B7B06D9AFA6C5BDE583751EA7D"; + "ABD4FC43DCE40E027E4ACC884CBFE92433E026206F6565C5EE5284A39441169A"; internal const string SupportedUsbipFilterSha256 = - "C290299FF4D0F6A597DB5CE03E15B29A5349CDCE7C587EBFBD9ECAECA04F73ED"; + "B93A5FC3C23D56F9B49634742374E32F269E53F764CAA2D5FCB43E3259F7ECED"; internal const string SupportedUsbipExecutableSha256 = - "FC1660E3759D8AF4CEDE48DBE194285A5A1DE85CE6E3216724499AFD32BE92E8"; + "817DA219FA153B4B37C3BC1AA8FCC63F33E046188C57CC3DD2C60A6EF0883C56"; + internal const string SupportedUsbipLibrarySha256 = + "F123EF5D288B817A890CBE39A3C2D212B3D8B1765C691C8703CCB76C7337986C"; private static readonly object serverStartLock = new object(); private static readonly object foreignViiperProcessLock = new object(); private static readonly ViiperDependencyReadiness dependencyReadiness = new(() => @@ -281,7 +283,9 @@ private static ViiperPrerequisiteStatus GetStatusCore(bool tryStartServer, bool Version usbipVersion = TryGetUsbipVersion(usbipPath); bool usbipInstalled = IsSupportedUsbipVersion(usbipVersion); bool usbipExecutableSafe = usbipInstalled && - FileHasSha256(usbipPath, SupportedUsbipExecutableSha256); + FileHasSha256(usbipPath, SupportedUsbipExecutableSha256) && + FileHasSha256(Path.Combine(Path.GetDirectoryName(usbipPath), + "libusbip.dll"), SupportedUsbipLibrarySha256); bool usbipRuntimeReady = false; string usbipProbeMessage; ViiperDependencyStatus dependencies = dependencyReadiness.Read(refreshDependencies || tryStartServer); @@ -304,7 +308,7 @@ private static ViiperPrerequisiteStatus GetStatusCore(bool tryStartServer, bool else { usbipProbeMessage = !usbipExecutableSafe && usbipInstalled - ? "The installed usbip.exe does not match the bundled 0.9.7.7 executable." + ? "The installed usbip.exe or libusbip.dll does not match the bundled 0.9.8.1 package." : !usbipDriverFilesSafe && usbipInstalled ? usbipDriverIntegrityMessage @@ -2611,7 +2615,7 @@ private static (bool Safe, string Message) } return (true, - "The registered usbip-win2 0.9.7.7 driver files match the " + + "The registered usbip-win2 0.9.8.1 driver files match the " + "verified signed package."); } catch (Exception ex) diff --git a/DS4Windows/DS4Forms/ViiperSetupProgress.xaml b/DS4Windows/DS4Forms/ViiperSetupProgress.xaml index d77effaf..47002d5b 100644 --- a/DS4Windows/DS4Forms/ViiperSetupProgress.xaml +++ b/DS4Windows/DS4Forms/ViiperSetupProgress.xaml @@ -83,11 +83,11 @@ - - + diff --git a/DS4Windows/DS4Forms/ViiperSetupPrompt.xaml.cs b/DS4Windows/DS4Forms/ViiperSetupPrompt.xaml.cs index 25cb620f..47faf21c 100644 --- a/DS4Windows/DS4Forms/ViiperSetupPrompt.xaml.cs +++ b/DS4Windows/DS4Forms/ViiperSetupPrompt.xaml.cs @@ -66,9 +66,9 @@ public ViiperSetupPrompt(string currentStatus, : "The installed VIIPER does not match this DS4Windows package."; requirementsHeadingText.Text = "Verified update required"; requirementsText.Text = usbipReplacementRequired - ? "• Install and verify bundled VIIPER 0.1.9-rc4.6.6\n" + + ? "• Install and verify bundled VIIPER backend\n" + "• Safely remove the unsupported USB-IP package\n" + - "• Restart, then finish installing USB-IP 0.9.7.7" + "• Restart, then finish installing USB-IP 0.9.8.1" : "• Install the exact bundled VIIPER build\n" + "• Keep your existing installation location\n" + "• The unverified backend will not be started"; @@ -88,8 +88,8 @@ public ViiperSetupPrompt(string currentStatus, summaryText.Text = currentStatus; requirementsHeadingText.Text = "Required before DS4Windows can run"; requirementsText.Text = - "• Install the bundled VIIPER 0.1.9-rc4.6.6 build\n" + - "• Install and verify USB-IP 0.9.7.7\n" + + "• Install the bundled VIIPER backend build\n" + + "• Install and verify USB-IP 0.9.8.1\n" + "• Start DS4Windows only after the runtime probe passes"; installButton.Content = "Install / Repair"; installPortableButton.Content = "Keep DS4Windows portable"; diff --git a/DS4Windows/DS4WinWPF.csproj b/DS4Windows/DS4WinWPF.csproj index f7db86b5..8a1a5554 100644 --- a/DS4Windows/DS4WinWPF.csproj +++ b/DS4Windows/DS4WinWPF.csproj @@ -189,13 +189,13 @@ PreserveNewest PreserveNewest - - extras\USBip-0.9.7.7-x64.exe + + extras\USBip-0.9.8.1-x64.exe PreserveNewest PreserveNewest - - extras\USBip-0.9.7.7-LICENSE.txt + + extras\USBip-0.9.8.1-LICENSE.txt PreserveNewest PreserveNewest diff --git a/DS4Windows/PortableBrokerContext.cs b/DS4Windows/PortableBrokerContext.cs index 08099238..68eb7699 100644 --- a/DS4Windows/PortableBrokerContext.cs +++ b/DS4Windows/PortableBrokerContext.cs @@ -66,7 +66,7 @@ internal static string DescribeReadinessFailure(string probeFailure) } } return "Portable VIIPER did not become ready.\n\nReadiness check: " + detail + - "\n\nInclude this check in your bug report. Use a complete extracted portable package and USB/IP 0.9.7.7. " + + "\n\nInclude this check in your bug report. Use a complete extracted portable package and USB/IP 0.9.8.1. " + "Use Install / Repair VIIPER in Settings to retry without closing DS4Windows. If a conflicting broker cannot be identified or accessed, close it manually first. Your key and profiles were not replaced."; } diff --git a/DS4Windows/PortableBrokerStartupDiagnostics.cs b/DS4Windows/PortableBrokerStartupDiagnostics.cs index e53664e4..eb3906c5 100644 --- a/DS4Windows/PortableBrokerStartupDiagnostics.cs +++ b/DS4Windows/PortableBrokerStartupDiagnostics.cs @@ -9,7 +9,7 @@ internal static string DescribeExit(int? code) string reason = code switch { 70 => "USB/IP could not be found or its version could not be checked.", - 71 => "The installed USB/IP version does not match the required version (0.9.7.7).", + 71 => "The installed USB/IP version does not match the required version (0.9.8.1).", 72 => "The USB/IP startup check timed out before VIIPER could open its connection.", 73 => "The USB/IP driver did not pass its compatibility check.", 74 => "VIIPER could not open or create its local connection key.", diff --git a/DS4WindowsTests/InstallerBackendIdentityTests.cs b/DS4WindowsTests/InstallerBackendIdentityTests.cs new file mode 100644 index 00000000..f97f4d83 --- /dev/null +++ b/DS4WindowsTests/InstallerBackendIdentityTests.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text.RegularExpressions; +using DS4Windows; + +namespace DS4WindowsTests; + +[TestClass] +public class InstallerBackendIdentityTests +{ + [TestMethod] + public void BootstrapperIdentityMatchesTheActualBundledBackend() + { + string binary = Path.Combine(Root(), "extras", ViiperSetupManager.BundledViiperName); + using var stream = File.OpenRead(binary); + string digest = Convert.ToHexString(SHA256.HashData(stream)); + string probe = Read("installer/DS4Windows.Bootstrapper/InfrastructureProbe.cs"); + Assert.AreEqual(digest, StringValue(probe, "ExpectedViiperHash")); + Assert.AreEqual(digest, ViiperSetupManager.SupportedViiperSha256); + Assert.AreEqual(FileVersionInfo.GetVersionInfo(binary).ProductVersion, + StringValue(probe, "ExpectedViiperVersion")); + } + + [TestMethod] + public void InfrastructureDetectionUsesTheSamePackageMarkerAtEveryBoundary() + { + string marker = $"VIIPER-{ViiperSetupManager.SupportedViiperReleaseTag.TrimStart('v')}+USBIP-{ViiperSetupManager.RequiredUsbipVersion}"; + Assert.AreEqual(marker, StringValue(Read("installer/DS4Windows.Bootstrapper/InfrastructureProbe.cs"), "ExpectedMarker")); + Assert.AreEqual(marker, StringValue(Read("installer/DS4Windows.SetupActions/Program.cs"), "InfrastructureVersion")); + Assert.AreEqual(marker, StringValue(Read("extras/install-viiper-backend.ps1"), "$script:InfrastructureVersion")); + StringAssert.Contains(Read("installer/DS4Windows.Bundle/Bundle.wxs"), + $"InfrastructureInstalled = "{marker}""); + } + + private static string StringValue(string source, string name) + { + Match match = Regex.Match(source, Regex.Escape(name) + "\\s*=\\s*\"([^\"]+)\""); + Assert.IsTrue(match.Success, $"Missing package identity: {name}"); + return match.Groups[1].Value; + } + + private static string Read(string file) => File.ReadAllText(Path.Combine(Root(), file)); + private static string Root([CallerFilePath] string source = "") => + Path.GetFullPath(Path.Combine(Path.GetDirectoryName(source)!, "..")); +} diff --git a/DS4WindowsTests/ViiperSetupManagerTests.cs b/DS4WindowsTests/ViiperSetupManagerTests.cs index 9ef548e2..3d4c63c5 100644 --- a/DS4WindowsTests/ViiperSetupManagerTests.cs +++ b/DS4WindowsTests/ViiperSetupManagerTests.cs @@ -64,8 +64,10 @@ public void UsbipVersionRequiresPinnedSafe0977() Assert.IsFalse(ViiperSetupManager.IsSupportedUsbipVersion(null)); Assert.IsFalse(ViiperSetupManager.IsSupportedUsbipVersion( new Version(0, 9, 7, 6))); - Assert.IsTrue(ViiperSetupManager.IsSupportedUsbipVersion( + Assert.IsFalse(ViiperSetupManager.IsSupportedUsbipVersion( new Version(0, 9, 7, 7))); + Assert.IsTrue(ViiperSetupManager.IsSupportedUsbipVersion( + new Version(0, 9, 8, 1))); Assert.IsFalse(ViiperSetupManager.IsSupportedUsbipVersion( new Version(0, 9, 7, 8))); Assert.IsFalse(ViiperSetupManager.IsSupportedUsbipVersion( @@ -346,7 +348,7 @@ public void UnsupportedUsbipVersionForcesGuidedRepair() Assert.IsTrue(ViiperSetupManager. RequiresVerifiedViiperUpdate(status)); - status.UsbipVersion = "0.9.7.7"; + status.UsbipVersion = "0.9.8.1"; status.UsbipInstalled = true; status.UsbipExecutableSafe = true; status.UsbipDriverFilesSafe = true; @@ -360,7 +362,7 @@ public void UnsafeUsbipRuntimeAndCitrixBypassPromptSuppression() { ViiperPrerequisiteStatus status = new ViiperPrerequisiteStatus { - UsbipVersion = "0.9.7.7", + UsbipVersion = "0.9.8.1", UsbipInstalled = true, UsbipExecutableSafe = true, UsbipDriverFilesSafe = false, diff --git a/DS4WindowsTests/ViiperUsbipPortManagerTests.cs b/DS4WindowsTests/ViiperUsbipPortManagerTests.cs index a6dedb32..4516851c 100644 --- a/DS4WindowsTests/ViiperUsbipPortManagerTests.cs +++ b/DS4WindowsTests/ViiperUsbipPortManagerTests.cs @@ -7,6 +7,30 @@ namespace DS4WindowsTests [TestClass] public class ViiperUsbipPortManagerTests { + [DataTestMethod] + [DataRow("", true)] + [DataRow("DS4W0123456789A", true)] + [DataRow("foreign", false)] + [DataRow("DS4WIN1234567", false)] + [DataRow("'foreign'", false)] + public void Usbip0981PortListingPreservesExactLocalOwnership(string serial, bool expected) + { + // Official 0.9.8.1 userspace/usbip/port.cpp output, including the + // new plain serial and receive-mode lines. + string output = "Imported USB devices\n====================\n" + + "Port 07: device in use at high-speed\n" + + " Sony Corp. DualSense\n" + + " -> usbip://localhost:3241/17-4\n" + + " -> remote bus/dev: 017/004\n" + + $" -> serial: {serial}\n" + + " -> mode: low-latency\n"; + var ports = ViiperUsbipPortManager.ParseImportedPorts(output); + Assert.AreEqual(1, ports.Count); + Assert.AreEqual(7, ports[0].Port); + Assert.AreEqual(expected, ViiperUsbipPortManager.IsDs4WindowsOwnedLocalPort(ports[0], "17-4")); + Assert.IsFalse(ViiperUsbipPortManager.IsDs4WindowsOwnedLocalPort(ports[0], "17-5")); + } + [DataTestMethod] [DataRow("localhost:3241/1-1", false)] [DataRow("127.0.0.1:3241/1-1", false)] diff --git a/docs/usbip-upgrade.md b/docs/usbip-upgrade.md new file mode 100644 index 00000000..48e80596 --- /dev/null +++ b/docs/usbip-upgrade.md @@ -0,0 +1,21 @@ +# USB/IP 0.9.8.1 integration (draft) + +This contribution updates the required USB/IP version, vendor installer and +file identities, userspace/driver ABI probes, serial parsing and the +uninstall/restart/install boundary. It depends on the VIIPER 0.9.8.1 ABI port. + +## Required before merging or publishing + +The bundled VIIPER backend remains the existing upstream package while this +change is reviewed. It must be replaced by the maintainer-approved build of +the matching VIIPER contribution, with source/provenance/digest records updated +as one package. The existing package must not be advertised as compatible +with the new driver. CI packages from this draft are not a hardware-test release. + +Upstream release tags, signing policy, updater destinations and trusted +repository identities are unchanged. No fork-produced VIIPER executable is +substituted for the approved upstream dependency. + +Driver replacement still requires a user-controlled restart. Setup checks the +matching usbip.exe, libusbip.dll and driver identities before enabling output. +Port cleanup preserves the exact localhost endpoint and device ownership. diff --git a/extras/USBip-0.9.7.7-LICENSE.txt b/extras/USBip-0.9.8.1-LICENSE.txt similarity index 100% rename from extras/USBip-0.9.7.7-LICENSE.txt rename to extras/USBip-0.9.8.1-LICENSE.txt diff --git a/extras/USBip-0.9.7.7-x64.exe b/extras/USBip-0.9.8.1-x64.exe similarity index 63% rename from extras/USBip-0.9.7.7-x64.exe rename to extras/USBip-0.9.8.1-x64.exe index 282b7332..e6cd9a6d 100644 Binary files a/extras/USBip-0.9.7.7-x64.exe and b/extras/USBip-0.9.8.1-x64.exe differ diff --git a/extras/install-viiper-backend.ps1 b/extras/install-viiper-backend.ps1 index 679f2e7b..cacbcc2e 100644 --- a/extras/install-viiper-backend.ps1 +++ b/extras/install-viiper-backend.ps1 @@ -86,7 +86,7 @@ $script:UsbipReplacementPhaseOne = $false $script:SetupMutex = $null $script:SetupMutexOwned = $false $script:SetupTransactionStarted = $false -$script:RequiredUsbipVersion = [Version]"0.9.7.7" +$script:RequiredUsbipVersion = [Version]"0.9.8.1" $script:ViiperServerArguments = "server --usb.retained-import-authority-id=4923336367393615921" # RC4.5 / RC4.5.1-4 shipped this exact VIIPER executable. Their in-app @@ -96,18 +96,20 @@ $script:ViiperServerArguments = $script:LegacyPortableViiperSha256 = "F1ECEF158F02D0BDCD1296C8D5097A281169081D0D59C1A8971592FAC78155EF" $script:UsbipInstallerSha256 = - "51620fa5f9f8be5932bc9d786deee557ce06d5407a99cab490dcfac71f185fea" + "38cad6d4432b52d5bb9409d9ad03b72fdffc4ada4cd3a48fbeca1a2752a8518a" $script:UsbipExecutableSha256 = - "fc1660e3759d8af4cede48dbe194285a5a1de85ce6e3216724499afd32be92e8" + "817da219fa153b4b37c3bc1aa8fcc63f33e046188c57cc3dd2c60a6ef0883c56" +$script:UsbipLibrarySha256 = + "f123ef5d288b817a890cbe39a3c2d212b3d8b1765c691c8703ccb76c7337986c" $script:UsbipUdeDriverSha256 = - "51db440065393e588a6b2585508c50eb3e1510b7b06d9afa6c5bde583751ea7d" + "abd4fc43dce40e027e4acc884cbfe92433e026206f6565c5ee5284a39441169a" $script:UsbipFilterDriverSha256 = - "c290299ff4d0f6a597db5ce03e15b29a5349cdce7c587ebfbd9ecaeca04f73ed" + "b93a5fc3c23d56f9b49634742374e32f269e53f764caa2d5fcb43e3259f7eced" $script:BundledViiperPath = Join-Path $script:PackageExtrasRoot ` "VIIPER-0.1.9-rc4.6.6-x64.exe" $script:BundledViiperSha256Path = $script:BundledViiperPath + ".sha256" $script:BundledUsbipInstallerPath = Join-Path $script:PackageExtrasRoot ` - "USBip-0.9.7.7-x64.exe" + "USBip-0.9.8.1-x64.exe" $programFilesRoot = if ($env:ProgramW6432) { $env:ProgramW6432 } @@ -151,7 +153,7 @@ $script:UsbipReplacementStatePath = Join-Path $script:InstallDir ` $script:UsbipUninstallKeyName = ` "{199505b0-b93d-4521-a8c7-897818e0205a}_is1" $script:InfrastructureRegistryPath = "HKLM:\SOFTWARE\DS4Windows" -$script:InfrastructureVersion = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.7.7" +$script:InfrastructureVersion = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.8.1" $script:System32 = [Environment]::SystemDirectory $script:PnPUtilPath = Join-Path $script:System32 "pnputil.exe" $script:TaskKillPath = Join-Path $script:System32 "taskkill.exe" @@ -551,7 +553,7 @@ function Get-UsbipDriverIntegrity { } if (-not [string]::Equals($hash, $expected[$serviceName], [StringComparison]::OrdinalIgnoreCase)) { - $details.Add("$serviceName does not match signed 0.9.7.7") + $details.Add("$serviceName does not match signed 0.9.8.1") continue } @@ -668,7 +670,7 @@ function Resolve-UsbipReplacementBoundary { $state.BootSessionId -eq $currentBootSessionId) { $script:RebootBoundaryPending = $true throw "USBIP replacement phase 1 is complete. Restart Windows before " + - "running Install / Repair again; 0.9.7.7 was not installed." + "running Install / Repair again; 0.9.8.1 was not installed." } Write-SetupLog ( @@ -789,16 +791,17 @@ function Test-Ds4WindowsOwnedUsbipPort([string]$block) { $serialLine = [regex]::Match( $block, '(?im)^\s*->\s+serial\b(?.*)$') if (-not $serialLine.Success) { - # usbip-win2 0.9.7.7 cannot assign an attach-time owner serial. The + # The published 0.9.8.1 serial override is broken. The # exact localhost VIIPER endpoint and bus/device tuple are its identity. return $true } $serial = [regex]::Match( - $serialLine.Groups['value'].Value, "^\s*'(?[^']*)'\s*$") + $serialLine.Groups['value'].Value, + "^\s*(?::\s*(?[^\r\n]*)|'(?[^']*)')\s*$") if (-not $serial.Success) { return $false } - $serialValue = $serial.Groups['serial'].Value + $serialValue = $serial.Groups['serial'].Value.Trim() return [string]::IsNullOrEmpty($serialValue) -or $serialValue -cmatch '^DS4W[0-9A-Fa-f]{11}$' } @@ -1112,11 +1115,13 @@ function Test-UsbipRuntime([string]$usbipPath) { } if (-not (Test-FileSha256 $usbipPath ` - $script:UsbipExecutableSha256)) { + $script:UsbipExecutableSha256) -or + -not (Test-FileSha256 (Join-Path (Split-Path -Parent $usbipPath) ` + "libusbip.dll") $script:UsbipLibrarySha256)) { $script:UsbipRuntimeProbeState = "executable-mismatch" Write-SetupLog ( - "usbip.exe does not match the verified executable from the " + - "bundled 0.9.7.7 package. Repair is required." + "usbip.exe or libusbip.dll does not match the verified files from the " + + "bundled 0.9.8.1 package. Repair is required." ) Yellow return $false } @@ -1143,7 +1148,7 @@ function Test-UsbipRuntime([string]$usbipPath) { if ($abiMismatch) { $script:UsbipRuntimeProbeState = "abi-mismatch" Write-SetupLog ( - "usbip-win2 0.9.7.7 is installed, but Windows still has a " + + "usbip-win2 0.9.8.1 is installed, but Windows still has a " + "different driver ABI loaded. A reboot is required." ) Yellow return $false @@ -3097,7 +3102,7 @@ try { Write-Host "Planned order:" -ForegroundColor Cyan Write-Host " 1. Install VIIPER and register both elevated startup tasks." ` -ForegroundColor Cyan - Write-Host " 2. Verify or install packaged usbip-win2 0.9.7.7." ` + Write-Host " 2. Verify or install packaged usbip-win2 0.9.8.1." ` -ForegroundColor Cyan Write-Host " 3. Verify the startup tasks remain exact and enabled." ` -ForegroundColor Cyan @@ -3113,7 +3118,7 @@ try { "interactively, or use -Yes after reviewing the installation " + "and restart requirements." } - Write-Host "VIIPER is bundled and installs first. An incompatible USBIP driver is removed only after VIIPER is in place, and 0.9.7.7 is installed only after a separate reboot." -ForegroundColor Yellow + Write-Host "VIIPER is bundled and installs first. An incompatible USBIP driver is removed only after VIIPER is in place, and 0.9.8.1 is installed only after a separate reboot." -ForegroundColor Yellow Write-Host "Save work before continuing; a driver replacement can require you to restart Windows and run Repair again." -ForegroundColor Yellow $answer = Read-Host "Install bundled VIIPER first and continue? [Y/N]" if ($answer -notmatch '^(?i:y|yes)$') { @@ -3276,7 +3281,7 @@ try { Save-StartupSetupIntent "RepairRequired" Set-InfrastructureStartupFailClosed $viiperPath $script:Ds4WindowsRestartPath - Write-Step "Step 2 of 4 - Checking usbip-win2 0.9.7.7" + Write-Step "Step 2 of 4 - Checking usbip-win2 0.9.8.1" $requiredUsbipVersion = $script:RequiredUsbipVersion try { $usbipVersion = Get-UsbipInstalledVersion @@ -3289,7 +3294,9 @@ try { $canonicalUsbipPresent = Test-Path -LiteralPath $script:CanonicalUsbipPath $usbipExecutableSafe = $canonicalUsbipPresent -and (Test-FileSha256 $script:CanonicalUsbipPath ` - $script:UsbipExecutableSha256) + $script:UsbipExecutableSha256) -and + (Test-FileSha256 (Join-Path (Split-Path -Parent $script:CanonicalUsbipPath) ` + "libusbip.dll") $script:UsbipLibrarySha256) $usbipDriverIntegrity = Get-UsbipDriverIntegrity if ($usbipDriverIntegrity.InstalledServiceCount -lt 0) { throw "USBIP driver integrity inspection failed: " + @@ -3404,7 +3411,7 @@ try { } else { $usbipInstaller = Join-Path $script:TempDir ` - "USBip-0.9.7.7-x64.exe" + "USBip-0.9.8.1-x64.exe" if (-not (Test-Path -LiteralPath ` $script:BundledUsbipInstallerPath -PathType Leaf)) { $missingUsbipName = Split-Path -Leaf ` @@ -3413,7 +3420,7 @@ try { "missing $missingUsbipName." } Write-SetupLog ( - "Using the bundled usbip-win2 0.9.7.7 installer." + "Using the bundled usbip-win2 0.9.8.1 installer." ) Green Assert-FileSha256 $script:BundledUsbipInstallerPath ` $script:UsbipInstallerSha256 @@ -3440,7 +3447,9 @@ try { $canonicalUsbipPresent = Test-Path -LiteralPath $script:CanonicalUsbipPath $usbipExecutableSafe = $canonicalUsbipPresent -and (Test-FileSha256 $script:CanonicalUsbipPath ` - $script:UsbipExecutableSha256) + $script:UsbipExecutableSha256) -and + (Test-FileSha256 (Join-Path (Split-Path -Parent $script:CanonicalUsbipPath) ` + "libusbip.dll") $script:UsbipLibrarySha256) $usbipDriverIntegrity = Get-UsbipDriverIntegrity $usbipDriverFilesSafe = [bool]$usbipDriverIntegrity.Safe $usbipVersionReady = $canonicalUsbipPresent -and $usbipVersion -and @@ -3484,7 +3493,7 @@ try { Write-Host "" Write-SetupLog ( "VIIPER is installed and the old USBIP package removal was " + - "started. Packaged 0.9.7.7 was not installed. Restart Windows, " + + "started. Packaged 0.9.8.1 was not installed. Restart Windows, " + "then run Install / Repair again to complete phase 2." ) Yellow } diff --git a/installer/DS4Windows.Bootstrapper/InfrastructureProbe.cs b/installer/DS4Windows.Bootstrapper/InfrastructureProbe.cs index 537ad8e7..17c7597d 100644 --- a/installer/DS4Windows.Bootstrapper/InfrastructureProbe.cs +++ b/installer/DS4Windows.Bootstrapper/InfrastructureProbe.cs @@ -10,13 +10,14 @@ namespace DS4Windows.Bootstrapper { internal static class InfrastructureProbe { - private const string ExpectedMarker = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.7.7"; + private const string ExpectedMarker = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.8.1"; private const string ExpectedViiperVersion = "0.1.9-rc4.6.6"; private const string ExpectedViiperHash = "9392A49E619E1D9B7956EA4BEDAFD2E74CBF5065C011554E80F756D1524F892B"; - private const string ExpectedUsbipVersion = "0.9.7.7"; - private const string ExpectedUsbipHash = "FC1660E3759D8AF4CEDE48DBE194285A5A1DE85CE6E3216724499AFD32BE92E8"; - private const string ExpectedUdeHash = "51DB440065393E588A6B2585508C50EB3E1510B7B06D9AFA6C5BDE583751EA7D"; - private const string ExpectedFilterHash = "C290299FF4D0F6A597DB5CE03E15B29A5349CDCE7C587EBFBD9ECAECA04F73ED"; + private const string ExpectedUsbipVersion = "0.9.8.1"; + private const string ExpectedUsbipHash = "817DA219FA153B4B37C3BC1AA8FCC63F33E046188C57CC3DD2C60A6EF0883C56"; + private const string ExpectedUsbipLibraryHash = "F123EF5D288B817A890CBE39A3C2D212B3D8B1765C691C8703CCB76C7337986C"; + private const string ExpectedUdeHash = "ABD4FC43DCE40E027E4ACC884CBFE92433E026206F6565C5EE5284A39441169A"; + private const string ExpectedFilterHash = "B93A5FC3C23D56F9B49634742374E32F269E53F764CAA2D5FCB43E3259F7ECED"; internal static bool IsHealthy() { @@ -30,7 +31,8 @@ internal static bool IsHealthy() if (!VersionMatches(viiper, ExpectedViiperVersion) || !HashMatches(viiper, ExpectedViiperHash) || !VersionMatches(usbip, ExpectedUsbipVersion) || - !HashMatches(usbip, ExpectedUsbipHash)) + !HashMatches(usbip, ExpectedUsbipHash) || + !HashMatches(Path.Combine(programFiles, "USBip", "libusbip.dll"), ExpectedUsbipLibraryHash)) { return false; } diff --git a/installer/DS4Windows.Bootstrapper/InstallerWindow.xaml b/installer/DS4Windows.Bootstrapper/InstallerWindow.xaml index 69072c45..9c74c6e7 100644 --- a/installer/DS4Windows.Bootstrapper/InstallerWindow.xaml +++ b/installer/DS4Windows.Bootstrapper/InstallerWindow.xaml @@ -105,8 +105,8 @@ - - + + diff --git a/installer/DS4Windows.Bundle/Bundle.wxs b/installer/DS4Windows.Bundle/Bundle.wxs index dae0e06d..1cd32627 100644 --- a/installer/DS4Windows.Bundle/Bundle.wxs +++ b/installer/DS4Windows.Bundle/Bundle.wxs @@ -103,7 +103,7 @@ PerMachine="yes" Permanent="yes" Vital="yes" - DetectCondition="InfrastructureInstalled = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.7.7" OR ManagedInstallRegistered OR ManagedViiperPresent" + DetectCondition="InfrastructureInstalled = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.8.1" OR ManagedInstallRegistered OR ManagedViiperPresent" InstallArguments="install --correlation-id "[SetupCorrelationId]" --install-root "[ProgramFiles64Folder]DS4Windows" --bundle-id "[WixBundleProviderKey]" --desktop-shortcut "[CreateDesktopShortcut]" --target-user-sid "[TargetUserSid]" --target-user-name "[TargetUserName]" --target-local-appdata "[TargetLocalAppData]" --target-roaming-appdata "[TargetRoamingAppData]"" RepairArguments="repair --correlation-id "[SetupCorrelationId]" --install-root "[ProgramFiles64Folder]DS4Windows" --bundle-id "[WixBundleProviderKey]" --desktop-shortcut "[CreateDesktopShortcut]" --target-user-sid "[TargetUserSid]" --target-user-name "[TargetUserName]" --target-local-appdata "[TargetLocalAppData]" --target-roaming-appdata "[TargetRoamingAppData]"" UninstallArguments="uninstall --correlation-id "[SetupCorrelationId]" --install-root "[ProgramFiles64Folder]DS4Windows""> diff --git a/installer/DS4Windows.SetupActions/Program.cs b/installer/DS4Windows.SetupActions/Program.cs index dacbe08f..edc2d942 100644 --- a/installer/DS4Windows.SetupActions/Program.cs +++ b/installer/DS4Windows.SetupActions/Program.cs @@ -19,7 +19,7 @@ internal static class Program { private const string RegistryKeyPath = @"SOFTWARE\DS4Windows"; private const string InfrastructureVersion = - "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.7.7"; + "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.8.1"; private const string CurrentBundledViiperName = "VIIPER-0.1.9-rc4.6.6-x64.exe"; private static string CorrelationId = diff --git a/utils/post-build.py b/utils/post-build.py index a037593b..86243456 100644 --- a/utils/post-build.py +++ b/utils/post-build.py @@ -78,7 +78,7 @@ def is_reparse_point(path: Path) -> bool: "extras/VIIPER-SYSTRAY-NOTICE.md", "extras/VIIPER-SYSTRAY-LICENSE.txt", "extras/VIIPER-WDL-NOTICE.txt", - "extras/USBip-0.9.7.7-x64.exe", + "extras/USBip-0.9.8.1-x64.exe", "extras/HidHide_1.5.230_x64.exe", "extras/FakerInput_0.1.0_x64.msi", ) diff --git a/utils/test-localization-package.py b/utils/test-localization-package.py index 5e7e7de1..ce64af6b 100644 --- a/utils/test-localization-package.py +++ b/utils/test-localization-package.py @@ -24,7 +24,7 @@ "extras/VIIPER-0.1.9-rc4.6.6-BUILD-NOTES.txt", "extras/LICENSE.txt", "extras/VIIPER-SYSTRAY-NOTICE.md", "extras/VIIPER-SYSTRAY-LICENSE.txt", "extras/VIIPER-WDL-NOTICE.txt", - "extras/USBip-0.9.7.7-x64.exe", "extras/HidHide_1.5.230_x64.exe", + "extras/USBip-0.9.8.1-x64.exe", "extras/HidHide_1.5.230_x64.exe", "extras/FakerInput_0.1.0_x64.msi", ) SATELLITES = ( diff --git a/utils/test-viiper-reboot-boundary.ps1 b/utils/test-viiper-reboot-boundary.ps1 index fe6709ab..71820bab 100644 --- a/utils/test-viiper-reboot-boundary.ps1 +++ b/utils/test-viiper-reboot-boundary.ps1 @@ -53,11 +53,34 @@ foreach ($functionName in @( "Set-UsbipReplacementBoundary", "Remove-MismatchedUsbipPackage", "Resolve-UsbipReplacementBoundary", + "Test-Ds4WindowsOwnedUsbipPort", "Suspend-StartupTasksUntilInfrastructureReady", "Set-InfrastructureStartupFailClosed")) { Invoke-Expression (Get-BackendFunctionDefinition $functionName) } +# Parse the new upstream port output and the older quoted form without running +# any installer top-level code or touching the installed USB/IP driver. +foreach ($case in @( + @{ Line = '-> serial: '; Owned = $true }, + @{ Line = '-> serial: DS4W0123456789A'; Owned = $true }, + @{ Line = '-> serial: foreign'; Owned = $false }, + @{ Line = "-> serial: 'foreign'"; Owned = $false }, + @{ Line = "-> serial ''"; Owned = $true }, + @{ Line = "-> serial 'DS4W0123456789A'"; Owned = $true }, + @{ Line = "-> serial 'foreign'"; Owned = $false }, + @{ Line = '-> serial foreign'; Owned = $false })) { + foreach ($location in @('localhost:3241/17-4', 'remote.example:3241/17-4', + 'localhost:3240/17-4', 'localhost:3241/x1-protected')) { + $block = "Port 07: device in use at high-speed`n -> usbip://$location`n $($case.Line)`n -> mode: low-latency" + $expected = $case.Owned -and $location -eq 'localhost:3241/17-4' + if ((Test-Ds4WindowsOwnedUsbipPort $block) -ne $expected) { + throw "USB-IP cleanup ownership mismatch: $location / $($case.Line)" + } + } +} +Write-Host "USB-IP cleanup rejects foreign imports in both port-output formats." + $testRoot = Join-Path ([IO.Path]::GetTempPath()) ( "DS4Windows-Usbip-Reboot-Test-" + [Guid]::NewGuid().ToString("N")) New-Item -ItemType Directory -Path $testRoot | Out-Null @@ -178,23 +201,23 @@ try { } $entry = [pscustomobject]@{ - DisplayVersion = "0.9.7.8" + DisplayVersion = "0.9.7.7" QuietUninstallString = '"' + $uninstaller + '"' UninstallString = $null } $removed = Remove-MismatchedUsbipPackage $entry ` - ([Version]"0.9.7.8") ([Version]"0.9.7.7") + ([Version]"0.9.7.7") ([Version]"0.9.8.1") if (-not $removed -or -not $script:UsbipReplacementPhaseOne -or -not $script:RebootRecommended -or $script:UsbipRuntimeReady) { - throw "0.9.7.8 phase-one replacement contract did not converge." + throw "0.9.7.7 phase-one replacement contract did not converge." } $state = Get-Content -LiteralPath $script:UsbipReplacementStatePath ` -Raw | ConvertFrom-Json if ($state.BootSessionId -ne "boot-a" -or - $state.RemovedVersion -ne "0.9.7.8" -or - $state.RequiredVersion -ne "0.9.7.7") { + $state.RemovedVersion -ne "0.9.7.7" -or + $state.RequiredVersion -ne "0.9.8.1") { throw "USB-IP replacement marker did not preserve exact versions." } @@ -251,7 +274,7 @@ try { $failedUninstallRejected = $false try { [void](Remove-MismatchedUsbipPackage $entry ` - ([Version]"0.9.7.8") ([Version]"0.9.7.7")) + ([Version]"0.9.7.7") ([Version]"0.9.8.1")) } catch { $failedUninstallRejected = $_.Exception.Message -match "exit code 5" @@ -261,7 +284,7 @@ try { throw "A failed USB-IP uninstall could authorize a later phase." } - Write-Host "USB-IP 0.9.7.8 -> reboot -> 0.9.7.7 state simulation passed." + Write-Host "USB-IP 0.9.7.7 -> reboot -> 0.9.8.1 state simulation passed." } finally { Remove-Item -LiteralPath $testRoot -Recurse -Force ` diff --git a/utils/validate-installer.py b/utils/validate-installer.py index 6f8893e6..0edf869a 100644 --- a/utils/validate-installer.py +++ b/utils/validate-installer.py @@ -29,7 +29,7 @@ "extras/VIIPER-SYSTRAY-NOTICE.md", "extras/VIIPER-SYSTRAY-LICENSE.txt", "extras/VIIPER-WDL-NOTICE.txt", - "extras/USBip-0.9.7.7-x64.exe", + "extras/USBip-0.9.8.1-x64.exe", "extras/HidHide_1.5.230_x64.exe", "extras/FakerInput_0.1.0_x64.msi", } @@ -62,7 +62,7 @@ "VIIPER-SYSTRAY-LICENSE.txt": "05C55BD8834035C143C17AA218BFF08E22B7A66C5603CBA8F070DCAAFC974B8F", "VIIPER-WDL-NOTICE.txt": "2485006947F464C2A69E9F863D50529261FB684A7AC5E6BAE0D03BFE65F9E025", } -VIIPER_INFRASTRUCTURE_MARKER = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.7.7" +VIIPER_INFRASTRUCTURE_MARKER = "VIIPER-0.1.9-rc4.6.6+USBIP-0.9.8.1" def parse_unique_provenance(source: str) -> dict[str, str]: @@ -665,8 +665,8 @@ def main() -> int: "Test-RecognizedProductExecutable", '$script:InstallerLogRoot = Assert-SafeManagedDirectory', '"VIIPER-0.1.9-rc4.6.6-x64.exe"', - '[Version]"0.9.7.7"', - '"USBip-0.9.7.7-x64.exe"', + '[Version]"0.9.8.1"', + '"USBip-0.9.8.1-x64.exe"', 'Start-AndVerifyViiper', 'Start-AndVerifyViiperDirectly', 'Suspend-StartupTasksUntilInfrastructureReady',