Modについてのあれこれ。FoModの作り方、Mod導入時のトラブル事例などのニッチな話を書いていきます。Skyrim概要ゲームをある程度進めると、セーブしたデータのロード時に確定CTDが発生する(少し前のセーブデータは問題なくロードできる)。原因もしかしたらPapyrus文字列数上限65535問題に該当しているかもしれません。この問題に該当する場合、そのセーブデータは破損しており、絶対にロードすることはできません。Skyrimのセーブデータ(.ESS)には、Papyrusスクリプトの使用する文字列を格納する文字列テーブルがあります。shortで表現されており、65535(0xffff)が上限となります。Papyrus文字列数の上限を超過しても、ある程度はプレイ続行でき、セーブすることも可能ですが、一度ゲームを再起動した後にそのセーブデータをロードすると確定CTDします(必要な文字列がすべて含まれていない不正なセーブファイルとなるようです)。上限を超過した状態でプレイを続行するのも問題があるようです(参考記事をざっと見た感じ、一見問題がないように見えるが内部的には文字列テーブルがおかしくなっている可能性ありとのこと)。参考記事によると、実験の結果、Skyrimのエンジンそのものは65535を超える文字を取り扱えるようであり、セーブデータの形式だけの問題らしいとのこと。Hadoramでセーブデータを開いたとき、ツールがアプリケーションエラーとなる場合、本問題に該当する可能性が高いです。fubrusを使って文字列数を正確に把握できます。ツールを起動し「Open」ボタンを押してセーブデータを開きます。3」を展開し、その下位にある「2.Papyrus」をクリックします。右のツリーに表示されるStrings(数字)が現在の文字列数を示しています。65535からこの数字を引いたものが上限までの猶予となります。error」ダイアログが表示された場合、既に文字列数上限を超過しています。文字列数は状況によって1,000~2,000くらい増減することがあるようです。文字列数が超過していなくても63,000以上であれば「以前気付かぬうちに上限を超していた可能性がある」と考えたほうがいいでしょう。ロードCTDするのに文字列数に余裕がある場合、残念ながら文字列数が原因ではありません。次のブログ記事を参考に他の原因を探ってください。Skyrimshot(tktkさん):スカイリムのCTD対策Skyrim箱庭DIY(くつみやさん):SkyrimでよくあるCTDの原因まとめ(ロード時に確定CTD)拙作のStrCountToolを使う方法(まとめてチェック)複数のセーブファイルを纏めてチェックできるツールを作成してみました。v8の拡張形式にも対応しています。Skeever実行例:ファイルが壊れている場合、赤枠のようにエラーメッセージが表示されます。Officeなどの表計算ソフトを使って表示することを想定していますので列が揃いませんがご勘弁を。対策方法ベゼスダがファイル形式を改善するのが一番ですが、Skyrimでは期待できないでしょう。Fallout4でも同じ問題があるのか気になるところですね。Shesonのv1.47以前は文字列テーブルを大量に消費しますが、v1.48以上にアップグレードすることで消費を大幅に削減できます(下記に実績を書いておきました)。1.61は文字列テーブルを大量に消費しますが、markdf氏のJContainersパッチを使って消費を大幅に削減できるそうです(要JContainer3.3)。v8に文字列数超過時の対策が追加されました。情報:スクリプトが65535より多い異なる文字列を使うと、セーブデータは破損しロードできません。これは文字数が65520より大きい場合のファイル保存形式をわずかに変更することにより修正します。逆もまた真なりで、文字数を65520以下にして再びセーブするとバニラの保存形式に戻ります。アドレス:大体30箇所のコードにパッチを当てた。v8のインストールされていない環境やセーブデータを扱う各種ツールでロードできなくなることに注意してください。●markdf氏によりRestringerの開発が進められています(テスト版あり)・PEXファイル中の文字列をJsonファイルに吐き出し、Jsonファイル中の文字列を参照するようPEXファイルを書き換えることにより、文字数を減らすアプローチです。・ENDPOINT(末端MOD)に対してパッチを当てることを想定。Lanternは末端MODである。和訳を交えて解説します。参考記事によると、文字列テーブルには次のものが格納されるとのことです。関数が利用するローカル変数の定義。通常、関数が終了した後にテーブルから削除されるが、MCM関連の変数の一部は保持され続けるようです。スクリプトプロパティ。文字列テーブルの大半はプロパティのようです。(筆者注:スクリプトの文字列プロパティの代表例はMCMのページ名です。ゲーム進行途中でMCMページ名の訳を変更するとMCMページが表示されなくなるのはこれに起因します)スクリプト/フラグメント名(.pex含まず)。これらのスクリプト名はプロパティと同様のようです。文字列テーブルに含まれるスクリプト名が極めて少ないことがこれを説明しています。文字列がすべてセーブデータに保存されるわけではなく、現在のセーブで使われているスクリプトの文字列が保存されるようです。従って、インストールしたMODの数や種類によっても変化しますし、ゲームの進行状況に応じたスクリプト環境の変化によっても増減します。いうMODが文字列数に悪影響を及ぼすのか(参考記事の和訳)確実ではないですが、一般的な経験則によれば、スクリプトが多いほどプロパティの数も多くなります。NPCs)が大きな影響を及ぼす理由をうまく説明しています。私は会話系MODの専門化ではないですが、クエストステージを進めるためだけにスクリプトが必要となるようです。私に言わせれば、MOD制作者がロールプレイの会話を追加することで16ビットの制限を圧迫するベゼスダのやり方はとてもとてもマヌケに思えます。大規模で複雑なMCMを持つMOD。MCMの各項目(スライダー、トグルボタンなど)それぞれに対して変数定義が必要です。文字列テーブルの内容を見ると"OID"を含みMOD作者の慣例に従ったネーミング規則を持つ変数名が大量にありました。MODが無意味なMCMを追加するのを見て私はため息をつきます。シンプルなままでいいよ。沢山のプロパティが付いたスクリプト。プロパティは文字列テーブルの数に大きな影響を与えるようです。Skyrim"をスクリプト版からクラフト版にダウングレードしました。スクリプト版ではスクリプト1つあたり230ほどのプロパティがあります。スクリプト版の方がはるかにエレガントで適切な実装ですが、クラフト版にはまったくスクリプトが含まれていません。アニメーションMOD。Lab)」のユーザーで問題になります。アニメーション毎にいくつかの文字列が追加されることが報告されています。loadでは、解決方法に関する議論や各MODの使用文字列数調査が継続して行われています。OP(最初の書き込み)にはMODの使用文字列数をまとめたgoogleスプレッドシートへのリンクもありますので参考にしてください(うっかり更新しないようにご注意を)。v8を使って実際に文字列数が65535を超過したセーブデータを作成し、その仕組みについて調査してみました。v8の仕組みについて説明します。Papyrusにおける文字管理方式(PEXファイルの場合)次のスクリプトをコンパイルしてPEXファイルを作成したとします。money")EndEventPEXファイルには次のような情報が保存されます。保存形式はバイナリ形式ですし保存される情報はもっと多いですが、分かりやすいように簡略化しています。(3))(4).(5)((6))EndFunctionこのように、スクリプトコード中の文字列はすべて文字列番号に置き換えられ、文字列本体は文字列テーブルにまとめられます。無視して文字列テーブルの1エントリにまとめられます。PEXファイルの文字列テーブルの上限は65535(0xffff)ですが、1つのスクリプトファイルで巨大なコードを書くことはないため、この時点では何も問題ありません。セーブデータに保存される文字列セーブデータには次の文字列が保存されます(ただし保存のトリガとなる一定の条件がある模様)。Papyrus広域変数に動的に代入された文字列アクティブスクリプトの場合スクリプトの文字列すべて実行中のスクリプト実体セーブデータ破損の仕組みSkyrimのゲームエンジンに読み込まれたスクリプトの文字列はすべて1つの文字列テーブルに保存されます。ゲームエンジンでは65,535を超える文字列テーブルを取り扱えるようですが、ファイルに保存する際に16ビットに切り詰められてしまいます。文字列数が65,600の場合、16進数に変換すると0x10040となりますが、セーブデータには下位16ビットの0x0040が文字列数として保存されます。調査した範囲では文字列テーブルは65,600個分すべてがセーブデータに保存されるようなので、次回読み込み時に「文字列数が0x0040なのにそれ以上の文字列が入っている」状態となり、データ形式不正=CTDとなります。StringCount32機能の仕組みCrashFixPlugin.iniのStringCount32を1にすることで、セーブデータ中の文字列数および文字列番号を拡張することができます。情報:スクリプトが65535より多い異なる文字列を使うと、セーブデータは破損しロードできません。これは文字数が65520より大きい場合のファイル保存形式をわずかに変更することにより修正します。逆もまた真なりで、文字数を65520以下にして再びセーブするとバニラの保存形式に戻ります。アドレス:大体30箇所のコードにパッチを当てた。StringCount32=1実際に文字列数が超過したセーブデータを作成し調査したところ、セーブデータ中の文字列数(UInt16)および文字列番号(UInt16)が次のように保存されていました。Fix拡張)データ読み込みの際は、値が0xffffなら続けてUInt32の値を読み込めばいい訳です。スマートですね!このセーブデータで継続的にプレイした訳ではありませんが、パッチ箇所に漏れがなければ上記の問題点を完璧に解決するものだと思います。いえ、文字列数が超過したセーブデータをセーブクリーナなどで読み込めないのは不便なので、あくまでも保険として利用するのが賢いと思います。実例(私の体験談)最近新しい環境を作成してニューゲームしたのですが、全ホールドの従士になってDawnguardクエストを開始しセラーナを救出した直後に本問題が発生しました。タイミングがタイミングなので諦めきれず、グーグル先生を使ってやっと参考記事を見つけた次第です。どの程度のMOD構成でどれくらいPapyrus文字列数を消費するかの目安になるかもしれないので、私の環境とPapyrus文字列数を晒しておきます。MOD環境カスタムボイスフォロワー、イマージョン系MOD、景観・遠景改善を中心に構築しました。Lab系MODは入れていません。低FPS(GTX760で28前後)でスクリプト遅延もひどく、あまり褒められた環境ではありません。他のブログ管理者様が「これを入れたら重くなる」というものがことごとく入っているはずなので注意してください(これでも大分減らしたんですが)。アルフタンド・アニモンキュロリー、霊魂の確認クエスト進行中グラフにしてみました。この調子で全クエストクリアまで持ちそうです。ちなみに、以前構築した2つの環境は、DynDOLODを使っていない以外この環境と似たようなMOD構成でしたが、文字列テーブル数は39,000程度でした。loadの投稿者bcspさんと、速やかにDynDOLODを改善してくださったShesonさんにこの場を借りて御礼申し上げます。Fixの作者meh321さんにも感謝します。Yearに匹敵する偉業だと思います。考えても悪いのはベゼスダですから。上記のグラフを見ていただければ分かりますが、MODそれぞれの(一度きりの)消費文字数はさほど問題ではありません(大規模MODを大量に導入する際に問題があるかもしれませんが)。オブジェクト増加に従って文字列消費量が増加するMODにさえ気をつければなんとかなると思います。v8とRestringerに関する情報を追記した。v8の詳細説明を追記した。StrCountToolのリンクを追記した。StrCountTool解説ページのリンクを追記した。StrCountToolの利用ガイドです。Reserved.ブログの記事内に記載されているメーカー名、製品名称等は、日本及びその他の国における各企業の商標または登録商標です。記事の転載はご遠慮ください。記事を引用する場合はトラックバックするか元のURLを明記してください。はてなブログをはじめよう!thinkingskeeverさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。