# データベース設計書

## 概要

本ドキュメントは、K-Shoot MANIA v1.75b（HSP 3.51製リズムゲーム）のデータ永続化設計を記述する。本プロジェクトはRDBMSを使用せず、メモリ上の配列構造とファイルシステムベースの永続化（.kscスコアファイル、.ksh譜面ファイル、config.ini設定ファイル）によりデータを管理する。本書では、これらのメモリ配列およびファイル構造をテーブルとして論理的に再構成し、データベース設計の観点から整理する。

## テーブル一覧

| テーブル名 | 対応エンティティ | 説明 |
| --- | --- | --- |
| Players | プレイヤー | プレイヤー情報。`score/`配下のディレクトリ名で管理 |
| Songs | 曲情報 | 曲のメタデータ。メモリ配列`minfo`/`minfostr`および.kshファイルのヘッダから構成 |
| Scores | スコア | プレイヤーの曲別・難易度別・設定別の最高スコア。メモリ配列`mscore`および.kscファイルで永続化 |
| GameResults | プレイ結果 | 各プレイの詳細な判定結果。`result.hsp`内の変数で一時保持し、スコア更新時に.kscへ反映 |
| Courses | コース情報 | 複数曲をまとめたコース。.kcoファイルで定義 |
| CourseScores | コーススコア | コース単位のスコア・メダル・グレード。メモリ配列`course_sc`/`course_md`/`course_gr`で管理 |
| KeyConfig | キー設定 | キーボード・ジョイパッドの入力設定。config.iniで永続化 |
| GameConfig | ゲーム設定 | ハイスピード・画面サイズ等の各種設定。config.iniで永続化 |

## 各テーブル定義

### 1. Players

プレイヤー情報を管理する論理テーブル。`score/`ディレクトリ配下のフォルダ名がプレイヤー名として機能する。デフォルトプレイヤーは「PLAYER」。

**対応コード**: `kshootmania.hsp:378` (`sdim players,64,notemax`)

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| player_id | int | NO (PK) | プレイヤーID（配列インデックス） |
| player_name | string(64) | NO | プレイヤー名（`score/`配下のディレクトリ名） |

**制約**:
- PK: `player_id`
- UNIQUE: `player_name`
- デフォルト値: `player_name = "PLAYER"`

**永続化**: `score/[player_name]/`ディレクトリとして存在

### 2. Songs

曲のメタデータを管理する論理テーブル。`songs/`配下の.kshファイルから読み込まれ、メモリ配列`minfo`/`minfostr`に展開される。

**対応コード**:
- `select.hsp:2573` (`dim minfo,SONG_MAX,4,5`)
- `select.hsp:2574` (`sdim minfostr,256,SONG_MAX,4,13`)

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| song_id | int | NO (PK) | 曲ID（配列インデックス、最大SONG_MAX=8192） |
| difficulty | int | NO (PK) | 難易度（0:NORMAL, 1:HARD, 2:SPECIAL, 3:EXTRA） |
| type | int | NO | 種別（-1:フォルダ, -2:ディレクトリ, 0以上:通常曲） |
| level | int | YES | レベル（1〜20） |
| folder_name | string(256) | NO | フォルダ名（`minfostr[song_id][difficulty][0]`） |
| title | string(256) | YES | 曲タイトル（`minfostr[song_id][difficulty][1]`） |
| artist | string(256) | YES | アーティスト名（`minfostr[song_id][difficulty][2]`） |
| ksh_filename | string(256) | YES | KSHファイル名（`minfostr[song_id][difficulty][5]`） |
| bpm | string(256) | YES | BPM値（`minfostr[song_id][difficulty][6]`） |
| duration | string(256) | YES | 曲の長さ（`minfostr[song_id][difficulty][7]`） |
| jacket_image | string(256) | YES | ジャケット画像パス（`minfostr[song_id][difficulty][8]`） |
| title_image | string(256) | YES | タイトル画像パス（`minfostr[song_id][difficulty][9]`） |
| artist_image | string(256) | YES | アーティスト画像パス（`minfostr[song_id][difficulty][10]`） |
| illustrator | string(256) | YES | イラストレーター名（`minfostr[song_id][difficulty][11]`） |

**制約**:
- PK: (`song_id`, `difficulty`)
- CHECK: `difficulty IN (0, 1, 2, 3)`
- CHECK: `type >= -9`

**永続化**: `songs/[フォルダ名]/[KSHファイル名].ksh`

### 3. Scores

プレイヤーの曲別・難易度別・設定別の最高スコアを管理する論理テーブル。メモリ配列`mscore`に展開され、.kscファイルで永続化される。

