renmori protect
renmori protect は Ren'Py の配布用ビルドを保護済みの配布用ビルドに変換します。game/ 以下のアセットとコンパイル済みスクリプトは暗号化コンテナ game/renmori.rmc に格納され、コンパイル済みスクリプトにはさらにスクリプト層の変換が施されます。平文の元ファイルは削除され、ローダー拡張 game/renmori.rpe とビルドマニフェスト renmori-manifest.json が書き込まれます。ゲームの起動時に Ren'Py がこの拡張を自動で読み込み、拡張がエンジン内部で復号してリソースを供給します。
構文
入力
ビルドディレクトリ
入力ディレクトリは game/ サブディレクトリを直接含んでいる必要があり、含まない場合は終了コード 2 で失敗します。game/ に renmori.rmc がすでに存在する場合は、そのビルドが保護済みであることを意味するため、同じく終了コード 2 で失敗し、二重の保護は行いません。入力には常に、新しい配布用ビルドか保護前に作成したバックアップを使用してください。
配布アーカイブ(.zip)
入力が有効な .zip ファイルの場合、protect はまずそれを展開し、展開結果に対して保護を実行します。
- 出力ディレクトリを決定:
-oを指定した場合はそのディレクトリ、指定しない場合はアーカイブと同じ階層にあるアーカイブ名(拡張子を除く)のディレクトリを使用(例:MyGame-1.0-pc.zipに対してはMyGame-1.0-pc/)、このディレクトリは存在していてはならない - 出力ディレクトリの親ディレクトリに一時ディレクトリを作成して展開。展開前にアーカイブ内の各メンバーのパスを検査し、展開先ディレクトリの外に展開されるメンバー(zip-slip)が 1 つでもあればコマンドは失敗する
- ビルドルートを特定:展開ルート自体が
game/を含む場合はそのまま使用し、含まない場合はトップレベルのサブディレクトリからgame/を含むディレクトリを 1 つだけ探す。これは、ランチャーがビルドを同名のフォルダで 1 階層包む一般的なケースに対応するもので、見つからない場合や複数見つかった場合はコマンドが失敗する - ビルドルートを出力ディレクトリに移動し、その後はインプレース保護と同じ方法でそのディレクトリを処理して、一時ディレクトリを削除
元のアーカイブは常に変更されず原本として残るため、アーカイブから入力した場合は _bak バックアップを別途作成しません。
出力モード
インプレース保護は game/ 以下の平文の元ファイルを削除します。<dir>_bak がすでに存在する場合、コマンドは一切の変更を行う前に終了コード 2 で失敗します。古いバックアップを削除するか、-o を使用するか、バックアップが不要であることを確認したうえで --no-backup を使用してください。バックアップディレクトリには平文のリソースがすべて含まれているため、決して配布用ビルドと一緒にアップロードしないでください。
実行手順
コマンドは以下の順序で実行され、予見可能な衝突はすべて、いずれかのファイルが変更される前にチェックされます。
- 入力の解析:ディレクトリはそのまま使用し、
.zipは展開してからビルドルートを特定 - 事前チェック:
game/が存在し、まだ保護されていないことを確認 - 設定のマージ:
renmori.toml(存在する場合)を読み込み、コマンドラインオプションとマージ。設定ファイルを参照 - 保護強度の決定:強度を指定しない場合はフル保護を使用し、クローズドプラグインが必要。
baselineを指定した場合はオープンソースのベースライン形式を使用し、プラグインをダウンロードしない。autoを指定した場合はプラグインが利用可能ならフル保護、そうでなければベースラインを使用。プラグインの取得方法は保護強度とクローズドプラグインを参照 - 鍵の準備(ベースラインのみ):
[keys]で指定した環境変数からコンテナ鍵とスクリプト鍵を読み込み、指定がない場合や変数が設定されていない場合は、今回のビルド用にランダムな鍵を生成 - 出力先の事前チェック:
-oのディレクトリまたは<dir>_bakがすでに存在するかを確認し、アーカイブ対象のファイル数とスクリプト数を集計。アーカイブ可能なファイルがない場合は失敗 - 概要の表示と確認:入力、モード、ファイル数、スクリプト数、ロックダウンの状態と鍵の取得元を stderr に出力。対話型端末では
[y/N]の確認を求め、yまたはyes以外の回答はすべてキャンセルとなり、キャンセル時は{"status": "aborted"}を出力して終了コード 2 で終了 - コピーまたはバックアップ:
-oモードではビルド全体を出力ディレクトリにコピーし、インプレースモードでは事前に<dir>_bakへコピー(--no-backupの場合はスキップ) - アーカイブ化と暗号化:アーカイブ対象のファイルを 1 つずつ
game/renmori.rmcに書き込み、.rpaアーカイブは分解して各エントリを書き込み、コンパイル済みスクリプトはスクリプト層の変換を経て書き込む - 平文の削除:アーカイブ済みの元ファイルと除去対象の
.rpyソースを削除し、その結果空になったサブディレクトリも削除(--keep-plaintextの場合はスキップ) - ローダー拡張の書き込み:
game/renmori.rpeを生成 - ビルドマニフェストの書き込み:ビルドルートに
renmori-manifest.jsonを生成し、stdout に結果の JSON を出力
クローズドプラグインを使用する場合、手順 5 は実行されず、鍵はプラグイン自身が導出します。手順 8 から 11 はプラグインが実行し、ビルドマニフェストは引き続き renmori が書き込みます。インプレースモードでは、renmori がまず元のビルドをバックアップディレクトリ(--no-backup の場合は一時ディレクトリで、成功後に削除)へ移動し、プラグインがそのディレクトリから読み込んで新しいビルドを書き出します。プラグインが失敗した場合は元のビルドが元の場所に戻され、-o モードで生成された不完全な出力は削除されます。
ファイル処理ルール
protect は game/ 以下のすべてのファイルを走査し、以下のルールで分類します。下表はベースライン保護の処理を示しています。フル保護ではプラグインがアーカイブ化を行いますが、ファイルの選択は同じく exclude と keep_on_disk の設定に従います。
コンテナ内のエントリ名には Ren'Py のゲーム相対パス形式を使用します。区切りはスラッシュで、先頭のスラッシュは付きません(例:images/bg room.png)。実行時、コンテナはローダーチェーンの中で最優先となるため、ディスク上に同名の平文ファイルが残っていても、エンジンが読み込むのは常にコンテナ内のバージョンです。
.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 の順に探し、最初に見つかったものを使用します。いずれも存在しない場合はデフォルト設定を使用します。全設定キーは設定ファイルを参照してください。
保護強度とプラグイン
--strength S
保護強度を設定します。設定キー strength に対応します。
両者の安全性の違いは保護強度とクローズドプラグインを参照してください。
--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 に対応します。このオプションはベースライン保護の処理に作用します。
--keep-plaintext を有効にして生成したビルドには一切の保護効果がなく、renmori verify も平文の残存によりエラーを報告します。このようなビルドは決してリリースしないでください。
出力の制御
--no-hints
保護の成功後に「パッケージ化とリリース」の案内(英語出力では Next - package & publish)を表示しません。デフォルトでは、ビルドの検証、itch.io や直接ダウンロード向けにディレクトリ全体を圧縮すること、SteamPipe でディレクトリの内容をアップロードすること、renmori.rmc と renmori.rpe を必ず併せて配布すること、そしてこのディレクトリに対して Ren'Py ランチャーのビルドを再実行しないことが案内されます。
共通オプション
--lang、--no-color、--quiet も同様に使用できます。CLI の概要を参照してください。
生成されるファイル
実行時には、Ren'Py が game/ 以下に cache/ ディレクトリも生成します。これはエンジンの通常の動作です。
例
コピーを保護し、元のビルドを残す場合:
インプレースで保護し、元のビルドを ./dist/MyGame-1.0-pc_bak にバックアップする場合:
ランチャーが生成した配布アーカイブを直接処理し、結果を ./dist/MyGame-1.0-pc/ に書き込む場合:
CI で非対話的に実行し、JSON のみを出力する場合:
サードパーティのプログラムが直接読み込むファイルを除外し、オープンソースのベースラインを明示的に使用する場合:
ローカルにあるプラグインを使用し、対象プラットフォームを指定する場合:
保護済みディレクトリがそのまま配布用ビルドです。このディレクトリに対して Ren'Py ランチャーの Build Distributions を再度実行しないでください。ランチャーはソースプロジェクトから再ビルドし、すべての保護を失わせます。