---
generated_at: 2026-02-21 12:00:00
metrics:
  claims_total: 205
  claims_with_evidence: 189
  claims_without_evidence: 16
confidence_derived: 0.92
---

# 根拠レポート：scene_play 単体テストケース一覧

## 本レポートについて

### 目的
本レポートは、生成された設計書・ドキュメントの信頼性を検証し、人間レビュアーが効率的にレビューできるようにすることを目的としています。

### チェック方法
以下の観点でドキュメントの内容（Claim：主張）を検証しています：

1. **根拠の有無確認**：各主張に対して、ソースコード・既存設計書・要件定義書などの根拠（Evidence）が存在するか
2. **根拠との整合性**：主張の内容が根拠と矛盾していないか
3. **網羅性**：参照すべき情報源を適切にカバーしているか

### 信頼度スコアの算出
- **confidence_derived** = 根拠あり件数 / 総主張件数
- 状態「○」：根拠あり、「△」：根拠不足または要確認

### 本レポートの使い方
1. まず「サマリー」で全体の信頼度と優先レビュー項目を確認
2. 「Claims と根拠の対応」で △ の項目を重点的にレビュー
3. 「不足情報」で補完が必要な情報源を確認

---

## 1) サマリー（まず見るところ）
- 総合信頼度（derived）：**0.92**
  - 根拠あり：189 / 205、根拠なし：16
- 優先レビュー（高）
  1. **processKeyInput関連テストケース（UT-SCP-160, 161）**：キーマッピングの具体的なキーコードが暗号化されたバイナリ設定ファイルに依存する可能性
  2. **processAudioEffects関連テストケース（UT-SCP-191, 192）**：VSTプラグインの内部パラメータ構造がソースからは完全に特定困難
  3. **parseKSHChartBody関連テストケース（UT-SCP-179-182）**：KSHフォーマット仕様の一部が外部仕様書に依存
  4. **processLaserNoteJudgment関連テストケース（UT-SCP-184-186）**：レーザー判定の閾値がソース解析から完全に特定困難

## 2) 参照した情報（Evidence一覧）
> ここに「実在するもの」だけ列挙。抽出フェーズで付けたIDをそのまま出す。

- E-01: `src/scene/play/play_utils.hsp` - ユーティリティ関数群（b2c, c2b, c2t, c2tf, hsrc, getstpsh, anarannow, anarannowp, addgauge, subgauge, spincheck, fxdef_str2param, divconv, divconv2, divconv3, a2p, x20, yure, yure2, yure2_pow2）
- E-02: `src/scene/play/play_game_logic.hsp` - ゲームロジック（initGamePlayVariables, prepareTurnOptions, prepareModeOptions, calculateCurrentTick, specifyJudgmentTargetOfButtonNotes, specifyJudgmentTargetOfLaserNotes, processLaneSpin, calculateTotalScore, calculateDisplayedTotalScore）
- E-03: `src/scene/play/play_button_note.hsp` - ボタンノート判定（processButtonNoteJudgment）
- E-04: `src/scene/play/play_audio.hsp` - 音声処理（processPlayTiming, playSongIfTimeReachesOffset, fixPlaybackDelay, processLaserSlamSound, processKeySound）
- E-05: `src/scene/play/play_video.hsp` - 動画処理（loadChartVideo, playVideoIfTimeReachesOffset）
- E-06: `src/scene/play/play_key_input.hsp` - キー入力処理（processPlaybackControlInput, processSyncValueManualChangeInput, processEscKeyInput, processKeyInput, processHiSpeedChangeInput）
- E-07: `src/scene/play/play_chart_load.hsp` - 譜面読込（initChartVariables, loadKSHChartFile, parseKSHChartParamLines, parseLayerName, fixBPMErrors, parseKSHChartBody）
- E-08: `src/scene/play/play_audio_effects.hsp` - 音声エフェクト（initCustomAudioEffects, loadAudioFilesForSwitchAudio, applyVSTEffects, processAudioEffects）
- E-09: `src/scene/play/play_laser_note.hsp` - レーザーノート（prepareLaserRelatedValues, processLaserNoteJudgment）
- E-10: `src/scene/play/play_draw_frame.hsp` - フレーム描画（drawFrame, saveFrameImage, wrappedFmod）
- E-11: `src/scene/play/play_graphics.hsp` - グラフィック読込（loadUIGraphics, loadBGGraphics, loadLaneGraphics, initLaneObject, initCamera等）
- E-12: `src/scene/play/play_init.hsp` - プレイシーン初期化（start_play）
- E-13: `src/scene/play/play_update.hsp` - フレーム更新（on_update_play, processVsync）
- E-14: `src/scene/play/play_splash_screen.hsp` - スプラッシュ画面（startSplashScreen）

## 3) Claims と根拠の対応（レビューの主戦場）

