# 帳票設計書 5-スコア記録ファイル

## 概要

本ドキュメントは、K-ShootMANIAのプレイヤーの各曲スコア・メダル・グレード・ゲージ率・チェイン数等のプレイ結果を.ksc形式で保存する「スコア記録ファイル」の帳票設計書である。プレイ完了後のリザルト画面で自動的にスコアデータが保存・更新される。

### 本帳票の処理概要

本帳票は、ゲームプレイ完了後にプレイ結果（スコア、メダル、グレード、ゲージ率、最大チェイン数、プレイ回数、クリア回数等）をCSV類似のテキスト形式で.kscファイルに保存する機能を提供する。既存のスコアが存在する場合は最高記録を比較して更新する。

**業務上の目的・背景**：音楽ゲームでは各楽曲・各難易度・各プレイ設定ごとのスコア記録を永続化し、プレイヤーの成長を記録する必要がある。スコア記録は楽曲選択画面でのハイスコア表示やメダル表示に使用され、プレイヤーのモチベーション維持に不可欠な機能である。

**帳票の利用シーン**：楽曲プレイ完了後のリザルト画面（result.hsp / course_result.hsp）で、オートプレイやアジャスト（判定調整）モードでない場合に自動保存される。また、楽曲選択画面で既存スコアの読み込み表示にも使用される。

**主要な出力内容**：
1. プレイ条件（ゲージタイプ、ノーツモード、レーザーモード等）のキー文字列
2. トータルスコア（最大10,000,000）
3. メダル（0=FAILED, 1=CLEARED, 2=FULL COMBO, 3=PERFECT）
4. グレード（0～5の6段階）
5. ゲージパーセント
6. 最大チェイン数
7. プレイ回数・クリア回数・フルコンボ回数・パーフェクト回数

**帳票の出力タイミング**：リザルト画面（on_update_result関数）の初期処理で、auto=0かつadjmode=0かつiscmdline=0の場合に保存される。コースモードの場合はcourse_result.hspで同様の保存処理が行われる。

**帳票の利用者**：K-ShootMANIAのランタイムエンジン（スコア読み書き）。プレイヤーは楽曲選択画面でハイスコアやメダルとして確認する。

## 帳票種別

データファイル出力（CSV類似テキスト形式）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | リザルト画面 | SCENE_RESULT（result.hsp） | プレイ完了後に自動保存 |
| - | コースリザルト画面 | SCENE_COURSE_RESULT（course_result.hsp） | コースプレイ完了後に自動保存 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | KSCテキスト（CSV類似のkey=value形式） |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | score/{PLAYER}/{songfolder}/{songname}.ksc |
| 出力方法 | HSPのnotesave命令によるテキスト保存 |
| 文字コード | Shift_JIS（HSPのデフォルト） |

### ファイルパス構造

```
score/
  {プレイヤー名}/
    {楽曲フォルダ名}/
      {譜面ファイル名}.ksc
```

コースモードの場合：
```
courses/score/
  {プレイヤー名}/
    {コースフォルダ名}/
      {コースファイル名}.ksc
```

## 帳票レイアウト

### レイアウト概要

1行が1つのプレイ条件に対応するスコアレコードで、キー=値の形式。値部分はカンマ区切り。

```
normal,normal,normal,on,on,on=8500000,1,3,85,1200,5,3,2,0
hard,normal,normal,on,on,on=9200000,2,4,92,1500,3,2,1,0
easy,mirror,normal,on,on,on=7800000,1,2,72,800,2,1,0,0
```

### ヘッダー部

該当なし（ヘッダーなしのデータファイル）

### 明細部（レコード構造）

各行のキー部分（=の左側）：