**対応コード**:
- `select.hsp:4842` (`dim mscore,SONG_MAX,4,9`)
- `select.hsp:4841-4956` (スコア読み込み処理 `*reloadscore`)
- `result.hsp:375-468` (スコア保存処理)

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| song_id | int | NO (PK) | 曲ID（`mscore`第1次元） |
| difficulty | int | NO (PK) | 難易度 0-3（`mscore`第2次元） |
| modifier_settings | string | NO (PK) | 難易度設定文字列（例: `easy,normal,normal,on,on,on`） |
| score_value | int | NO | 最高スコア（0〜10,000,000）。`mscore[*][*][0]`、初期値-1（未プレイ） |
| medal | int | YES | メダル（-1:未プレイ, 0:失敗, 1:クリア, 2:FC, 3:PFC）。`mscore[*][*][1]` |
| grade | int | YES | グレード（-1:未プレイ, 0〜8:段階評価）。`mscore[*][*][2]`、初期値-1 |
| gauge_percent | int | YES | ゲージ達成率（0〜100%） |
| max_chain | int | YES | 最大連続コンボ数 |
| play_count | int | NO | プレイ回数。`mscore[*][*][3]` |
| clear_count | int | NO | クリア回数 |
| full_combo_count | int | NO | フルコンボ回数 |
| perfect_count | int | NO | パーフェクト回数 |

**制約**:
- PK: (`song_id`, `difficulty`, `modifier_settings`)
- FK: `song_id` → Songs.`song_id`
- CHECK: `score_value >= -1 AND score_value <= 10000000`
- CHECK: `medal IN (-1, 0, 1, 2, 3)`
- CHECK: `difficulty IN (0, 1, 2, 3)`
- CHECK: `play_count >= 0`

**永続化**: `score/[player_name]/[song_folder]/[song_name].ksc`

**modifier_settings の構成**:

| 位置 | 項目 | 値 |
| --- | --- | --- |
| 1 | 効率型 | easy / normal / hard |
| 2 | 譜面変更 | normal / mirror / random / s-random / h-random |
| 3 | 特殊効果 | normal |
| 4 | 短ノート表示 | on / off / auto / hide |
| 5 | 長ノート表示 | on / off / auto / hide |
| 6 | アナログ表示 | on / off / auto / hide |

### 4. GameResults

各プレイの詳細な判定結果を一時的に保持するテーブル。プレイ終了時に計算され、スコア更新時に.kscファイルへ反映される。

**対応コード**: `result.hsp:77-90`

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| total_score | int | NO | 総合スコア |
| critical | int | NO | クリティカル判定数（完璧な判定） |
| near | int | NO | ニア判定数（近い判定） |
| errorn | int | NO | エラー判定数（ミス） |
| critical_short | int | NO | ショートノートのクリティカル数 |
| critical_long | int | NO | ロングノートのクリティカル数 |
| near_short | int | NO | ショートノートのニア数 |
| errorn_short | int | NO | ショートノートのエラー数 |
| errorn_long | int | NO | ロングノートのエラー数 |
| shortnum | int | NO | ショートノート総数 |
| gauge_final | double | NO | 最終ゲージ値 |

**制約**:
- メモリ上のみ（永続化はScoresテーブル経由）
- 1プレイ分のみ保持（次のプレイで上書き）

### 5. Courses

複数曲をまとめたコース情報を管理する論理テーブル。.kcoファイルで定義される。

**対応コード**: `select.hsp:1007-1009`

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| course_id | int | NO (PK) | コースID（配列インデックス） |
| course_name | string(256) | NO | コース名 |
| kco_filename | string(256) | NO | KCOファイル名 |

**制約**:
- PK: `course_id`

**永続化**: `courses/[コース名].kco`

### 6. CourseScores

コース単位のスコア・メダル・グレードを管理する論理テーブル。

**対応コード**: `select.hsp:1007-1009`

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| course_id | int | NO (PK) | コースID。`course_sc`配列インデックス |
| score | int | NO | コース総合スコア（`course_sc[course_id]`） |
| medal | int | NO | コースメダル（`course_md[course_id]`） |
| grade | int | NO | コースグレード（`course_gr[course_id]`） |

**制約**:
- PK: `course_id`
- FK: `course_id` → Courses.`course_id`

**永続化**: `courses/score/[player_name]/[course_name].ksc`

### 7. KeyConfig

キーボード・ジョイパッドの入力設定を管理する論理テーブル。