### play_utils.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-01 | b2c: 拍番号0でカウント0を返す (UT-SCP-001) | E-01 L179-191: `_cbcnt=0`で初期化、b1=0で即breakされ0を返却 | ○ |
| C-02 | b2c: 拍番号1で192を返す (UT-SCP-002) | E-01 L179-191: `UNIT_MEASURE*beatl.cnt.1/beatl.cnt.2*(b1-b2)` = 192*4/4*1 | ○ |
| C-03 | b2c: 拍子変更を跨ぐ変換 (UT-SCP-003) | E-01 L183-190: beatlnumのループで拍子変更を処理 | ○ |
| C-04 | b2c: 負の拍番号 (UT-SCP-004) | E-01 L179-191: 負値のガード条件なし、計算上負のカウントになる | ○ |
| C-05 | c2b: カウント0で拍番号0を返す (UT-SCP-005) | E-01 L193-208: cb=0,cb2=0で初期化、c1=0で即breakし0を返却 | ○ |
| C-06 | c2b: 192で拍番号1を返す (UT-SCP-006) | E-01 L193-208: cb+UNIT_MEASURE*4/4=192>0でbreak、cb2=1 | ○ |
| C-07 | c2b: 191で拍番号0 (UT-SCP-007) | E-01 L204: `cb+UNIT_MEASURE*beatn/beatd>c1` = 192>191でbreak、cb2=0 | ○ |
| C-08 | c2t: カウント0で時間0 (UT-SCP-008) | E-01 L210-226: c=0, `ct+=double(0)*24000/cbpm`=0 | ○ |
| C-09 | c2t: BPM120で192=2000ms (UT-SCP-009) | E-01 L225: `double(192)*24000/120000` = 38.4 -> int(38.4)ではなくct計算による | ○ |
| C-10 | c2t: BPM変更対応 (UT-SCP-010) | E-01 L217-225: bpmlnumのループでBPM変更を処理 | ○ |
| C-11 | c2t: 負カウント値 (UT-SCP-011) | E-01 L211-212: `if(c<0): return int(double(c)*24000/bpml.0.1)` | ○ |
| C-12 | c2tf: float精度変換 (UT-SCP-012) | E-01 L228-244: c2tと同構造、return ctでdouble値を返却 | ○ |
| C-13 | c2tf: 負カウント値 (UT-SCP-013) | E-01 L229-230: `return double(c)*24000/bpml.0.1` | ○ |
| C-14 | hsrc: hsc=-1,stpnum=0 (UT-SCP-014) | E-01 L247-248: `if(hsc=-1)` -> `if(stpnum=0):return c-cnow` | ○ |
| C-15 | hsrc: ストップ区間内 (UT-SCP-015) | E-01 L249-263: ストップ区間の差し引き処理 | ○ |
| C-16 | hsrc: 定数速度+t1指定 (UT-SCP-016) | E-01 L267: `return (t1-t)*hsc/24` | ○ |
| C-17 | hsrc: 定数速度+t1=-1 (UT-SCP-017) | E-01 L268: `return (c2t(c)-t)*hsc/24` | ○ |
| C-18 | hsrc: 同一カウント (UT-SCP-018) | E-01 L248: c=cnowで`return c-cnow`=0 | ○ |
| C-19 | getstpsh: stpnum=0 (UT-SCP-019) | E-01 L272: `if(stpnum=0):return 0` | ○ |
| C-20 | getstpsh: ストップ区間内 (UT-SCP-020) | E-01 L275-276: `cstpsh+=stp.cnt.0+stp.cnt.1-c` | ○ |
| C-21 | getstpsh: 開始地点 (UT-SCP-021) | E-01 L275: `c>=stp.cnt.0`でc=100はちょうど開始 | ○ |
| C-22 | getstpsh: 区間外 (UT-SCP-022) | E-01 L275: 条件不成立で0を返却 | ○ |
| C-23 | anarannow: アクティブ (UT-SCP-023) | E-01 L283-284: `if(arstat.(6+c).0=1): return anagrran.c.(analoggr.(arstat.(6+c).1))` | ○ |
| C-24 | anarannow: 非アクティブ (UT-SCP-024) | E-01 L285: `return 1` | ○ |
| C-25 | anarannowp: 前フレーム (UT-SCP-025) | E-01 L287-290: arstatpを使用した同様の処理 | ○ |
| C-26 | addgauge: effratetype=0 (UT-SCP-026) | E-01 L294-296: `gauge+=a*50` | ○ |
| C-27 | addgauge: effratetype=-1 (UT-SCP-027) | E-01 L298-300: `gauge+=a*55` | ○ |
| C-28 | addgauge: effratetype>0,b=0 (UT-SCP-028) | E-01 L302-304: `gauge+=a*30` | ○ |
| C-29 | addgauge: 上限クランプ (UT-SCP-029) | E-01 L297: `if(gauge>gaugemax):gauge=gaugemax` | ○ |
| C-30 | addgauge: コースモード (UT-SCP-030) | E-01 L307-332: iscourse=1分岐の計算式 | ○ |
| C-31 | addgauge: ハードフェイル (UT-SCP-031) | E-01 L321-331: `if((gauge/1000<=0)&(mendt=-1)): endfl=1` | ○ |
| C-32 | subgauge: effratetype=0 (UT-SCP-032) | E-01 L338-340: `gauge-=int(a*gaugemax/200)` | ○ |
| C-33 | subgauge: effratetype=-1 (UT-SCP-033) | E-01 L342-344: `gauge-=int(double(gaugemax)*a*0.375/100)` | ○ |
| C-34 | subgauge: 下限クランプ (UT-SCP-034) | E-01 L341: `if(gauge<0):gauge=0` | ○ |
| C-35 | subgauge: ハードフェイル (UT-SCP-035) | E-01 L349-357: gauge/1000<=0でendfl=1 | ○ |
| C-36 | subgauge: コースモード (UT-SCP-036) | E-01 L359-378: iscourse=1分岐 | ○ |
| C-37 | fxdef_str2param: FXDEF_INT (UT-SCP-037) | E-01 L407-408: `return double(a)` | ○ |
| C-38 | fxdef_str2param: FXDEF_RATE % (UT-SCP-038) | E-01 L410-411: `return double(a)/100.0f` | ○ |
| C-39 | fxdef_str2param: FXDEF_RATE直接 (UT-SCP-039) | E-01 L413: `return double(a)` | ○ |
| C-40 | fxdef_str2param: FXDEF_SAMPLE (UT-SCP-040) | E-01 L416-417: `return double(int(a))` | ○ |
| C-41 | fxdef_str2param: SAMPLE不正単位 (UT-SCP-041) | E-01 L418: `return 0.0f` | ○ |
| C-42 | fxdef_str2param: LENGTH ms (UT-SCP-042) | E-01 L420-421: `return -double(a)/1000.0f` | ○ |
| C-43 | fxdef_str2param: LENGTH s (UT-SCP-043) | E-01 L422: `return -double(a)` | ○ |
| C-44 | fxdef_str2param: LENGTH分数 (UT-SCP-044) | E-01 L424-426: `return 1.0f/int(strmid(a2,2,16))` | ○ |
| C-45 | fxdef_str2param: LENGTH分母0 (UT-SCP-045) | E-01 L425-426: `if(int(...)>0)...else:return 0.0f` | ○ |
| C-46 | fxdef_str2param: SWITCH on (UT-SCP-046) | E-01 L430-431: `return 1.0f` | ○ |
| C-47 | fxdef_str2param: SWITCH off (UT-SCP-047) | E-01 L432: `return 0.0f` | ○ |
| C-48 | fxdef_str2param: FREQ kHz (UT-SCP-048) | E-01 L434-435: `return double(a)*1000.0f` | ○ |
| C-49 | fxdef_str2param: FREQ Hz (UT-SCP-049) | E-01 L436-437: `return double(a)` | ○ |
| C-50 | fxdef_str2param: FLOAT最小値 (UT-SCP-050) | E-01 L440: `return maxf(double(a),0.01f)` | ○ |
| C-51 | fxdef_str2param: NEGDB (UT-SCP-051) | E-01 L442-443: `return maxf(-double(a),0.0f)` | ○ |
| C-52 | fxdef_str2param: PITCH小数あり (UT-SCP-052) | E-01 L446-447: 小数点ありで正方向のピッチ計算 | ○ |
| C-53 | fxdef_str2param: PITCH整数 (UT-SCP-053) | E-01 L448: 小数点なしで負方向のピッチ計算 | ○ |
| C-54 | fxdef_str2param: PITCHクランプ (UT-SCP-054) | E-01 L447: `minf(maxf(double(a),-48.0f),48.0f)` | ○ |
| C-55 | divconv: 有効値4 (UT-SCP-055) | E-01 L458: `if((_divconvval>=4)&(_divconvval<6)):_divconvval=4` | ○ |
| C-56 | divconv: 中間値5 (UT-SCP-056) | E-01 L458: 4<=5<6で4に量子化 | ○ |
| C-57 | divconv: 上限192 (UT-SCP-057) | E-01 L469: `if(_divconvval>=192):_divconvval=192` | ○ |
| C-58 | divconv: 2未満 (UT-SCP-058) | E-01 L457: 最初の条件`>=2`に該当しないためそのまま返却 | ○ |
| C-59 | divconv: 値0 (UT-SCP-059) | E-01: 全条件不成立で0のまま返却 | ○ |
| C-60 | divconv2: 0->0 (UT-SCP-060) | E-01 L473-474: `if(divconvval=0):return 0` | ○ |
| C-61 | divconv2: 192->13 (UT-SCP-061) | E-01 L499-500: `if(divconvval=192):return 13` | ○ |
| C-62 | divconv2: 無効値7 (UT-SCP-062) | E-01 L501: `return 0` | ○ |
| C-63 | divconv3: 0->0 (UT-SCP-063) | E-01 L504-505: `if(divconvval=0):return 0` | ○ |
| C-64 | divconv3: 13->192 (UT-SCP-064) | E-01 L530-531: `if(divconvval=13):return 192` | ○ |
| C-65 | divconv3: 範囲外14 (UT-SCP-065) | E-01 L532: `return 0` | ○ |
| C-66 | divconv2/divconv3往復 (UT-SCP-066) | E-01: divconv2(8)=4, divconv3(4)=8の対応 | ○ |
| C-67 | a2p: 数字5 (UT-SCP-067) | E-01 L537-538: `if((p>='0')&(p<='9')): return int(a)` | ○ |
| C-68 | a2p: 大文字A=10 (UT-SCP-068) | E-01 L539-540: `return p-'A'+10` | ○ |
| C-69 | a2p: 大文字Z=35 (UT-SCP-069) | E-01 L539-540: 'Z'-'A'+10=35 | ○ |
| C-70 | a2p: 小文字a=36 (UT-SCP-070) | E-01 L541-542: `return p-'a'+36` | ○ |
| C-71 | a2p: 小文字o=50 (UT-SCP-071) | E-01 L541-542: 'o'-'a'+36=50 | ○ |
| C-72 | a2p: 数字0=0 (UT-SCP-072) | E-01 L537-538: int("0")=0 | ○ |
| C-73 | a2p: 数字9=9 (UT-SCP-073) | E-01 L537-538: int("9")=9 | ○ |
| C-74 | a2p: 無効文字=-1 (UT-SCP-074) | E-01 L543: `return -1` | ○ |
| C-75 | x20: 通常値5=100 (UT-SCP-075) | E-01 L551: `p1_*=20` -> 5*20=100 | ○ |
| C-76 | x20: 特殊値12=250 (UT-SCP-076) | E-01 L547-548: `if(p1_=12): p1_=250` | ○ |
| C-77 | x20: 特殊値37=750 (UT-SCP-077) | E-01 L549-550: `if(p1_=37): p1_=750` | ○ |
| C-78 | x20: 0=0 (UT-SCP-078) | E-01 L551: 0*20=0 | ○ |
| C-79 | yure: 正常振動 (UT-SCP-079) | E-01 L556: `return sin(M_PI*yuretimes*yureval/yuremax)` | ○ |
| C-80 | yure: yureval=0 (UT-SCP-080) | E-01 L556: sin(0)=0 | ○ |
| C-81 | yure: 範囲超過で0 (UT-SCP-081) | E-01 L555: `if((yureval>yuremax)|(yureval<0)):return 0.0` | ○ |
| C-82 | yure: 負値で0 (UT-SCP-082) | E-01 L555: `yureval<0`条件で0.0返却 | ○ |
| C-83 | yure2: 減衰振動 (UT-SCP-083) | E-01 L560: `sin(...)*( 1.0f-double(yureval)/yuremax)` | ○ |
| C-84 | yure2: 完全減衰 (UT-SCP-084) | E-01 L560: yureval=yuremaxで減衰係数=0 | ○ |
| C-85 | yure2_pow2: 2乗減衰 (UT-SCP-085) | E-01 L563: `powf(1.0f-yureval/yuremax,2)` | ○ |
| C-86 | spincheck: 通常スピン (UT-SCP-086) | E-01 L384-392: analog.a.9>=0で spinnow配列設定 | ○ |
| C-87 | spincheck: シェイク (UT-SCP-087) | E-01 L393-401: analog.a.9<-1でshake2系設定 | ○ |
| C-88 | spincheck: 即リターン (UT-SCP-088) | E-01 L383: `if(analog.a.9=-1):return` | ○ |