| No | フィールド | 説明 | データ取得元 | 値の例 |
|----|----------|------|-------------|--------|
| 1 | ゲージタイプ | エフェクティブレートの種類 | effratetype | "normal"/"hard"/"easy" |
| 2 | ターンモード | ノーツの配置変更 | turn | "normal"/"mirror"/"random"/"s-random"/"h-random" |
| 3 | 予備フィールド | 予約領域 | 固定 | "normal" |
| 4 | BTモード | ショートノーツの扱い | shortmode | "on"/"off"/"auto"/"hide" |
| 5 | FXモード | ロングノーツの扱い | longmode | "on"/"off"/"auto"/"hide" |
| 6 | レーザーモード | アナログデバイスの扱い | analogmode | "on"/"off"/"auto"/"hide" |

各行の値部分（=の右側、カンマ区切り）：

| No | フィールド | 説明 | データ取得元 | 型 |
|----|----------|------|-------------|------|
| 1 | スコア | トータルスコア（0～10000000） | totalscore | 整数 |
| 2 | メダル | クリア状態（0=FAILED, 1=CLEARED, 2=FULL COMBO, 3=PERFECT） | stat6 | 整数 |
| 3 | グレード | 成績ランク（0～5） | stat1_ | 整数 |
| 4 | ゲージ率 | エフェクティブレートのパーセント値 | gpercent | 整数 |
| 5 | 最大チェイン | 最大コンボ数 | chainmax_song | 整数 |
| 6 | プレイ回数 | 累計プレイ回数 | playcount | 整数 |
| 7 | クリア回数 | 累計クリア回数 | clearcount | 整数 |
| 8 | フルコンボ回数 | 累計フルコンボ回数 | fullcombocount | 整数 |
| 9 | パーフェクト回数 | 累計パーフェクト回数 | perfectcount | 整数 |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 非オートプレイ | auto=0であること | Yes |
| 非アジャストモード | adjmode=0であること | Yes |
| 非コマンドライン実行 | iscmdline=0であること | Yes |
| プレイ完了 | リザルト画面に到達していること | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | ファイル内の行順序 | 既存順序を維持 |

### 改ページ条件

該当なし

## データベース参照仕様

### 参照テーブル一覧

本帳票はデータベースを使用しない。既存のスコアファイル（.ksc）を読み込み、プレイ結果と比較して更新する。

| データ構造名 | 用途 | 備考 |
|-----------|------|---------|
| totalscore | 今回のトータルスコア | プレイ中に計算される |
| gauge/gaugemax | ゲージ値（クリア判定に使用） | エフェクティブレート |
| critical/near/errorn | 判定結果のカウント | CRITICAL/NEAR/ERROR数 |
| chainmax_song | 最大チェイン数 | プレイ中の最大コンボ |
| effratetype | ゲージタイプ | -1=EASY, 0=NORMAL, 1=HARD |
| turn | ターンモード | 0=normal, 1=mirror, 2=random, 3=s-random, 4=h-random |
| shortmode/longmode/analogmode | ノーツモード | 0=ON, 1=OFF, 2=AUTO, 3=HIDE |
| players.cplayer | 現在のプレイヤー名 | プレイヤーディレクトリ名 |
| csongdir | 楽曲ディレクトリ | パス構築に使用 |
| cnotesfile | 譜面ファイル名 | .kscファイル名の基準 |

### テーブル別参照項目詳細

#### 既存.kscファイル

