# 機能設計書 13-サブフォルダ対応

## 概要

本ドキュメントは、K-Shoot MANIA の選曲画面における「サブフォルダ対応」機能の設計を記述する。楽曲フォルダ構造のネスト表示に対応し、親ディレクトリ・サブフォルダ間をナビゲーション可能にする機能である。

### 本機能の処理概要

本機能は、`songs`ディレクトリ配下のフォルダ構造を解析し、楽曲パッケージ(トップレベルフォルダ) > サブフォルダ > 楽曲フォルダの階層構造を曲リストとして表示する。フォルダエントリを選択してEnterキーで階層を下り、BackSpaceキーまたは「<<」エントリで階層を上がるナビゲーションを提供する。

**業務上の目的・背景**：楽曲パッケージが増えると、すべての楽曲がフラットに表示されるのは非効率である。サブフォルダ対応により、パッケージ制作者が意図した階層構造を維持したまま楽曲をブラウズでき、大規模なライブラリでも整理された状態で選曲できる。

**機能の利用シーン**：
- ルートメニューでフォルダを選択してEnter: 該当フォルダ内の楽曲・サブフォルダ一覧を表示
- サブフォルダエントリを選択してEnter: さらにネストしたフォルダの楽曲を表示
- 「<<」エントリを選択またはBackSpaceキー: 親ディレクトリへ戻る

**主要な処理内容**：
1. `*refreshfilelist`サブルーチンでsongs配下のディレクトリ構造を再帰的に走査
2. フォルダ内の`.ksh`ファイル存在チェックと曲リストへの登録
3. サブフォルダ(`.ksh`ファイルを直接含まないディレクトリ)の検出と一覧表示
4. `filelistsubdir`/`filelistsubdirnum`によるサブフォルダ管理
5. `foldername.csv`によるサブフォルダ表示名のカスタマイズ対応

**関連システム・外部連携**：ファイルシステム(songs/ディレクトリ構造)。

**権限による制御**：なし。全プレイヤーが利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | 曲選択画面 | 主画面 | sndirstat配列によるネスト構造のフォルダ表示・BackSpaceキーでの親ディレクトリ移動 |

## 機能種別

ファイルシステム走査 / ナビゲーション処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| dir | string | Yes | 現在のディレクトリパス(""=ルート, フォルダ名=通常) | - |
| dir_default | string | Yes | ゲームのベースディレクトリパス | 存在するパス |
| dirtype | int | Yes | ディレクトリ種別(0:通常, 1:All, 2:Favorite, 3:Courses) | 0-3 |
| sortlist / sortlist2 | int[] | Yes | ソート済みインデックス配列 | - |

### 入力データソース

- ファイルシステム: `songs/`配下のディレクトリ構造
- `conf.ini`: フォルダごとのvolume/offset設定
- `foldername.csv`: サブフォルダ表示名定義

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| minfo | int[SONG_MAX][4][5] | 曲リスト情報配列(フォルダ/楽曲の種別等) |
| minfostr | string[SONG_MAX][4][13] | 曲リスト文字列配列(パス/タイトル等) |
| mnum | int | 曲リストの総項目数 |
| filelistsubdirnum | int | サブフォルダ数 |
| filelist | string | 楽曲ファイルパスの改行区切りリスト |

### 出力先

- メモリ上の配列(minfo, minfostr): 選曲画面の表示に使用
- 画面表示: フォルダ・楽曲の一覧リスト

## 処理フロー

### 処理シーケンス