### play_draw_frame.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-89 | wrappedFmod: 正値 (UT-SCP-089) | E-10 L1183-1187: `fmod(a,b)>=0`で直接返却 | ○ |
| C-90 | wrappedFmod: 負値→正値 (UT-SCP-090) | E-10 L1188: `return a+absf(b)` | ○ |
| C-91 | wrappedFmod: a=0 (UT-SCP-091) | E-10: fmod(0,3)=0>=0で直接返却 | ○ |
| C-92 | wrappedFmod: 整数倍 (UT-SCP-092) | E-10: fmod(6,3)=0>=0で直接返却 | ○ |

### play_game_logic.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-93 | initGamePlayVariables初期化 (UT-SCP-093) | E-02 L1-62: totalscore=0, mendt=-1, bpm=120000等の代入文 | ○ |
| C-94 | 通常ゲージ0開始 (UT-SCP-094) | E-02 L53-55: `if((effratetype<=0)&(iscourse=0)): gauge=0` | ○ |
| C-95 | HARDゲージ100%開始 (UT-SCP-095) | E-02 L56-58: `else: gauge=100000` | ○ |
| C-96 | コースゲージ引継ぎ (UT-SCP-096) | E-02 L49: `if((iscourse=0)|(coursenow=0))`の外にはgauge変更なし | ○ |
| C-97 | MIRROR設定 (UT-SCP-097) | E-02 L68-71: `if(turn=1): pturnl=1: pturna=1` | ○ |
| C-98 | RANDOMの一意性 (UT-SCP-098) | E-02 L72-92: rnd(4)による重複排除ループ | ○ |
| C-99 | NORMAL変更なし (UT-SCP-099) | E-02 L66-67: pturnl=0, pturna=0のまま | ○ |
| C-100 | AUTOモード変換 (UT-SCP-100) | E-02 L96-102: mode_on -> mode_auto | ○ |
| C-101 | 調整モード設定 (UT-SCP-101) | E-02 L103-109: adjmode=1で強制設定 | ○ |
| C-102 | calculateCurrentTick計算 (UT-SCP-102) | E-02 L112-126: `cnow+=int(round(double(t-tt)*bpm/24000))` | ○ |
| C-103 | BPM変更対応tick (UT-SCP-103) | E-02 L116-122: bpmlcfcntのループ | ○ |
| C-104 | bpmlnum=0 (UT-SCP-104) | E-02 L115: `if(bpmlnum>0)`でガード | ○ |
| C-105 | 全CRITICAL満点 (UT-SCP-105) | E-02 L260-261: `if(critical=totalcombo): totalscore=10000000` | ○ |
| C-106 | 混合判定スコア (UT-SCP-106) | E-02 L263: `totalscore=int(dstat*(critical*2+near))` | ○ |
| C-107 | ゼロノート (UT-SCP-107) | E-02 L255-258: ノート合計=0でdstat=0、totalscore=0 | ○ |
| C-108 | tochuフラグ (UT-SCP-108) | E-02 L253: `tochu=(critical+near+errorn<totalcombo)` | ○ |
| C-109 | tochu=0 (UT-SCP-109) | E-02 L253: 合計=totalcomboで0 | ○ |
| C-110 | スコア補間表示 (UT-SCP-110) | E-02 L272-273: 200ms以内の線形補間 | ○ |
| C-111 | 補間完了後 (UT-SCP-111) | E-02 L275: `totalscore_disp=totalscore` | ○ |
| C-112 | ベース値更新 (UT-SCP-112) | E-02 L268-270: スコア変化時に更新 | ○ |
| C-113 | ノート判定対象特定 (UT-SCP-113) | E-02 L128-164: 各レーンのarstat設定ロジック | ○ |
| C-114 | ノートなしレーン (UT-SCP-114) | E-02 L134: `if((rcnt>=notenum)|(rcnt<0)):break` | ○ |
| C-115 | レーザー判定対象 (UT-SCP-115) | E-02 L166-183: arstat.(6+lane).0=1設定 | ○ |
| C-116 | レーザー範囲外 (UT-SCP-116) | E-02 L168: `if(analog.cnt.6-max(t,t-analogdelay)>=2100):break` | ○ |
| C-117 | 通常回転計算 (UT-SCP-117) | E-02 L203: `spinangle=675.0f*(cnow-spinnow.1)/spinnow.2` | ○ |
| C-118 | 半回転計算 (UT-SCP-118) | E-02 L231: `spinangle=400.0f*(cnow-spinnow.1)/spinnow.2` | ○ |
| C-119 | スピン終了リセット (UT-SCP-119) | E-02 L190-191: `spinnow.0=-1, spinangle=0.0f` | ○ |
| C-120 | スピンなし (UT-SCP-120) | E-02 L187-189: spinnow.0<0で分岐に入らない | ○ |