| 参照項目 | 帳票項目との対応 | 取得条件 | 備考 |
|---------|----------------|---------|------|
| 各行のスコア値 | 最高スコアの比較用 | scorestat2.0 | 新スコアが高い場合のみ更新 |
| 各行のメダル値 | 最高メダルの比較用 | scorestat2.1 | 新メダルが高い場合のみ更新 |
| 各行のグレード値 | 最高グレードの比較用 | scorestat2.2 | 新グレードが高い場合のみ更新 |
| 各行のゲージ率 | 最高ゲージ率の比較用 | scorestat2.3 | 新ゲージ率が高い場合のみ更新（tochu=0条件付き） |
| 各行の最大チェイン | 最高チェインの比較用 | scorestat2.4 | 新チェインが高い場合のみ更新 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| メダル(stat6) | (gauge*100/gaugemax>=70) + (errorn=0) + (totalscore=10000000) | 整数（累積加算） | 0=FAILED, 1=CLEARED, 2=FC, 3=PERFECT |
| グレード(stat1_) | (totalscore2>=7000000) + (>=8000000) + (>=8900000) + (>=9400000) + (>=9800000) | 整数（累積加算） | 0～5の6段階 |
| totalscore2 | totalscore*9/10 + (gauge*100/gaugemax)*10000 | 整数 | グレード計算用の補正スコア |
| gpercent | 100*gauge/gaugemax（通常ゲージ）/ gauge/1000（HARDゲージ） | 整数 | ゲージタイプ依存 |
| diff | totalscore - totalscorep（前回最高スコア） | 整数 | ハイスコア更新差分 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[リザルト画面初期化] --> B{auto=0 & adjmode=0 & iscmdline=0?}
    B -->|No| Z[保存スキップ]
    B -->|Yes| C[条件キー文字列の構築]
    C --> D[既存.kscファイルのexist確認]
    D --> E{ファイル存在?}
    E -->|Yes| F[noteload で既存スコア読み込み]
    E -->|No| G[新規レコード作成]
    F --> H[各行の条件キーを比較]
    H --> I{同一条件のレコードあり?}
    I -->|Yes| J[最高記録の比較・更新]
    I -->|No| K[既存行をそのまま保持]
    J --> L[buf文字列に更新行を追記]
    K --> L
    L --> M{全行処理完了?}
    M -->|No| H
    M -->|Yes| N{新規レコード追加が必要?}
    N -->|Yes| O[新規行をbufに追加]
    N -->|No| P[パス]
    G --> O
    O --> Q[scoreディレクトリ構造の確認・作成]
    P --> Q
    Q --> R[notesave .kscファイル]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| プレイヤーディレクトリ不存在 | 新規プレイヤーの初回プレイ | なし | mkdirで自動作成（result.hsp:451行目） |
| 楽曲サブディレクトリ不存在 | 楽曲の初回プレイ | なし | mkdirで自動作成（result.hsp:456-465行目） |
| 途中終了 | ESCキーによるプレイ中断 | なし | tochu=1として記録（ゲージ率は0として保存） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1ファイルあたり数行～数十行（プレイ条件の組み合わせ数） |
| 目標出力時間 | 即座（テキスト読み書きのみ） |
| 同時出力数上限 | 1 |

## セキュリティ考慮事項

- スコアデータはプレーンテキスト形式で保存され、改ざんが容易
- プレイヤーディレクトリ名はユーザーが自由に設定可能（ファイルシステム制約の範囲内）
- 楽曲フォルダ名はKSHファイルのディレクトリ構造に依存するため、パストラバーサルの可能性は低い
- オンラインランキング（IR）との連携では別途認証・検証が行われる

## 備考

- コースモードでの保存先は courses/score/{PLAYER}/{coursefolder}/{coursename}.ksc（course_result.hsp:293-296行目）
- ゲージタイプの保存キーで、同一難易度の別ゲージ（例: normalとhard）は別レコードとして保存される
- HARDゲージクリア時にはnormal条件の行も同時に存在する場合がある（stat5_2変数による処理）
- tochu=1（途中終了）の場合、メダルは強制的に0（FAILED）になる

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

スコア記録に関連するグローバル変数とファイルパスの構成を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | result.hsp | `src/scene/result.hsp` | 78-158行目: critical, near, errorn, totalscore等の集計処理 |
| 1-2 | result.hsp | `src/scene/result.hsp` | 309-336行目: medalの文字列決定ロジック |

**読解のコツ**: スコア計算はresult.hspの初期処理で行われる。noteresult配列にはプレイ中の各ノーツの判定結果が格納されており、これをループで集計する。

#### Step 2: エントリーポイントを理解する

on_update_result関数の中でスコア保存がどこで呼ばれるかを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | result.hsp | `src/scene/result.hsp` | 341行目: if(auto=0)&(adjmode=0)&(iscmdline=0) の保存条件判定 |

