> For AI agents: the complete documentation index is available at https://renmori.mewbaka.cn/ja/llms.txt, the full documentation bundle is available at https://renmori.mewbaka.cn/ja/llms-full.txt.

# renmori protect

`renmori protect` は Ren'Py の配布用ビルドを保護済みの配布用ビルドに変換します。`game/` 以下のアセットとコンパイル済みスクリプトは暗号化コンテナ `game/renmori.rmc` に格納され、コンパイル済みスクリプトにはさらにスクリプト層の変換が施されます。平文の元ファイルは削除され、ローダー拡張 `game/renmori.rpe` とビルドマニフェスト `renmori-manifest.json` が書き込まれます。ゲームの起動時に Ren'Py がこの拡張を自動で読み込み、拡張がエンジン内部で復号してリソースを供給します。

## 構文

```bash
renmori protect <game-dir | game.zip> [options]
```

| 引数         | 説明                                                                   |
| ---------- | -------------------------------------------------------------------- |
| `game-dir` | 配布用ビルドのルートディレクトリ、すなわち `game/` サブディレクトリを直接含むディレクトリ（`game/` 自体は指定しない）  |
| `game.zip` | Ren'Py ランチャーが生成した配布アーカイブ（例：`MyGame-1.0-pc.zip`、`MyGame-1.0-mac.zip`） |

## 入力

### ビルドディレクトリ

入力ディレクトリは `game/` サブディレクトリを直接含んでいる必要があり、含まない場合は終了コード 2 で失敗します。`game/` に `renmori.rmc` がすでに存在する場合は、そのビルドが保護済みであることを意味するため、同じく終了コード 2 で失敗し、二重の保護は行いません。入力には常に、新しい配布用ビルドか保護前に作成したバックアップを使用してください。

### 配布アーカイブ（.zip）

入力が有効な `.zip` ファイルの場合、`protect` はまずそれを展開し、展開結果に対して保護を実行します。

1. 出力ディレクトリを決定：`-o` を指定した場合はそのディレクトリ、指定しない場合はアーカイブと同じ階層にあるアーカイブ名（拡張子を除く）のディレクトリを使用（例：`MyGame-1.0-pc.zip` に対しては `MyGame-1.0-pc/`）、このディレクトリは存在していてはならない
2. 出力ディレクトリの親ディレクトリに一時ディレクトリを作成して展開。展開前にアーカイブ内の各メンバーのパスを検査し、展開先ディレクトリの外に展開されるメンバー（zip-slip）が 1 つでもあればコマンドは失敗する
3. ビルドルートを特定：展開ルート自体が `game/` を含む場合はそのまま使用し、含まない場合はトップレベルのサブディレクトリから `game/` を含むディレクトリを 1 つだけ探す。これは、ランチャーがビルドを同名のフォルダで 1 階層包む一般的なケースに対応するもので、見つからない場合や複数見つかった場合はコマンドが失敗する
4. ビルドルートを出力ディレクトリに移動し、その後はインプレース保護と同じ方法でそのディレクトリを処理して、一時ディレクトリを削除

元のアーカイブは常に変更されず原本として残るため、アーカイブから入力した場合は `_bak` バックアップを別途作成しません。

## 出力モード

| モード               | 発動条件                      | 元のビルド        | バックアップ                    |
| ----------------- | ------------------------- | ------------ | ------------------------- |
| インプレース保護（デフォルト）   | `-o` 未指定、入力がディレクトリ        | 保護済みビルドに書き換え | 事前に同じ階層の `<dir>_bak` へコピー |
| インプレース保護、バックアップなし | `-o` 未指定、`--no-backup` 指定 | 保護済みビルドに書き換え | 作成しない、操作は取り消し不可           |
| 新しいディレクトリへ出力      | `-o OUTPUT` 指定            | 変更されない       | 不要、`OUTPUT` は存在していてはならない  |
| アーカイブから保護         | 入力が `.zip`                | アーカイブは変更されない | 不要、アーカイブが原本               |