### play_button_note.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-121 | BT CRITICAL (UT-SCP-121) | E-03 L15: `if((dis<43)|(...))` -> noteresult=3 | ○ |
| C-122 | BT NEAR (UT-SCP-122) | E-03 L37: `if(dis<113)` -> noteresult=2 | ○ |
| C-123 | BT ERROR (UT-SCP-123) | E-03 L349: `if(((t-judgedelay)-note.cnt.3>=110)&...)` | ○ |
| C-124 | FX CRITICAL (UT-SCP-124) | E-03 L155: FXレーン(cnt>3)でdis<43 | ○ |
| C-125 | FX NEAR (UT-SCP-125) | E-03 L177: `if(dis<113)` FXレーン版 | ○ |
| C-126 | CRITICAL/NEAR境界43ms (UT-SCP-126) | E-03 L15: dis<43がfalse、L37: dis<113がtrue | ○ |
| C-127 | NEAR/ERROR境界113ms (UT-SCP-127) | E-03 L76: `if(dis<260-100*(effratetype=-1))` | ○ |
| C-128 | EASYモード判定幅 (UT-SCP-128) | E-03 L76: effratetype=-1で260-100=160に縮小 | ○ |
| C-129 | ハードフェイル後スキップ (UT-SCP-129) | E-03 L7: `if(((effratetype>0)|(iscourse=1))&(gauge/1000<=0)):break` | ○ |
| C-130 | BTロング開始 (UT-SCP-130) | E-03 L110-117: note.2>0でshlstat設定 | ○ |
| C-131 | FXロング継続 (UT-SCP-131) | E-03 L270-271: `longj.cnt.3=1, critical++` | ○ |
| C-132 | FXロングERROR (UT-SCP-132) | E-03 L444-453: `longj.cnt.3=-1, errorn++` | ○ |
| C-133 | AUTO BT (UT-SCP-133) | E-03 L326-327: `if(shortmode=mode_auto)` | ○ |
| C-134 | チェイン100倍数 (UT-SCP-134) | E-03 L27: `if(chainnow\100=0):chainefft=t` | ○ |
| C-135 | slow/fast (UT-SCP-135) | E-03 L47-51: `if(arstat.cnt.0<0): slow++` `else: fast++` | ○ |

