Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
98 changes: 98 additions & 0 deletions docs/hid-probe-run.md
Original file line number Diff line number Diff line change
@@ -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` をそのまま返してほしい。何件目で止まったかがメッセージに入っている。
Loading