**対応コード**:
- `kshootmania.hsp:685` (`dim key,14`)
- `kshootmania.hsp:699` (`dim key2,14`)
- `kshootmania.hsp:713` (`dim joykey,2,14`)

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| key_index | int | NO (PK) | キーインデックス（0〜13） |
| key_primary | int | NO | 主キーボードキーコード（`key[key_index]`） |
| key_secondary | int | NO | 副キーボードキーコード（`key2[key_index]`） |
| joy_profile1 | int | NO | ジョイパッドプロファイル1（`joykey[0][key_index]`） |
| joy_profile2 | int | NO | ジョイパッドプロファイル2（`joykey[1][key_index]`） |

**キーインデックス定義**:

| Index | 機能 |
| --- | --- |
| 0 | BT-A（ボタンA） |
| 1 | BT-B（ボタンB） |
| 2 | BT-C（ボタンC） |
| 3 | BT-D（ボタンD） |
| 4 | FX-L（エフェクト左） |
| 5 | FX-R（エフェクト右） |
| 6 | LASER-L（レーザー左回転） |
| 7 | LASER-R（レーザー右回転） |
| 8-10 | 予約 |
| 11 | Enter |
| 12 | Esc |
| 13 | その他 |

**制約**:
- PK: `key_index`
- CHECK: `key_index >= 0 AND key_index <= 13`

**永続化**: `config.ini`（`key=`, `key2=`, `joy=`, `joy2=`行）

### 8. GameConfig

ゲーム全般の設定を管理する論理テーブル。

**対応コード**: `kshootmania.hsp:390-499`

| カラム名 | データ型 | NULL | 説明 |
| --- | --- | --- | --- |
| config_key | string | NO (PK) | 設定キー |
| config_value | string | NO | 設定値 |

**主要設定項目**:

| config_key | 説明 | 型 | 例 |
| --- | --- | --- | --- |
| hispeed | ハイスピード値 | string | `x10`, `C3000`, `m1000` |
| hispeedtype | ハイスピード種別 | string | `x`, `C`, `m`, `a`, `O` |
| fullscr | フルスクリーンモード | int | 0 または 1 |
| windowsize | ウィンドウサイズ | string | `640,480` |
| fullscrsize | フルスクリーンサイズ | string | `1280,720` |
| laserinput | レーザー入力方式 | int | 0〜4 |

**制約**:
- PK: `config_key`

**永続化**: `config.ini`

## 備考

### データ永続化の仕組み

本プロジェクトはRDBMSを使用せず、以下の3種類のファイルでデータを永続化する。

1. **スコアファイル（.ksc）**: テキスト形式。1行が1レコードで、`[難易度設定]=[スコアデータ]`の形式。`score/[player_name]/[song_folder]/[song_name].ksc`に格納される。
2. **譜面ファイル（.ksh）**: テキスト形式。曲のメタデータと譜面データを含む。`songs/[フォルダ]/[ファイル名].ksh`に格納される。
3. **設定ファイル（config.ini）**: INI形式。キー設定・画面設定等を含む。アプリケーション実行ディレクトリに格納される。

### メモリ配列とファイルの対応関係

| メモリ配列 | 定義 | 対応ファイル |
| --- | --- | --- |
| `mscore[SONG_MAX][4][9]` | スコアデータ | .kscファイル |
| `minfo[SONG_MAX][4][5]` | 曲メタデータ（数値） | .kshファイルヘッダ |
| `minfostr[256][SONG_MAX][4][13]` | 曲メタデータ（文字列） | .kshファイルヘッダ |
| `players[notemax]` | プレイヤー名 | `score/`ディレクトリ |
| `key[14]`, `key2[14]`, `joykey[2][14]` | キー設定 | config.ini |
| `course_sc`, `course_md`, `course_gr` | コーススコア | courses/score/*.ksc |

### スコアファイル（.ksc）フォーマット詳細

各行は以下の形式で構成される：

```
[効率型],[譜面変更],[特殊効果],[短ノート],[長ノート],[アナログ]=[スコア],[メダル],[グレード],[ゲージ率],[チェーン数],[プレイ回数],[クリア回数],[フルコンボ回数],[パーフェクト回数]
```

**例**:
```
easy,normal,normal,on,on,on=5234100,3,4,85,2048,5,5,3,1
normal,mirror,normal,on,on,on=6543210,2,3,88,2800,2,1,0,0
```

### バージョン管理とキャッシュ

- 現在のバージョン: `v1.75b`（`currentver="175b"`）
- バージョン更新時に全キャッシュを削除（`kshootmania.hsp:805-808`）
- キャッシュディレクトリ: `cache/songs/`, `cache/bg/`, `cache/gate/jacket/`
