# 機能設計書 4-プロジェクト設定

## 概要

本ドキュメントは、Godot Engineにおけるプロジェクト設定機能の詳細設計を記述する。ProjectSettingsは、プロジェクト全体の設定を管理するシングルトンクラスであり、project.godotファイルの読み書き、オートロード管理、グローバル設定へのアクセスを提供する。

### 本機能の処理概要

プロジェクト設定機能は、Godot Engineにおけるプロジェクト固有の設定値の管理、永続化、アクセスを統括するシステムである。ゲームのウィンドウサイズ、入力マッピング、レンダリング設定、オートロードなど、プロジェクト全体に影響する設定を一元管理する。

**業務上の目的・背景**：ゲーム開発では、プロジェクト全体で共有される設定（解像度、フレームレート、入力設定など）を効率的に管理する必要がある。ProjectSettingsは、これらの設定をproject.godotファイルに保存し、エディタとランタイムの両方から一貫したAPIでアクセスできるようにする。また、プラットフォームごとの設定オーバーライドや、機能フラグによる条件付き設定もサポートする。

**機能の利用シーン**：
- プロジェクト起動時の設定読み込み
- エディタでのプロジェクト設定編集
- スクリプトからの設定値取得（GLOBAL_GET）
- オートロードシーン/スクリプトの登録と管理
- プラットフォーム固有の設定オーバーライド
- カスタム設定の追加と管理
- グローバルグループの管理

**主要な処理内容**：
1. project.godotファイルの読み込み・保存
2. 設定値の取得・設定（get_setting、set_setting）
3. 機能オーバーライド（プラットフォーム固有設定）
4. オートロードの管理（add_autoload、remove_autoload）
5. グローバルグループの管理
6. パスのローカライズ・グローバライズ
7. カスタムプロパティ情報の管理
8. 設定変更追跡
9. バイナリ/テキスト形式での保存
10. リソースパックのロード

**関連システム・外部連携**：
- InputMap: 入力設定の読み込み
- FileAccess: 設定ファイルの読み書き
- ResourceUID: リソースUID管理
- OS: プラットフォーム機能検出
- ConfigFile: 設定ファイルパーサー

**権限による制御**：設定はres://project.godotに保存され、プロジェクトディレクトリへの書き込み権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | プロジェクト設定ウィンドウ | 主画面 | 設定の編集・保存 |
| - | オートロードタブ | 参照画面 | オートロード管理 |
| - | エディタ設定 | 参照画面 | エディタ固有設定 |

## 機能種別

エンジンコア機能 / 設定管理 / ファイルI/O

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| p_setting | String | Yes | 設定名（"category/subcategory/name"形式） | 空でないこと |
| p_value | Variant | Yes | 設定値 | - |
| p_default_value | Variant | No | デフォルト値 | - |
| p_path | String | Yes | project.godotファイルパス | 有効なパス |
| p_autoload | AutoloadInfo | Yes | オートロード情報 | name、pathが有効 |

### 設定ファイル形式

project.godotファイルはテキスト形式（ConfigFile互換）:
```ini
[section]
key=value
key.override.platform=override_value
```

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| setting_value | Variant | 設定値 |
| autoload_list | HashMap<StringName, AutoloadInfo> | オートロード一覧 |
| global_groups | HashMap<StringName, String> | グローバルグループ一覧 |
| resource_path | String | プロジェクトルートパス |
| version | uint32_t | 設定バージョン番号 |

### 保存先

- `res://project.godot` - メイン設定ファイル（テキスト形式）
- `res://project.binary` - バイナリ形式（エクスポート時）
- `res://.godot/` - プロジェクトデータディレクトリ

## 処理フロー

### 処理シーケンス

```
1. 初期化 (setup)
   ├─ リソースパック確認
   ├─ project.godot 検索
   │      ├─ 指定パスから開始
   │      └─ 上位ディレクトリを順次検索
   ├─ 設定ファイル読み込み
   │      ├─ テキスト形式 (_load_settings_text)
   │      └─ バイナリ形式 (_load_settings_binary)
   ├─ 機能オーバーライド解析
   ├─ オートロード解析
   └─ 入力マップ初期化

2. 設定取得 (get_setting_with_override)
   ├─ 機能オーバーライド確認
   │      └─ OS::has_feature() でプラットフォーム判定
   ├─ オーバーライド値があれば返却
   └─ 通常値を返却

3. 設定変更 (_set)
   ├─ 値が変更されたか確認
   ├─ 機能オーバーライド登録
   ├─ オートロード/グローバルグループ処理
   ├─ バージョンインクリメント
   └─ 変更通知キュー追加

4. 保存 (save_custom)
   ├─ 設定をセクション別に分類
   ├─ テキスト形式で書き出し
   └─ 保存時刻を記録
```

### フローチャート