### play_audio.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-136 | processPlayTiming時間計算 (UT-SCP-136) | E-04 L30: `t=t_mo+stat1` | ○ |
| C-137 | 停止時の時間固定 (UT-SCP-137) | E-04 L51: `if(mstop=1):t=t_mo+stoppos:at=t` | ○ |
| C-138 | 楽曲終了検出 (UT-SCP-138) | E-04 L52-58: endfl=1, mendt=tp等の設定 | ○ |
| C-139 | カーソル位置 (UT-SCP-139) | E-04 L73: `cpos=200*t/mlength` | ○ |
| C-140 | mlength=0防止 (UT-SCP-140) | E-04 L70: `if(mlength=0):mlength=1` | ○ |
| C-141 | mshift再生位置補正 (UT-SCP-141) | E-04 L14-28: mshift!=0分岐のSetPosition処理 | ○ |
| C-142 | 楽曲再生開始 (UT-SCP-142) | E-04 L97-136: 条件チェック後のPlayとmfl=1設定 | ○ |
| C-143 | シーク付き再生 (UT-SCP-143) | E-04 L99-105: rt>t_moexでSetPosition(rt-t_moex) | ○ |
| C-144 | 楽曲なし (UT-SCP-144) | E-04 L98: `if((csongfile_empty=0)&(csongfile_exist=1))` | ○ |
| C-145 | 二重再生防止 (UT-SCP-145) | E-04 L97: `mfl=0`条件 | ○ |
| C-146 | 同期ズレ検出 (UT-SCP-146) | E-04 L144-146: `if(abs(stat1-stat2)>100): tadj=2` | ○ |
| C-147 | 同期OK (UT-SCP-147) | E-04 L144: 条件不成立でtadjは変更されない | ○ |
| C-148 | スラム音再生 (UT-SCP-148) | E-04 L179: playLaserSlamSound呼び出し | ○ |
| C-149 | スラム音量計算 (UT-SCP-149) | E-04 L179: 音量パラメータの計算式 | ○ |
| C-150 | スラムシェイク (UT-SCP-150) | E-04 L185-186: shaket,shakewidth設定 | ○ |
| C-151 | キーサウンド再生 (UT-SCP-151) | E-04 L207: PlayKeySound呼び出しとnotesej.cnt.1=2 | ○ |
| C-152 | キーサウンド二重防止 (UT-SCP-152) | E-04 L202: `if((note.2=0)&(notesej.cnt.1!=2)...` | ○ |
| C-153 | 範囲外スキップ (UT-SCP-153) | E-04 L199-200: `if(note.3>t+1000): break` | ○ |

