Skyrim自作MOD: StrCountTool 利用ガイド (v0.1版)
拙作のツール StrCountToolの利用ガイドです。
このツールを使えば、SkyrimセーブデータのPapyrus文字列数を確認したり、プラグインファイル/スクリプトファイル中の文字列を表示することができます。
ツールのダウンロードはこちら→:StrCountTool v0.1
StrCountTool 利用ガイド (v0.1版)
目次
- 目次
- 1. はじめに
- 2. 主な特徴
- 3. 必要条件
- 4. インストール方法
- 5. アンインストール方法
- 6. 基本的な使い方(一般プレイヤー向け)
- 7. 少し高度な使い方(MOD制作者/問題調査者向け)(作成中)
- 8. 表示項目一覧
- 本ガイドの改訂履歴
1. はじめに
Skyrimセーブデータの文字列数上限65535問題をご存知でしょうか?
セーブデータにはバニラやMODで追加されたスクリプトに関する情報が保存されます。
スクリプトで使われている文字列データはセーブデータ中の文字列テーブルにまとめて保存されるのですが、セーブデータの文字列テーブルには最大65535個(符号なし16ビット)という上限があり、総文字列数が上限を超えるとセーブデータが破損しロードできなくなるというものです。
※この問題の詳細については以下の記事を参照してください。
bscpさんによる元記事:http://forums.nexusmods.com/index.php?/topic/3924850-corrupt-saves-strcount-0xffff-ctd-on-load/
日本語による解説(本ブログ):Skyrim Mod導入トラブル事例:セーブデータのロード時に確定CTD(Papyrus文字列数上限65535問題)-2016/5/13追記 - Thinking Skeever
本ツールを使えば、文字列の超過が発生していないか、増加傾向はないかを比較的簡単に確認することができます。
また、セーブデータではなく、プラグインファイル(ESM/ESP)やスクリプトファイル(PEX)に含まれる文字列の情報も表示できます。プラグインファイルやスクリプトファイルに含まれる文字列の一部がセーブデータに保存されることが確認されています。どのMODが文字列数増加に影響を与えているか調査する一助となるはずです。
2. 主な特徴
共通操作
セーブデータ(ESS)ファイル表示
- 単一ファイル表示/複数ファイルをまとめて一覧表示
- 文字列数だけでなく、ヘッダ情報/ゲーム内時間/アクティブスクリプト/文字列テーブル内容も表示可能
3. 必要条件
動作環境
私はWindows XP/8/10を持っていないので動作検証できていませんが、恐らく.NET Framework 4.0以降がインストールされていれば動作します。
表計算ソフト(オプション)
本ツールの表示結果は表計算ソフトで表示することを意図しており、列揃えなどの調整を一切行っていません。
Microsoft Office、Apache OpenOffice、LibreOfficeなどの表計算ソフトを使うことで表示結果を見やすくしたり、グラフにしたりできます。
本ガイドではLibreOfficeを使った操作を紹介しています。
4. インストール方法
- ツールをダウンロードします。こちらからどうぞ→:StrCountTool v0.1
- ZIPファイルを回答し、StrCountToolごと任意のフォルダにコピーします。ただしゲームのフォルダ、Mod Organizer/NMMのフォルダ、Program Filesフォルダは避けてください。良く分からなければデスクトップ上でも問題ありません。
- 旧バージョンからのアップデート時には、単純にファイルを上書きコピーしてください。
- ツールを起動するにはStrCountTool.exeを実行します。
5. アンインストール方法
アンインストールする場合、インストールしたStrCountToolフォルダごと削除してください。レジストリは使っていませんのでクリーニングの必要はありません(厳密にはWindowsと.NET Frameworkによって少量のレジストリが作られているかもしれませんが、誤差の範囲だと思います)。
6. 基本的な使い方(一般プレイヤー向け)
セーブデータの文字列数が超過していないか確認する基本的な使い方を説明します。
ツールの起動とオプションの設定
文字列数を確認する(単一ファイル)
左右のテキストボックスのどちらかにセーブデータ(ESS)ファイルをドラッグ&ドロップします。
片方のテキストボックスが邪魔ですので、真ん中のセパレータを左右に動かして調整してください。
「globalDataTable3.Papyrus.strCount:」の数字が文字列数です。
文字列数が上限を超過している場合エラーとなります。
発生するエラーは不定で、「Error: インデックスおよびカウントはバッファー内の場所を参照しなければなりません。」エラーとなる場合もあります。
文字列数が上限を超過していても、Crash Fix v8以降を使っている場合は次のようになります。上限の65535を突破できているのがお分かりでしょうか?
文字列数の判断の目安は次のとおりです(私の個人的見解です)。
文字列数 | 判定 | 説明 |
---|---|---|
~45,000 | 安全 | まあ大丈夫でしょう。 |
50,000~55,000 | 警戒 | 上限超過に対する備えが必要です。文字列数が増加傾向にあるか監視すべきです |
55,000~60,000 | 危険 | そろそろ抜本的な対策が必要です。 |
60,000~ | 対策要 | このままプレイを続けるのは危険です。セーブデータのクリーニングが出来なくなってもよいならCrash Fix v8以降を導入しましょう。そうでなければ使用中MODの文字列数対策版を探すか、不要なMODを削除しましょう。 |
エラー | 南無 | このデータは破損しています。過去のデータにさかのぼってやり直してください。 |
文字列数を確認する(複数ファイルをまとめて)
テキストボックスにファイルをまとめてドラッグ&ドロップします。
セーブフォルダごとドロップしてもかまいません。また、拡張子.ESS以外のファイルは無視されますので、.SKSEファイルもまとめてドロップしても問題ありません。
ドロップすると進捗ダイアログが表示されます。
読み込みが終わると次のような一覧が表示されます。タブ区切りのテキストなので列が揃いませんがご勘弁を。
「Display Level」が「Summary」の場合、右から2つ目の数字が文字列数です。
文字列数が超過している場合、赤枠のようなエラーメッセージが表示されます。
表計算ソフトで開く
テキストボックスを右クリックして「Open as .csv」を選択すると、表計算ソフトで結果を表示できます。
Libre Officeがインストールされている場合、次のダイアログが表示されます。「区切る」「タブ」をチェックして「OK」ボタンを押します。
他の表計算ソフトでも同様のダイアログが表示されますので、読み替えて設定してください。
表計算ソフトで表示した様子です。列が揃って見やすくなりました。
ご注意:
表計算ソフトで開くcsvファイルはStrCountToolインストールフォルダのTempフォルダに作成されますが、StrCountTool終了時に自動的に削除されます。データを編集した場合、別名で保存しないと結果が失われます。
グラフを表示する
表計算ソフトで開いたらグラフを表示してみましょう。文字列数の増加傾向がビジュアルに見えるのでとても分かりやすいです。
strCount列(J列)全体を選択し、「挿入」-「グラフ」メニューを選択します。
「グラフウィザード」画面が表示されたら「完了」ボタンを押します。
これで単純な棒グラフが表示できました。
今度は横軸をゲーム内時間にして時系列で表示してみましょう。
今度はGameDaysPast列(I列)とstrCount列(J列)全体を選択し、「挿入」-「グラフ」メニューを選択します。
「グラフウィザード」画面が表示されたら好みのスタイルを選択して「完了」ボタンを押します。
ここでは「線のみ」を選んでみます。
時系列グラフが表示できました。
ゲーム内時間に比例して文字列数が増加している様子が分かります。ところどころMODの追加や更新で文字列数が急増しているのがよく分かりますね。
2つのセーブデータを比較する
文字列数の増加傾向がある場合、2つのセーブデータを比較して調査することで原因を突き止められるかもしれません。
本ツールではファイル比較ツールWinMergeを使って2つのセーブデータを比較することができます。、
ファイル比較ツールの準備
先にWinMergeをインストールし、WinMerge.exeのインストールされたフォルダをWindowsのPATH環境変数に追加しておいてください(パスを通しておいてください)。
パスを通したくない場合や、WinMerge以外の比較ツールを使う場合、StrCountToolのインストールフォルダ中のStrCountTool.iniを次のように修正してください。
[General]
winMergePath=winmerge.exe ← ここにファイル比較ツールのフルパスを指定する
ファイル比較ツール活用のススメ:
ファイル比較ツールはあらゆる場面で役立つとても便利なツールです。この機会に是非導入しましょう。
場面ごとの活用例:
- MOD制作:修正したファイルの洗い出しと点検、過去のリリースと比較してファイル過不足チェックなど
- MOD翻訳:過去訳との比較、翻訳対象MODの変更箇所洗い出しなど
- MOD利用:MODアップデート時の変更点の洗い出し、Skyrim.iniファイルの変更点の見直しなど
比較の手順
左のテキストボックスに古いファイルを、右のテキストボックスに新しいファイルをドロップします。
ファイルを開いたら「Compare Text」ボタンを押します。
ファイル比較ツールが起動します。
「*** Papyrus Strings (数字):」の下が、セーブデータ中に含まれる文字列の比較となります。
スクロールして追加された文字列を眺めてみましょう。「F7」「F8」キーで前の・次の差異のある行にジャンプできるので便利です。
このセーブデータの場合、ゲームが進行するごとに「c28042」「c28297」という文字が増えていきました。調査の結果、DynDOLODが生成する文字列であることが判明し、DynDOLODを最新版にすることで増加傾向を止めることができました。
比較のコツ:
セーブデータ中の文字列は実行中のスクリプト(アクティブスクリプト)の数・種類によってもめまぐるしく変化します。
ゲーム内時間がある程度離れたデータや、新しい土地に入る前後・新しいクエスト開始前後とったイベントまたがりののデータを比較した方が問題を特定しやすいと思います。
7. 少し高度な使い方(MOD制作者/問題調査者向け)(作成中)
(現在作成中です)
8. 表示項目一覧
セーブデータ(ESS)ファイル
Lvl ※1 | 表示項目 | 説明 | 表示例 |
---|---|---|---|
1 | header.version: | ファイル形式のバージョン | 9 |
1 | header.saveNumber: | セーブ番号 | 10 |
1 | header.playerName: | プレイヤー名 | Bowmore |
1 | header.playerLevel: | プレイヤーレベル | 1 |
1 | header.playerLocation: | プレイヤーの場所 | ラッキー・スキーヴァー |
1 | header.gameDate: | ゲーム内日時 | 001.35.36 |
1 | header.playerRaceEditorId: | プレイヤー種族 | NordRace |
1 | header.playerSex: | プレイヤー性別。女性は1 | 0 |
2 | header.playerCurExp: | 36.75 | |
2 | header.playerLvlUpExp: | 100 | |
1 | header.fileTime: | ファイル保存日時 | 2016/03/14 11:00:33 |
2 | formVersion: | 74 | |
1 | globalDataTable1.GlobalVariables.GameDaysPassed: | ゲーム内経過日数(Skyrim広域変数より) ※2 | 0.5688909 |
1 | globalDataTable1.GlobalVariables.TimeScale: | タイムスケール(Skyrim広域変数より) | 6 |
1 | gameDateString: | ゲーム内日時文字列(Skyrim広域変数より組み立て) | 4E 201/07/17 13:39 |
1 | GameHoursPassed (GameDaysPassed multiplied by 24): | ゲーム内経過時間(GameDaysPassed * 24) ※2 | 13.65338 |
1 | RealHoursPassed (GameHoursPassed divided by TimeScale): | 経過実時間(GameHoursPassed / TimeScale) ※2 ※3 | 2.275563 |
2 | globalDataTable3.Papyrus.header: | 4 | |
1 | globalDataTable3.Papyrus.strCount: | Papyrus文字列数。Crash Fixによる拡張形式の場合、末尾に(Crash Fixed)が表示される | 39164 |
2 | globalDataTable3.Papyrus.stringsSize (bytes): | Papyrus文字列テーブルのサイズ(16進表記) | 0x000EC0E9 |
2 | globalDataTable3.Papyrus.scriptCount: | スクリプト数 | 5421 |
2 | globalDataTable3.Papyrus.scriptInstanceCount: | スクリプトインスタンス数 | 55115 |
2 | globalDataTable3.Papyrus.referenceCount: | リファレンス数 | 0 |
2 | globalDataTable3.Papyrus.arrayInfoCount: | 1244 | |
2 | globalDataTable3.Papyrus.papyrusRuntime: | 0x00017AD0 | |
1 | globalDataTable3.Papyrus.activeScriptCount: | アクティブスクリプト数 | 4 |
2 | String Count used in Active Scripts: | アクティブスクリプトで使われているPapyrus文字列数 | 196 |
3 | globalDataTable3.Papyrus.FunctionMessageCount: | 3 | |
3 | globalDataTable3.Papyrus.SuspendedStackCount1: | 0 | |
3 | globalDataTable3.Papyrus.SuspendedStackCount2: | 0 | |
3 | globalDataTable3.Papyrus.Unknown0Count: | 0 | |
3 | globalDataTable3.Papyrus.QueuedUnbindCount: | 0 | |
3 | globalDataTable3.Papyrus.SaveFileVersion: | 5 | |
3 | globalDataTable3.Papyrus.ArrayCount1: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount1a: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount2: | 1 | |
3 | globalDataTable3.Papyrus.ArrayCount3: | 74 | |
3 | globalDataTable3.Papyrus.ArrayCount4: | 20 | |
3 | globalDataTable3.Papyrus.ScriptListCount: | 1 | |
3 | globalDataTable3.Papyrus.ArrayCount4a: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount4b: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount4c: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount4d: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount5: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount6: | 3 | |
3 | globalDataTable3.Papyrus.ArrayCount7: | 1 | |
3 | globalDataTable3.Papyrus.ArrayCount8: | 29 | |
3 | globalDataTable3.Papyrus.ArrayCount9: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount10: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount11: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount12: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount13: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount14: | 0 | |
3 | globalDataTable3.Papyrus.ArrayCount15: | 0 | |
2 | *** Active Scripts | アクティブスクリプトの一覧 | - |
3 | Local Variables | アクティブスクリプトで使われているローカル変数の一覧 | - |
3 | Papyrus Strings used in Function | アクティブスクリプトで使われているPapyrus文字列の一覧 | - |
3 | *** Papyrus Strings used in Active Scripts | アクティブスクリプトで使われているPapyrus文字列の一覧 (全アクティブスクリプト分) | - |
1 | *** Papyrus Strings | Papyrus文字列の一覧 | - |
※1:表示レベル(1=Summary / 2=Detail / 3=More Detail)
※2:メニュー表示中の時間はカウントされない。
※3:ゲーム途中でTimeScale値が変更された場合には不正確となる。
プラグイン(ESM/ESP)ファイル
Lvl ※1 | 表示項目 | 説明 |
---|---|---|
0 | プロパティ一覧 | プロパティ定義の一覧。Display LevelがSummaryの場合、文字列型プロパティのみ表示 |
1 | Script Names | プロパティ定義されたスクリプト名の一覧 |
1 | Property Names | プロパティ名の一覧 |
0 | Property Strings | プロパティで定義された文字列の一覧 |
※1:表示レベル(1=Summary / 2=Detail / 3=More Detail)
スクリプト(PEX)ファイル
Lvl ※1 | 表示項目 | 説明 |
---|---|---|
0 | String table | 使用されている文字列の一覧 |
※1:表示レベル(1=Summary / 2=Detail / 3=More Detail)