```mermaid
flowchart TD
    A[setup呼び出し] --> B{リソースパックあり?}
    B -->|Yes| C[パックをロード]
    B -->|No| D[project.godot検索]
    C --> D

    D --> E{見つかった?}
    E -->|Yes| F[設定読み込み]
    E -->|No| G{上位検索?}
    G -->|Yes| H[親ディレクトリで再検索]
    H --> D
    G -->|No| I[エラー]

    F --> J{バイナリ形式?}
    J -->|Yes| K[_load_settings_binary]
    J -->|No| L[_load_settings_text]
    K --> M[設定解析]
    L --> M

    M --> N[機能オーバーライド解析]
    N --> O[オートロード解析]
    O --> P[入力マップ初期化]
    P --> Q[完了]

    subgraph 設定取得
        R[get_setting_with_override] --> S{オーバーライドあり?}
        S -->|Yes| T[OS::has_feature確認]
        T --> U{機能あり?}
        U -->|Yes| V[オーバーライド値返却]
        U -->|No| W[次のオーバーライド確認]
        W --> S
        S -->|No| X[通常値返却]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 設定名形式 | "category/name"または"category/subcategory/name"形式 | 常時 |
| BR-002 | 機能オーバーライド | "setting.feature"形式で指定、先にマッチしたものを使用 | 常時 |
| BR-003 | オートロードパス | "*"プレフィックスはシングルトンを示す | オートロード設定時 |
| BR-004 | ビルトイン設定順序 | ビルトイン設定は先に表示（order < NO_BUILTIN_ORDER_BASE） | エディタ表示時 |
| BR-005 | 設定変更追跡 | _versionをインクリメントして変更を追跡 | 設定変更時 |
| BR-006 | スレッドセーフ | _THREAD_SAFE_METHOD_マクロで保護 | 常時 |

### 計算ロジック

**パスローカライズ**:
```
if path starts with protocol "xxx://":
    return path (unchanged)
if path is under resource_path:
    return path.replace(resource_path, "res://")
else:
    return path
```

**パスグローバライズ**:
```
if path starts with "res://":
    return path.replace("res://", resource_path)
elif path starts with "uid://":
    resolved_path = ResourceUID::uid_to_path(path)
    return globalize(resolved_path)
elif path starts with "user://":
    return path.replace("user://", OS::get_user_data_dir())
else:
    return path