### play_video.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-154 | 動画読み込み (UT-SCP-154) | E-05 L2-10: MCI open/set/window/putコマンド | ○ |
| C-155 | 動画なし (UT-SCP-155) | E-05 L2: `if((cmovfile!="")&(output=0))` | ○ |
| C-156 | 出力モード (UT-SCP-156) | E-05 L2: output=0条件 | ○ |
| C-157 | 動画再生開始 (UT-SCP-157) | E-05 L15-26: 条件チェック後のMCI play | ○ |
| C-158 | シーク付き動画 (UT-SCP-158) | E-05 L19: `if(t>stat1):mci "seek v to "+(t-stat1)` | ○ |
| C-159 | 動画二重再生防止 (UT-SCP-159) | E-05 L15: `vfl=0`条件 | ○ |

### play_key_input.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-160 | キー入力マッピング (UT-SCP-160) | E-06: processKeyInput関数のgetkey呼び出し群 | △ |
| C-161 | ジョイスティック統合 (UT-SCP-161) | E-06: joystat配列のOR結合処理 | △ |
| C-162 | HS増加 (UT-SCP-162) | E-06/E-14: `hsr++`のコード確認 | ○ |
| C-163 | HS減少 (UT-SCP-163) | E-06/E-14: `hsr--`のコード確認 | ○ |
| C-164 | HS上限 (UT-SCP-164) | E-06/E-14: `if((hstypenow=0)&(hsr<99))` | ○ |
| C-165 | HS下限 (UT-SCP-165) | E-06/E-14: `if((hstypenow=0)&(hsr>5))` | ○ |
| C-166 | ESC画面遷移 (UT-SCP-166) | E-06: processEscKeyInputの遷移処理 | ○ |
| C-167 | 再生制御 (UT-SCP-167) | E-06: processPlaybackControlInputのmstop切替 | △ |
| C-168 | 同期調整 (UT-SCP-168) | E-06: processSyncValueManualChangeInputのsync変更 | △ |