:::warning
インプレース保護は `game/` 以下の平文の元ファイルを削除します。`<dir>_bak` がすでに存在する場合、コマンドは一切の変更を行う前に終了コード 2 で失敗します。古いバックアップを削除するか、`-o` を使用するか、バックアップが不要であることを確認したうえで `--no-backup` を使用してください。バックアップディレクトリには平文のリソースがすべて含まれているため、決して配布用ビルドと一緒にアップロードしないでください。
:::

## 実行手順

コマンドは以下の順序で実行され、予見可能な衝突はすべて、いずれかのファイルが変更される前にチェックされます。

1. **入力の解析**：ディレクトリはそのまま使用し、`.zip` は展開してからビルドルートを特定
2. **事前チェック**：`game/` が存在し、まだ保護されていないことを確認
3. **設定のマージ**：`renmori.toml`（存在する場合）を読み込み、コマンドラインオプションとマージ。[設定ファイル](https://renmori.mewbaka.cn/ja/cli/configuration.md)を参照
4. **保護強度の決定**：強度を指定しない場合はフル保護を使用し、クローズドプラグインが必要。`baseline` を指定した場合はオープンソースのベースライン形式を使用し、プラグインをダウンロードしない。`auto` を指定した場合はプラグインが利用可能ならフル保護、そうでなければベースラインを使用。プラグインの取得方法は[保護強度とクローズドプラグイン](https://renmori.mewbaka.cn/ja/cli/plugin.md)を参照
5. **鍵の準備**（ベースラインのみ）：`[keys]` で指定した環境変数からコンテナ鍵とスクリプト鍵を読み込み、指定がない場合や変数が設定されていない場合は、今回のビルド用にランダムな鍵を生成
6. **出力先の事前チェック**：`-o` のディレクトリまたは `<dir>_bak` がすでに存在するかを確認し、アーカイブ対象のファイル数とスクリプト数を集計。アーカイブ可能なファイルがない場合は失敗
7. **概要の表示と確認**：入力、モード、ファイル数、スクリプト数、ロックダウンの状態と鍵の取得元を stderr に出力。対話型端末では `[y/N]` の確認を求め、`y` または `yes` 以外の回答はすべてキャンセルとなり、キャンセル時は `{"status": "aborted"}` を出力して終了コード 2 で終了
8. **コピーまたはバックアップ**：`-o` モードではビルド全体を出力ディレクトリにコピーし、インプレースモードでは事前に `<dir>_bak` へコピー（`--no-backup` の場合はスキップ）
9. **アーカイブ化と暗号化**：アーカイブ対象のファイルを 1 つずつ `game/renmori.rmc` に書き込み、`.rpa` アーカイブは分解して各エントリを書き込み、コンパイル済みスクリプトはスクリプト層の変換を経て書き込む
10. **平文の削除**：アーカイブ済みの元ファイルと除去対象の `.rpy` ソースを削除し、その結果空になったサブディレクトリも削除（`--keep-plaintext` の場合はスキップ）
11. **ローダー拡張の書き込み**：`game/renmori.rpe` を生成
12. **ビルドマニフェストの書き込み**：ビルドルートに `renmori-manifest.json` を生成し、stdout に結果の JSON を出力

クローズドプラグインを使用する場合、手順 5 は実行されず、鍵はプラグイン自身が導出します。手順 8 から 11 はプラグインが実行し、ビルドマニフェストは引き続き `renmori` が書き込みます。インプレースモードでは、`renmori` がまず元のビルドをバックアップディレクトリ（`--no-backup` の場合は一時ディレクトリで、成功後に削除）へ移動し、プラグインがそのディレクトリから読み込んで新しいビルドを書き出します。プラグインが失敗した場合は元のビルドが元の場所に戻され、`-o` モードで生成された不完全な出力は削除されます。

## ファイル処理ルール

`protect` は `game/` 以下のすべてのファイルを走査し、以下のルールで分類します。下表はベースライン保護の処理を示しています。フル保護ではプラグインがアーカイブ化を行いますが、ファイルの選択は同じく `exclude` と `keep_on_disk` の設定に従います。

| ファイル                                      | 処理方法                                                                                |
| ----------------------------------------- | ----------------------------------------------------------------------------------- |
| 画像、音声、動画、フォント、翻訳などのアセット                   | コンテナに格納し、平文の元ファイルを削除                                                                |
| `.rpyc`、`.rpymc` コンパイル済みスクリプト             | スクリプト層の変換を経てコンテナに格納し、平文の元ファイルを削除                                                    |
| `.rpy`、`.rpym` ソースで、同名のコンパイル済みファイルが存在するもの | アーカイブせず直接削除。エンジンはコンパイル済みスクリプトのみを実行し、配布用ビルドにソースを同梱すると、一度でも復号されれば元のシナリオがそのまま得られてしまうため |
| `.rpy`、`.rpym` ソースで、コンパイル済みファイルが存在しないもの   | 内容が失われないよう、コンテナに格納                                                                  |
| `.rpa` アーカイブ（RPA-3.0、RPA-2.0）             | 分解して各エントリを個別のファイルとしてコンテナに格納し、アーカイブを削除。ディスク上の同名の単独ファイルと衝突した場合は、先に検出された方を保持           |
| `game/saves/`                             | ディレクトリ全体をスキップし、セーブデータはそのまま                                                          |
| `game/cache/` 内の `.rpyb`                  | コンテナに格納（スクリプトから派生したコンパイル済みキャッシュのため）                                                 |
| `game/cache/` 内のその他のファイル                  | スキップしてそのまま保持（例：`shaders.txt`、`build_info.json`）                                     |
| `steam_appid.txt`                         | デフォルトで暗号化せずディスク上に保持。Steam SDK は Ren'Py のローダーを経由せず、このファイルを直接読み込むため                   |
| `exclude` または `keep_on_disk` に一致するファイル    | 暗号化せずディスク上に保持                                                                       |
| シンボリックリンクおよび通常ファイル以外                      | スキップ                                                                                |

コンテナ内のエントリ名には Ren'Py のゲーム相対パス形式を使用します。区切りはスラッシュで、先頭のスラッシュは付きません（例：`images/bg room.png`）。実行時、コンテナはローダーチェーンの中で最優先となるため、ディスク上に同名の平文ファイルが残っていても、エンジンが読み込むのは常にコンテナ内のバージョンです。

:::danger
`.rpy`、`.rpyc`、`.rpym`、`.rpymc` などのスクリプトファイルを `exclude` で除外しないでください。ランタイムのローダーは、デコンパイラの注入を防ぐため、`game/` 内でディスク上にあるスクリプト系ファイルを開くことを拒否します。そのため、除外したスクリプトは読み込めなくなります。`exclude` は、エンジン以外のプログラムが読み込むファイルにのみ使用してください。
:::

## オプション

### 出力とバックアップ

#### `-o, --output DIR`

保護済みビルドを `DIR` に書き込み、元のビルドは変更しません。`DIR` は存在していてはならず、存在する場合は終了コード 2 で失敗します。親ディレクトリが存在しない場合は自動で作成されます。このモードではバックアップを作成しません。入力が `.zip` の場合、`DIR` は展開して保護する先のディレクトリとなります。

#### `--no-backup`

インプレース保護時に `<dir>_bak` バックアップを作成せず、元のビルドを直接書き換えます。この操作は取り消せないため、CI で使い捨てのビルドディレクトリに対してのみ使用することを推奨します。`-o` を指定した場合や入力が `.zip` の場合、このオプションは効果を持ちません。

#### `-y, --yes`

保護前の確認プロンプトと、クローズドプラグインの初回ダウンロード時の確認プロンプトをスキップします。stdin が端末でない場合や `--quiet` を指定した場合も、プロンプトはスキップされます。

### 設定

#### `-c, --config FILE`

`FILE` から設定を読み込みます。ファイルが存在しない場合は終了コード 2 で失敗します。指定しない場合は `<game-dir>/renmori.toml`、カレントディレクトリの `renmori.toml` の順に探し、最初に見つかったものを使用します。いずれも存在しない場合はデフォルト設定を使用します。全設定キーは[設定ファイル](https://renmori.mewbaka.cn/ja/cli/configuration.md)を参照してください。

### 保護強度とプラグイン

#### `--strength S`

保護強度を設定します。設定キー `strength` に対応します。

| 値               | 動作                                                                             |
| --------------- | ------------------------------------------------------------------------------ |
| 指定なし（デフォルト）     | フル保護：クローズドプラグインが宣言する最も強い強度を使用。プラグインが利用できない場合は終了コード 2 で失敗し、ベースラインへの自動的な格下げは行わない |
| `baseline`      | オープンソースのベースライン形式：固定のコンテナ形式と同梱のプレースホルダー鍵を使用し、プラグインのダウンロードも呼び出しも行わない             |
| `auto`          | プラグインが利用可能ならその最も強い強度、そうでなければベースラインを使用                                          |
| プラグインが宣言するその他の値 | プラグインに処理を委ね、プラグインが利用できない場合やその値に対応していない場合は終了コード 2 で失敗                           |

両者の安全性の違いは[保護強度とクローズドプラグイン](https://renmori.mewbaka.cn/ja/cli/plugin.md)を参照してください。

#### `--plugin PATH`

`PATH` にあるクローズドプラグインを使用し、キャッシュの検索や自動ダウンロードは行いません。`RENMORI_PLUGIN` の設定と同等で、コマンドライン引数が優先されます。パスが存在しない場合はその旨を表示してこの引数を無視し、キャッシュの検索または自動ダウンロードを続行します。`PATH` には実行ファイルのほか、現在の Python インタープリタで実行される `.py` スクリプトも指定できます。

#### `--platforms LIST`

プラグインでのみ有効です。カンマ区切りの対象プラットフォームのリストで（例：`win-x64,linux-x64,mac-universal`）、設定キー `platforms` に対応します。指定しない場合は、プラグインがビルド内の `lib/py3-*` ディレクトリから自動検出します。

#### `--launcher`

プラグインでのみ有効です。鍵をネイティブランチャーに紐付けてビルドと共に配布し、そのランチャーからゲームを起動した場合にのみ鍵が再構築されるようにします。設定キー `launcher` に対応します。

### 暗号化パラメータ

#### `--chunk-size N`

コンテナのチャンクサイズ（単位はバイト）で、設定キー `chunk_size` に対応し、デフォルトは 65536 です。各チャンクは個別に暗号化され、16 バイトの認証タグが付加されます。大きなチャンクは大容量の CG や動画に有利であり、小さなチャンクは多数の小さなファイルによる追加オーバーヘッドを抑えます。このパラメータはベースラインコンテナのチャンクサイズを決定するもので、フル保護のチャンクパラメータはプラグインがビルドごとに決定します。

#### `--exclude GLOB`

`GLOB` に一致するファイルを暗号化せずディスク上に保持します。複数回指定できます。コマンドラインのパターンは設定ファイルの `exclude` と `keep_on_disk` の後ろに追加され、それらを置き換えることはありません。

マッチングのルール：

- パターンはスラッシュ区切りのゲーム相対パスに対して照合される（例：`thirdparty/*` は `game/thirdparty/` 以下のファイルに一致）
- Python の `fnmatch` のセマンティクスを使用：`*` は任意の文字に一致し、ディレクトリ区切りもまたぐ。`?` は 1 文字、`[seq]` は文字集合に一致
- Windows では大文字と小文字を区別せず、macOS と Linux では区別する
- シェルによる事前展開を避けるため、各パターンを引用符で囲むことを推奨（例：`--exclude "thirdparty/*"`）

### ランタイムの動作

#### `--no-lockdown`

保護済みビルドで開発者機能を維持します。デフォルトで有効なロックダウンは、実行時に以下を行います。

- `config.developer` と `config.console` を無効化し、開発者モードと開発者コンソールを使用不可にする
- スクリプトやテキストデータをエクスポートできる Ren'Py のコマンドラインサブコマンド `compile`、`dialogue`、`translate`、`add_from`、`merge_strings` の実行を拒否

設定キー `lockdown` に対応します。リリース版ではロックダウンを有効のままにしてください。

#### `--keep-plaintext`

アーカイブ化の完了後に平文の元ファイルを削除せず、除去対象の `.rpy` ソースも削除しません。問題の調査専用です。設定キー `keep_plaintext` に対応します。このオプションはベースライン保護の処理に作用します。

:::danger
`--keep-plaintext` を有効にして生成したビルドには一切の保護効果がなく、`renmori verify` も平文の残存によりエラーを報告します。このようなビルドは決してリリースしないでください。
:::

### 出力の制御

#### `--no-hints`

保護の成功後に「パッケージ化とリリース」の案内（英語出力では `Next - package & publish`）を表示しません。デフォルトでは、ビルドの検証、itch.io や直接ダウンロード向けにディレクトリ全体を圧縮すること、SteamPipe でディレクトリの内容をアップロードすること、`renmori.rmc` と `renmori.rpe` を必ず併せて配布すること、そしてこのディレクトリに対して Ren'Py ランチャーのビルドを再実行しないことが案内されます。

#### 共通オプション

`--lang`、`--no-color`、`--quiet` も同様に使用できます。[CLI の概要](https://renmori.mewbaka.cn/ja/cli/overview.md#%E5%85%B1%E9%80%9A%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3)を参照してください。

## 生成されるファイル

| パス                      | 説明                                                                                                                                      |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `game/renmori.rmc`      | 暗号化コンテナ。アーカイブ化されたすべてのアセットと変換済みのスクリプトを格納                                                                                                 |
| `game/renmori.rpe`      | ローダー拡張。Ren'Py の起動時に自動で読み込まれ、エンジン内部での復号とリソースの供給を担当                                                                                       |
| `renmori-manifest.json` | ビルドマニフェスト。ツールのバージョン、コンテナのハッシュ、ファイル数などを記録し、`renmori verify` が使用（形式は[出力・ビルドマニフェスト・終了コード](https://renmori.mewbaka.cn/ja/cli/output.md)を参照） |
| `<dir>_bak/`            | インプレース保護時に生成される完全な平文のバックアップで、配布用ビルドには含めない                                                                                               |

実行時には、Ren'Py が `game/` 以下に `cache/` ディレクトリも生成します。これはエンジンの通常の動作です。

## 例

コピーを保護し、元のビルドを残す場合：

```bash
renmori protect ./dist/MyGame-1.0-pc -o ./dist/MyGame-1.0-pc-protected
```

インプレースで保護し、元のビルドを `./dist/MyGame-1.0-pc_bak` にバックアップする場合：

```bash
renmori protect ./dist/MyGame-1.0-pc
```

ランチャーが生成した配布アーカイブを直接処理し、結果を `./dist/MyGame-1.0-pc/` に書き込む場合：

```bash
renmori protect ./dist/MyGame-1.0-pc.zip
```

CI で非対話的に実行し、JSON のみを出力する場合：

```bash
renmori protect ./build/MyGame-pc --no-backup --quiet -y
```

サードパーティのプログラムが直接読み込むファイルを除外し、オープンソースのベースラインを明示的に使用する場合：

```bash
renmori protect ./dist/MyGame-1.0-pc -o ./out --strength baseline --exclude "thirdparty/*"
```

ローカルにあるプラグインを使用し、対象プラットフォームを指定する場合：

```bash
renmori protect ./dist/MyGame-1.0-pc -o ./out --plugin ./renmori-plugin-linux-x64 --platforms win-x64,linux-x64
```

:::warning
保護済みディレクトリがそのまま配布用ビルドです。このディレクトリに対して Ren'Py ランチャーの _Build Distributions_ を再度実行しないでください。ランチャーはソースプロジェクトから再ビルドし、すべての保護を失わせます。
:::