```

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

本機能はデータベースを使用しない。ファイルシステムのみで動作する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR_FILE_NOT_FOUND | ファイルエラー | project.godotが見つからない | パスを確認 |
| ERR_FILE_CANT_OPEN | ファイルエラー | ファイルを開けない | 権限を確認 |
| ERR_FILE_CORRUPT | ファイルエラー | 設定ファイルが破損 | ファイルを修復 |
| ERR_FAIL_COND | 前提条件エラー | 存在しない設定にアクセス | has_settingで確認 |

### リトライ仕様

ファイル操作のリトライ機構は提供されない。呼び出し側で適切にエラーハンドリングする。

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

本機能はトランザクションを使用しない。ただし、以下の操作は一貫性が保証される:

- `_THREAD_SAFE_METHOD_`マクロによる排他制御
- 設定変更時の`_version`インクリメント

## パフォーマンス要件

- GLOBAL_GET_CACHEDマクロによる高速キャッシュアクセス
- _versionベースの変更検出で不要な再読み込みを回避
- RBMapによる設定の順序付きアクセス

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

- 設定ファイルはプロジェクトディレクトリ内に限定
- エディタオーバーライドは別プレフィックスで管理
- 隠しプレフィックスによる内部設定の保護

## 備考

- CONFIG_VERSION = 5（現在の設定ファイルバージョン）
- NO_BUILTIN_ORDER_BASE = 1 << 16（ビルトイン設定の順序境界）
- project_data_dir_name = ".godot"（プロジェクトデータディレクトリ）

---

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

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

### 推奨読解順序

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

ProjectSettingsの内部データ構造を把握することが、全体理解の基盤となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | project_settings.h | `core/config/project_settings.h` | クラス定義、VariantContainer、AutoloadInfo |

**読解のコツ**:
- `VariantContainer`（74-94行）が各設定の状態を保持
- `AutoloadInfo`（67-71行）がオートロード情報を保持
- `props`（100行）がRBMapで設定を管理

**主要データ構造**:
- **100行**: `RBMap<StringName, VariantContainer> props` - 設定マップ
- **108行**: `HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides` - 機能オーバーライド
- **111行**: `HashMap<StringName, AutoloadInfo> autoloads` - オートロードマップ
- **112行**: `HashMap<StringName, String> global_groups` - グローバルグループ

#### Step 2: 設定の読み込みを理解する

project.godotファイルの読み込み処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | project_settings.cpp | `core/config/project_settings.cpp` | setup、_load_settings_text |

**主要処理フロー**:
- **setup()**: プロジェクトパス検索と設定読み込み
- **_load_settings_text()**: テキスト形式の設定読み込み
- **_load_settings_binary()**: バイナリ形式の設定読み込み

#### Step 3: 設定アクセスを理解する

設定の取得・設定処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | project_settings.cpp | `core/config/project_settings.cpp` | get_setting_with_override、_set、_get |

**主要処理フロー**:
- **410-435行**: `get_setting_with_override()` - 機能オーバーライド付き取得
- **284-375行**: `_set()` - 設定変更処理
- **377-385行**: `_get()` - 設定取得処理

#### Step 4: パス変換を理解する

res://とファイルシステムパスの変換を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | project_settings.cpp | `core/config/project_settings.cpp` | localize_path、globalize_path |

**主要処理フロー**:
- **152-216行**: `localize_path()` - ファイルパス→res://変換
- **261-282行**: `globalize_path()` - res://→ファイルパス変換

#### Step 5: オートロード管理を理解する

オートロードの追加・削除処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | project_settings.cpp | `core/config/project_settings.cpp` | add_autoload、remove_autoload |
| 5-2 | project_settings.cpp | `core/config/project_settings.cpp` | _set内のautoload処理（342-365行） |

**主要処理フロー**:
- オートロード登録: `_set()`内でautoload/で始まる設定を検出
- `*`プレフィックスでシングルトン判定

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

```
ProjectSettings (Object継承、シングルトン)
    │
    ├─ setup(p_path, p_main_pack, p_upwards, p_ignore_override)
    │      ├─ load_resource_pack() [パックがある場合]
    │      ├─ project.godot 検索
    │      │      └─ 上位ディレクトリ走査 (p_upwards時)
    │      ├─ _load_settings_text_or_binary()
    │      │      ├─ _load_settings_text()
    │      │      │      └─ ConfigFile形式のパース
    │      │      └─ _load_settings_binary()
    │      │             └─ バイナリ形式のデシリアライズ
    │      └─ _add_builtin_input_map()
    │
    ├─ get_setting_with_override(p_name)
    │      ├─ feature_overrides.getptr(p_name)
    │      ├─ for each override:
    │      │      └─ OS::has_feature(feature)
    │      └─ props.find(p_name)
    │
    ├─ _set(p_name, p_value)
    │      ├─ 値変更確認
    │      ├─ 機能オーバーライド解析 ("name.feature"形式)
    │      ├─ オートロード処理 ("autoload/"プレフィックス)
    │      │      ├─ "*"プレフィックス検出 (シングルトン)
    │      │      └─ add_autoload()
    │      ├─ グローバルグループ処理 ("global_group/"プレフィックス)
    │      ├─ _version++
    │      └─ _queue_changed(p_name)
    │
    ├─ _get(p_name, r_ret)
    │      └─ props[p_name].variant
    │
    ├─ localize_path(p_path)
    │      ├─ プロトコル検出 ("xxx://")
    │      ├─ DirAccess::change_dir()
    │      └─ resource_path との比較
    │
    ├─ globalize_path(p_path)
    │      ├─ "res://" → resource_path 置換
    │      ├─ "uid://" → ResourceUID解決後に置換
    │      └─ "user://" → OS::get_user_data_dir() 置換
    │
    └─ save_custom(p_path, p_custom, p_custom_features, p_merge)
           ├─ 設定をセクション別に分類
           ├─ _save_settings_text() または _save_settings_binary()
           └─ last_save_time 更新
```

### データフロー図

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

project.godot ─────────────▶ _load_settings_text() ────────▶ props (RBMap)
(テキスト形式)                    │
                                 ▼
                          ConfigFile パース
                                 │
                                 ├─ 設定値 ──────────────────▶ VariantContainer
                                 ├─ "name.feature" ─────────▶ feature_overrides
                                 └─ "autoload/" ────────────▶ autoloads

GLOBAL_GET(name) ─────────▶ get_setting_with_override() ──▶ Variant
                                 │
                                 ├─ feature_overrides確認
                                 │      └─ OS::has_feature()
                                 └─ props[name]

set_setting(name, value) ─▶ _set() ─────────────────────────▶ props更新
                                 │                              │
                                 ├─ _version++ ──────────────▶ 変更検出
                                 └─ _queue_changed() ────────▶ 変更通知

localize_path() ◀─────────▶ パス変換 ◀──────────────────────▶ globalize_path()
  (res://形式)                                                 (絶対パス)

save() ─────────────────────▶ _save_settings_text() ────────▶ project.godot
                                 │
                                 └─ セクション別にソート
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| project_settings.cpp | `core/config/project_settings.cpp` | ソース | ProjectSettingsクラスの実装 |
| project_settings.h | `core/config/project_settings.h` | ヘッダ | ProjectSettingsクラスの定義 |
| config_file.cpp | `core/io/config_file.cpp` | ソース | 設定ファイルパーサー |
| input_map.cpp | `core/input/input_map.cpp` | ソース | 入力マッピング |
| resource_uid.cpp | `core/io/resource_uid.cpp` | ソース | リソースUID管理 |
| os.cpp | `core/os/os.cpp` | ソース | OS機能（has_feature等） |
| dir_access.cpp | `core/io/dir_access.cpp` | ソース | ディレクトリアクセス |
| file_access.cpp | `core/io/file_access.cpp` | ソース | ファイルアクセス |
| file_access_pack.cpp | `core/io/file_access_pack.cpp` | ソース | パックファイルアクセス |