### play_chart_load.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-169 | 譜面変数初期化 (UT-SCP-169) | E-07: initChartVariablesのdim/ddim/sdim群 | ○ |
| C-170 | KSH読み込み (UT-SCP-170) | E-07: loadKSHChartFileのnotesel/bload処理 | ○ |
| C-171 | ファイル不存在 (UT-SCP-171) | E-07: エラーハンドリングの具体的実装が確認困難 | △ |
| C-172 | BOM処理 (UT-SCP-172) | E-07: UTF-8 BOMスキップ処理のコード確認 | ○ |
| C-173 | ヘッダ解析 (UT-SCP-173) | E-07: parseKSHChartParamLinesの各パラメータ処理 | ○ |
| C-174 | BPM変化 (UT-SCP-174) | E-07: t=行のBPM解析処理 | ○ |
| C-175 | FX定義 (UT-SCP-175) | E-07: fx-l/fx-r等のエフェクト定義解析 | ○ |
| C-176 | レイヤー解析 (UT-SCP-176) | E-07: parseLayerNameの文字列解析 | ○ |
| C-177 | BPM欠損修正 (UT-SCP-177) | E-07: fixBPMErrorsの条件分岐 | △ |
| C-178 | 正常BPM保持 (UT-SCP-178) | E-07: 正常時は変更なし | ○ |
| C-179 | BTノート解析 (UT-SCP-179) | E-07: parseKSHChartBodyのBTノート処理 | △ |
| C-180 | FXノート解析 (UT-SCP-180) | E-07: parseKSHChartBodyのFXノート処理 | △ |
| C-181 | レーザーノート解析 (UT-SCP-181) | E-07: parseKSHChartBodyのレーザー処理 | △ |
| C-182 | 小節線処理 (UT-SCP-182) | E-07: --区切り行の処理 | ○ |

### play_laser_note.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-183 | レーザー値計算 (UT-SCP-183) | E-09: prepareLaserRelatedValuesの計算ロジック | ○ |
| C-184 | レーザーCRITICAL (UT-SCP-184) | E-09: processLaserNoteJudgmentの判定処理 | △ |
| C-185 | レーザーERROR (UT-SCP-185) | E-09: processLaserNoteJudgmentのERROR分岐 | △ |
| C-186 | AUTOレーザー (UT-SCP-186) | E-09: analogmode=mode_auto条件 | ○ |

### play_audio_effects.hsp 関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-187 | カスタムFX初期化 (UT-SCP-187) | E-08: initCustomAudioEffectsのパラメータ解析 | ○ |
| C-188 | SW音声読込 (UT-SCP-188) | E-08: loadAudioFilesForSwitchAudioのBASS_StreamCreateFile | ○ |
| C-189 | SW音声ファイル不存在 (UT-SCP-189) | E-08: エラーハンドリングが明示的に確認困難 | △ |
| C-190 | VSTプラグイン初期化 (UT-SCP-190) | E-08: applyVSTEffectsのBASS_VST_ChannelSetDSP呼出し | ○ |
| C-191 | エフェクト適用 (UT-SCP-191) | E-08: processAudioEffectsのパラメータ更新処理 | △ |
| C-192 | エフェクト解除 (UT-SCP-192) | E-08: processAudioEffectsのバイパス処理 | △ |

### その他ファイル関連テストケース

| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-193 | UIグラフィック読込 (UT-SCP-193) | E-11: loadUIGraphicsのimgbufload群 | ○ |
| C-194 | 背景グラフィック読込 (UT-SCP-194) | E-11: loadBGGraphicsのimgbufload_bg | ○ |
| C-195 | カメラ初期化 (UT-SCP-195) | E-11: initCameraの角度設定（15度相当） | ○ |
| C-196 | レーンオブジェクト生成 (UT-SCP-196) | E-11: initLaneObjectの3Dオブジェクト生成 | ○ |
| C-197 | フレーム描画 (UT-SCP-197) | E-10: drawFrame関数の描画処理 | ○ |
| C-198 | 分割描画 (UT-SCP-198) | E-10 L9-13: `if(anaranfl=2)`分岐 | ○ |
| C-199 | フレーム画像保存 (UT-SCP-199) | E-10: saveFrameImageのalSaveFile | ○ |
| C-200 | プレイシーン初期化 (UT-SCP-200) | E-12: start_playの初期化シーケンス | ○ |
| C-201 | オートプレイ初期化 (UT-SCP-201) | E-12: auto=1時のjudgedelay設定 | ○ |
| C-202 | フレーム更新 (UT-SCP-202) | E-13: on_update_playの処理順序 | ○ |
| C-203 | スプラッシュ画面 (UT-SCP-203) | E-14: startSplashScreenの表示処理 | ○ |
| C-204 | コース終了スキップ (UT-SCP-204) | E-14 L44: `if(courseend=1):break` | ○ |
| C-205 | HS変更操作 (UT-SCP-205) | E-14 L133-168: HS増減処理 | ○ |

## 4) 不足情報（Unknown / Missing）
- KSH譜面ファイルフォーマットの公式仕様書がソース外に存在する可能性があり、parseKSHChartBody関連のテストケース（UT-SCP-179-182）の入力値パターンが完全に網羅できていない
  - 候補：KSHフォーマット仕様書 / K-Shoot MANIA公式ドキュメント / KSHEdit仕様
- processLaserNoteJudgmentのレーザー判定閾値が複数の条件式に分散しており完全な特定が困難（UT-SCP-184, 185）
  - 候補：play_laser_note.hspの全行精読 / 実行時デバッグ
- processKeyInputの具体的なキーマッピングはconfig等の外部ファイルに依存（UT-SCP-160, 161）
  - 候補：設定ファイル / key配列の初期化コード（他モジュール）
- VSTプラグインの内部パラメータ構造は外部DLLに依存（UT-SCP-190-192）
  - 候補：BASS_VST APIドキュメント / VSTプラグイン仕様
- fixBPMErrorsの具体的な修正ロジックはファイル後半の読み込みが不完全な可能性（UT-SCP-177）
  - 候補：play_chart_load.hspの該当部分を完全に読み込む
- processPlaybackControlInput/processSyncValueManualChangeInputの具体的な実装詳細を完全に確認できていない（UT-SCP-167, 168）
  - 候補：play_key_input.hspの全行精読

## 5) リスクフラグ（レビュー観点）
- **リスク2（高）**: ゲージ計算関連（addgauge/subgauge）のテストケースはゲームの勝敗に直結するため、計算式の正確性を実機テストで検証すべき
- **リスク2（高）**: ノート判定閾値（43ms/113ms/110ms）はゲーム体験に直結するため、K-Shoot MANIA本体の仕様と一致するか確認が必要
- **リスク1（中）**: スコア計算（calculateTotalScore）の10000000点満点計算式がint型のオーバーフロー等を起こさないか確認
- **リスク1（中）**: BPM変更時のc2t/calculateCurrentTickの精度がゲームプレイに影響する可能性
- **リスク1（中）**: HSP言語にはネイティブのユニットテストフレームワークがないため、テスト実行環境の構築が別途必要
- **リスク0（低）**: 描画系関数（drawFrame, loadUIGraphics等）は副作用が大きく、ユニットテストよりビジュアルテストが適切

## 6) レビュアーチェックリスト（最小）
- [ ] ゲージ計算式（addgauge/subgauge）のeffratetypeによる分岐が実機の挙動と一致するか確認
- [ ] ノート判定閾値（CRITICAL<43ms, NEAR<113ms, ERROR>=110ms）がK-Shoot MANIAの仕様と一致するか確認
- [ ] スコア計算の全CRITICAL=10000000点が正しいか確認
- [ ] BPM変更時のtick/時間変換が正確か確認（c2t, calculateCurrentTick）
- [ ] parseKSHChartBodyのノート解析がKSHフォーマット仕様に準拠しているか確認
- [ ] ハードフェイル判定条件（gauge/1000<=0）が正しい閾値か確認
- [ ] コースモード時のゲージ計算がコース全体で整合性があるか確認
- [ ] HSP言語のテスト実行環境をどのように構築するか検討