```
1. *refreshfilelist開始: dir変数の値に応じた処理分岐
   └─ dir="" → ルートメニュー生成
   └─ dir=".\\" → Allフォルダ(dirtype=1)
   └─ dir先頭"?" → Favoriteフォルダ(dirtype=2)
   └─ dir先頭".." → Coursesフォルダ(dirtype=3)
   └─ その他 → 通常フォルダ(dirtype=0)
2. 通常/Allフォルダの場合:
   a. notesel filelistdir2でsongs直下のフォルダリストを取得
   b. 各フォルダについてconf.iniを読み込み(volume/offset)
   c. foldername.csvがあればサブフォルダ表示名を取得
   d. dirlistで各フォルダ内を走査:
      ├─ .kshファイルが存在 → filelistに追加
      └─ .kshファイルなし → filelistsubdirに追加(サブフォルダ)
   e. サブフォルダをさらに走査:
      └─ 各サブフォルダ内のフォルダを走査し.kshファイルを検出
3. 楽曲ごとのメタデータ解析(KSHLib_GetChartMetaDataValue or 直接パース)
4. ソート処理(通常順/レベル順)
5. minfo/minfostr配列への登録
```

### フローチャート

```mermaid
flowchart TD
    A[*refreshfilelist開始] --> B{dirの値は?}
    B -->|空| C[ルートメニュー生成]
    B -->|通常フォルダ| D[dirtype=0で処理]
    B -->|".\\"|  E[Allモード dirtype=1]
    D --> F[conf.ini読み込み]
    E --> F
    F --> G[foldername.csv読み込み]
    G --> H[フォルダ内走査]
    H --> I{.kshファイルあり?}
    I -->|Yes| J[filelistに追加]
    I -->|No| K[filelistsubdirに追加]
    J --> L[サブフォルダ走査]
    K --> L
    L --> M[サブフォルダ内の楽曲を走査]
    M --> N[メタデータ解析・minfo登録]
    C --> O[All/Courses/Favoriteエントリ + フォルダエントリ生成]
    N --> P[終了]
    O --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-13-1 | サブフォルダ検出 | .kshファイルを直接含まないフォルダをサブフォルダとして扱う | フォルダ走査時 |
| BR-13-2 | 2階層ネスト | songs/[パッケージ]/[サブフォルダ]/[楽曲フォルダ]/の3階層まで対応 | 常時 |
| BR-13-3 | ソート済み表示 | フォルダ名を小文字化してアルファベット順にソート(sortnote) | 常時 |
| BR-13-4 | foldername.csv | CSV形式で「フォルダ名,表示名」を定義し、サブフォルダの表示名をカスタマイズ可能 | foldername.csvが存在する場合 |
| BR-13-5 | conf.ini | フォルダごとにvolume/offset/cache/tweet_optionを設定可能 | conf.iniが存在する場合 |
| BR-13-6 | サブフォルダ区切り | サブフォルダの切り替わりに区切りエントリ(minfo.*.0.0=-3)を挿入 | サブフォルダが存在する場合 |

### 計算ロジック

なし。

## データベース操作仕様

### 操作別データベース影響一覧

本機能ではデータベース操作は行わない。ファイルシステムの読み取りのみ。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | フォルダ不存在 | songs配下のフォルダが存在しない場合 | dirlist結果が空のため、該当フォルダをスキップ |
| - | safeChdirC失敗 | 特殊文字を含むパスへの移動失敗 | safeChdirCがfalseを返し、該当フォルダをスキップ |

### リトライ仕様

なし。

## トランザクション仕様

トランザクション管理は不要。ファイルシステムの読み取り操作のみ。

## パフォーマンス要件

- 全楽曲フォルダの走査は選曲画面初期化時に一括実行される
- SONG_MAX(上限値)を超える楽曲数の場合、配列オーバーフローが発生する可能性がある
- dircacheフラグによるキャッシュ制御が可能

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

特になし。ローカルファイルシステムの読み取り操作のみ。

## 備考

- `sndirstat`配列はsplit命令でパスを分割して各階層の名前を取得するために使用される
- Allモード(`dir=".\\"`/dirtype=1)では全フォルダを横断して楽曲を一覧表示する
- `filelistsubdirnum`はサブフォルダ数のカウントに使用され、カーソル移動時のフォルダ境界判定にも利用される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | select.hsp | `src/scene/select.hsp` | minfo配列のフォルダ種別値(-1:フォルダ, -2:戻る, -3:区切り, -4:All, -5:All戻る, -6:Favorite, -7:Favorite戻る, -8:Courses, -9:Courses戻る, 0-3:楽曲, 4:コース) |
| 1-2 | select.hsp | `src/scene/select.hsp` | filelistsubdir(サブフォルダ名リスト)、filelistsubdirnum(サブフォルダ数)の管理構造 |

**読解のコツ**: HSPのnotesel/notegetは文字列バッファを行単位で操作する命令。dirlistはディレクトリ内容を改行区切りの文字列として返す。

#### Step 2: ルートメニュー生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | select.hsp | `src/scene/select.hsp` | 2579-2628行目のdir=""(ルート)時の処理 |

**主要処理フロー**:
1. **2581-2584行目**: Allエントリの生成(hidealldir=0かつフォルダ2つ以上)
2. **2586-2592行目**: Coursesエントリの生成(.kcoファイルが存在する場合)
3. **2595-2616行目**: Favoriteエントリの生成(.favファイル分)
4. **2618-2626行目**: 通常フォルダエントリの生成

#### Step 3: 通常/Allフォルダの走査を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | select.hsp | `src/scene/select.hsp` | 2838-2968行目の通常/Allフォルダ走査処理 |

**主要処理フロー**:
- **2842-2861行目**: conf.ini読み込み(volume/offset/cache)
- **2864-2891行目**: foldername.csv読み込み(UTF-8 BOM対応)
- **2893-2924行目**: フォルダ内走査(.kshファイルの有無でサブフォルダ判定)
- **2905-2915行目**: .kshファイルなしのフォルダをfilelistsubdirに追加
- **2927-2929行目**: サブフォルダ名のソート(小文字化してアルファベット順)
- **2937-2968行目**: サブフォルダ内のさらにネストしたフォルダを走査

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

```
*refreshfilelist (2555行目)
    |
    +-- [ルートメニュー生成] (2579-2628行目)
    |       +-- All / Courses / Favorite / フォルダエントリ生成
    |
    +-- [Favoriteフォルダ処理] (2671-2835行目, dirtype=2)
    |
    +-- [Coursesフォルダ処理] (2659-2670行目, dirtype=3)
    |
    +-- [通常/Allフォルダ処理] (2838-2968行目)
    |       +-- conf.ini読み込み
    |       +-- foldername.csv読み込み
    |       +-- フォルダ走査(dirlist "*.ksh")
    |       +-- サブフォルダ検出(filelistsubdir)
    |       +-- サブフォルダ内走査(二重ループ)
    |
    +-- [コースファイル処理] (2971-3027行目)
    |
    +-- [楽曲メタデータ解析] (3029行目以降)
```

### データフロー図

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

songs/ディレクトリ構造 ------> dirlist命令でフォルダ走査 ----> filelistdir
                                    |
songs/[dir]/conf.ini --------> noteload/パース ------------> dirvol/diroffset
                                    |
songs/[dir]/foldername.csv --> csvstr2パース ----------------> subnamelist
                                    |
*.kshファイル検出 -----------> .ksh有無判定 -------+--------> filelist(楽曲パスリスト)
                                                   |
.kshなしフォルダ -------> filelistsubdir追加 ----> filelistsubdir(サブフォルダリスト)
                                    |
                            minfo/minfostr配列登録 ---------> 曲リスト表示データ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| select.hsp | `src/scene/select.hsp` | ソース | *refreshfilelistサブルーチンでフォルダ構造走査・曲リスト生成 |
| conf.ini | `songs/[パッケージ名]/conf.ini` | 設定 | フォルダごとのvolume/offset/cache設定 |
| foldername.csv | `songs/[パッケージ名]/foldername.csv` | 設定 | サブフォルダ表示名のカスタマイズ定義 |
| filesystem_utils.as | `src/filesystem_utils.as` | ソース | safeChdirC関数による特殊文字対応ディレクトリ移動 |
