diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3e9081..24f0232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,9 +22,10 @@ jobs: } # rustup が rust-toolchain.toml を読むため、ここで版を固定しない。 + # llvm-tools は hid_probe.exe の import を読む llvm-readobj のために入れる。 - name: Install components if: steps.detect.outputs.exists == 'true' - run: rustup component add clippy rustfmt + run: rustup component add clippy rustfmt llvm-tools - name: Format if: steps.detect.outputs.exists == 'true' @@ -37,3 +38,105 @@ jobs: - name: Test if: steps.detect.outputs.exists == 'true' run: cargo test + + # 配布先に VC++ 再頒布可能パッケージのインストールを要求しないよう CRT を静的リンクする。 + # hidapi は cc 経由で C を混ぜるが、cc は crt-static を見て /MT に切り替える。 + - name: Build hid_probe + if: steps.detect.outputs.exists == 'true' + env: + RUSTFLAGS: -C target-feature=+crt-static + run: cargo build --bin hid_probe --release + + # import を読む道具に llvm-readobj を選ぶ。dumpbin は Visual Studio の版とエディションで + # 置き場所が変わるが、llvm-readobj は rustup が toolchain 配下の固定位置に置く。 + - name: Package hid_probe + if: steps.detect.outputs.exists == 'true' + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + + $exePath = 'target/release/hid_probe.exe' + $stageDir = 'dist' + + # Windows 10 / 11 に標準で入る DLL。api-ms-win-* (UCRT を含む) は OS の一部。 + # ここに無い import は配布先に無い可能性があるため exe に同梱する。 + $systemDllNames = @( + 'advapi32.dll', 'bcrypt.dll', 'cfgmgr32.dll', 'crypt32.dll', 'hid.dll', + 'kernel32.dll', 'kernelbase.dll', 'msvcrt.dll', 'ntdll.dll', 'ole32.dll', + 'oleaut32.dll', 'powrprof.dll', 'secur32.dll', 'setupapi.dll', 'shell32.dll', + 'user32.dll', 'userenv.dll', 'winmm.dll', 'ws2_32.dll' + ) + + $sysroot = (rustc --print sysroot).Trim() + $readobj = Get-ChildItem -Path (Join-Path $sysroot 'lib/rustlib/*/bin/llvm-readobj.exe') | + Select-Object -First 1 + if (-not $readobj) { + throw "llvm-readobj が $sysroot 配下に無い" + } + + # --coff-imports は Import と DelayImport のブロックを出し、各ブロックの Name 行が DLL 名になる。 + $readobjOutput = & $readobj.FullName --coff-imports $exePath + if ($LASTEXITCODE -ne 0) { + throw "llvm-readobj が失敗した (exit $LASTEXITCODE)" + } + + $importedDllNames = @($readobjOutput | + Select-String -Pattern '^\s+Name:\s+(\S+)$' | + ForEach-Object { $_.Matches[0].Groups[1].Value } | + Sort-Object -Unique) + + # Windows の exe は必ず kernel32 等を import する。0 件なら読み取りが壊れている。 + if ($importedDllNames.Count -eq 0) { + Write-Host $readobjOutput + throw "import が 1 件も取れなかった" + } + + New-Item -ItemType Directory -Force -Path $stageDir | Out-Null + Copy-Item -Path $exePath -Destination $stageDir -Force + + # 配布先に無い可能性のある DLL は、PATH と VC 再頒布ディレクトリから探して artifact に入れる。 + $vcRedistGlob = 'C:\Program Files*\Microsoft Visual Studio\*\*\VC\Redist\MSVC\*\x64\Microsoft.VC*.CRT' + $vcRedistDirs = Get-ChildItem -Path $vcRedistGlob -Directory -ErrorAction SilentlyContinue + $searchDirs = @($env:PATH -split ';') + @($vcRedistDirs | ForEach-Object { $_.FullName }) + + Write-Host 'hid_probe.exe が import する DLL:' + $unresolved = @() + foreach ($name in $importedDllNames) { + $lowered = $name.ToLowerInvariant() + if ($systemDllNames -contains $lowered -or $lowered -like 'api-ms-win-*') { + Write-Host " $name (system)" + continue + } + + $resolved = $searchDirs | + Where-Object { $_ } | + ForEach-Object { Join-Path $_ $name } | + Where-Object { Test-Path -Path $_ -PathType Leaf } | + Select-Object -First 1 + + if ($resolved) { + Copy-Item -Path $resolved -Destination $stageDir -Force + Write-Host " $name (同梱: $resolved)" + } else { + Write-Host " $name (見つからないため同梱できない)" + $unresolved += $name + } + } + + if ($unresolved.Count -gt 0) { + throw "配布先に無い可能性のある DLL を同梱できなかった: $($unresolved -join ', ')" + } + + Write-Host '' + Write-Host 'artifact の中身:' + Get-ChildItem -Path $stageDir | ForEach-Object { + Write-Host " $($_.Name) ($($_.Length) bytes)" + } + + - name: Upload hid_probe + if: steps.detect.outputs.exists == 'true' + uses: actions/upload-artifact@v4 + with: + name: hid_probe-windows-x64 + path: dist/ + if-no-files-found: error diff --git a/docs/hid-probe-run.md b/docs/hid-probe-run.md new file mode 100644 index 0000000..bb7b0eb --- /dev/null +++ b/docs/hid-probe-run.md @@ -0,0 +1,98 @@ +# hid_probe 実行手順 + +Windows 実機で DualSense の HID を読み取れるか確かめるための調査用ツール。Steam と Dark Souls Remastered を起動したまま動かし、出力ログをそのまま返してほしい。 + +所要目安は約 30 秒。 + +## このツールが何をするか + +Sony の HID デバイスを一覧し、DualSense を 1 つ読み取り専用で開く。入力レポートを 300 件受け取り、受信間隔の統計を出して終了する。 + +コントローラへの書き込みは行わない。`write` と `send_feature_report` は呼んでいない。 + +## 用意するもの + +- Windows 10 または 11 の PC +- DualSense +- Steam +- Dark Souls Remastered + +## 手順 + +### 1. hid_probe.exe を入手する + +GitHub Release に添付された `hid_probe.exe` を落とす。置き場所は `C:\Users\<名前>\Downloads` のような普通のフォルダでよい。 + +### 2. 警告が出たときの対処 + +**SmartScreen** + +「Windows によって PC が保護されました」という画面が出た場合は、「詳細情報」を押すと「実行」ボタンが現れるので、それを押す。 + +**ウイルス対策ソフト** + +Windows Defender やサードパーティ製のソフトが exe を隔離した場合は、実行せずにその時点のスクリーンショットを送ってほしい。除外設定の追加やセキュリティ機能の無効化はしないでほしい。 + +判断に迷ったときも、実行せずにスクリーンショットを送ってもらえれば十分。 + +### 3. Steam と Dark Souls Remastered を起動する + +1. Steam を起動する +2. Dark Souls Remastered を起動し、タイトル画面まで進める + +この状態を保ったまま次へ進む。ゲームは終了しない。 + +### 4. DualSense を接続し、接続方式を記録する + +DualSense を PC につなぎ、どちらでつないだかを控える。 + +- USB ケーブル +- Bluetooth + +### 5. hid_probe.exe を実行してログを取る + +`hid_probe.exe` を置いたフォルダを開き、アドレスバーに `powershell` と打って Enter を押す。そのフォルダで PowerShell が開く。 + +PowerShell の場合: + +```powershell +.\hid_probe.exe > log.txt 2>&1 +``` + +コマンドプロンプト (cmd) を使う場合: + +``` +hid_probe.exe > log.txt 2>&1 +``` + +`2>&1` はエラー出力もファイルに入れるための指定。これが無いとエラーがログに残らないので、必ず付けてほしい。 + +実行中はコントローラのスティックを適当に動かし続ける。終了するとプロンプトが戻り、同じフォルダに `log.txt` ができている。 + +### 6. log.txt の全文を返す + +一部を抜き出したり要約したりせず、全文を貼り付けるか、ファイルごと添付してほしい。 + +## こちらで見ること + +ログに出る受信間隔の `median` が 3000 から 5000µs の範囲に入るかどうかを記録する。範囲を外れていてもそのまま返してほしい。値の意味はこちらで判断するので、数字を見て実行し直す必要はない。 + +## うまくいかないとき + +### exe が起動しない、すぐ終わる + +`log.txt` の中身をそのまま返してほしい。それが答えになる。 + +### 「対象デバイスが見つからない」と出る + +その `log.txt` をそのまま返してほしい。ログの先頭に Sony のデバイス一覧が出ているので、何が見えているかはこちらで分かる。 + +### デバイスを開く段階でエラーが出る + +回避策を探さないでほしい。管理者権限で実行し直す、Steam を終了する、別のツールで試す、といった対処は不要。Steam 起動中に開けるかどうかがこの調査の目的なので、Steam を止めると測りたいものが測れなくなる。 + +エラーメッセージの全文を `log.txt` ごと返してもらえれば十分。 + +### ログが途中で止まっている + +300 件に届く前に read が失敗した状態。その `log.txt` をそのまま返してほしい。何件目で止まったかがメッセージに入っている。