**主要処理フロー**:
1. **341行目**: 保存条件チェック（auto/adjmode/iscmdline）
2. **351行目**: buf文字列の初期化、条件キー文字列（stat5_）の構築
3. **377行目**: 既存.kscファイルの存在確認
4. **383-431行目**: 既存レコードの読み込み・比較・更新処理
5. **436-438行目**: 新規レコードの追加
6. **440-468行目**: ディレクトリ作成・notesaveによるファイル保存

#### Step 3: スコア比較・更新処理を理解する

既存スコアとの比較ロジックを詳細に読む。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | result.hsp | `src/scene/result.hsp` | 383-431行目: 既存レコードの行ごとの比較・更新処理 |

**主要処理フロー**:
- **389-390行目**: split stat1,"=",scorestat でキーと値を分割
- **392行目**: if(scorestat.0=stat5_) で条件キーが一致するか確認
- **396-398行目**: スコアが既存より高い場合のみ更新
- **403-404行目**: メダルが既存より高い場合のみ更新
- **406-407行目**: グレードが既存より高い場合のみ更新
- **409-411行目**: ゲージ率が既存より高い場合のみ更新（tochu=0条件付き）
- **412-413行目**: 最大チェインが既存より高い場合のみ更新
- **415-422行目**: プレイ回数/クリア回数/FC回数/パーフェクト回数の累積加算

#### Step 4: コースモードの保存処理を理解する

コースモード特有の保存ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | course_result.hsp | `src/scene/course_result.hsp` | 264-371行目: コースモードのスコア保存処理。保存先がcourses/scoreになる |

### プログラム呼び出し階層図

```
kshootmania.hsp (メインループ)
    |
    +-- on_update_result (result.hsp:1)
    |       |
    |       +-- [判定集計] (78-158行目)
    |       |       noteresult配列のループ集計
    |       |       critical, near, errorn カウント
    |       |
    |       +-- [保存条件チェック] (341行目)
    |       |       auto=0 & adjmode=0 & iscmdline=0
    |       |
    |       +-- [条件キー構築] (351-376行目)
    |       |       stat5_ = "normal,normal,normal,on,on,on" 等
    |       |
    |       +-- [既存.ksc読み込み] (377-386行目)
    |       |       exist → noteload
    |       |
    |       +-- [比較・更新] (389-431行目)
    |       |       各レコードの最高記録を比較
    |       |
    |       +-- [ディレクトリ作成] (440-466行目)
    |       |       mkdir (必要に応じて)
    |       |
    |       +-- [ファイル保存] (467-468行目)
    |               notesave .kscファイル
    |
    +-- on_update_course_result (course_result.hsp:1)
            |
            +-- [コーススコア保存] (264-371行目)
                    保存先: courses/score/{PLAYER}/...
```

### データフロー図

```
[入力]                    [処理]                         [出力]

プレイ結果             result.hsp                     score/
  totalscore    --->     on_update_result (1-857行目)     {PLAYER}/
  gauge/gaugemax         |                               {songfolder}/
  critical/near/errorn   +-- 判定集計                      {songname}.ksc
  chainmax_song          +-- 条件キー構築
  effratetype            +-- 既存.ksc読み込み
  turn                   +-- 比較・更新
  shortmode/longmode/    +-- notesave
    analogmode
                                                       courses/score/
既存.kscファイル    course_result.hsp                    {PLAYER}/
  (前回のスコア) --->  on_update_course_result             {coursefolder}/
                       (264-371行目)                      {coursename}.ksc
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| result.hsp | `src/scene/result.hsp` | ソース | リザルト画面。判定集計・スコア保存の主処理（1-857行目） |
| course_result.hsp | `src/scene/course_result.hsp` | ソース | コースリザルト画面。コーススコア保存（264-371行目） |
| kshootmania.hsp | `kshootmania.hsp` | ソース | プレイヤー名・ディレクトリ設定の管理 |
| play_game_logic.hsp | `src/scene/play/play_game_logic.hsp` | ソース | プレイ中のスコア計算・判定処理 |
| {songname}.ksc | `score/{PLAYER}/{songfolder}/` | データ | スコア記録ファイル（本帳票の出力先） |
