難解度「AAA」
※このページ全体の難解度です。
repeat
 BASICやC/C++でいうFOR文に近い仕組みを持ち、現在のループ回数を示す擬似変数 $Val を利用することで、様々な変換を含む置き換えを可能とします。書式は
[repeat]SSRファイル, 開始値, 終了値, 増加値
[repeat]SSRファイル, ループ値リストファイル
の、いずれかです。
SSRファイル置き換えるスクリプトを記述したSSRファイルを指定します。SSRファイルについては後述します。ファイル指定の詳細は、《C-ファイル指定》を参照。
開始値ループ変数の開始値を記述します。ループ変数は、「開始値」から始まり、ループ毎に「増加値」ずつ増え、「終了値」以上になると終了します。(ループ変数が「終了値」のときの処理は行いません)
終了値ループ変数の終了値を記述します。
増加値ループ変数のループ毎の増加量を記述します。
ループ値
リストファイル
開始値・終了値・を用いる場合、ループ変数はその間で(増加値に従って)変化していくわけですが、増加値が固定でない場合は、直接ループ変数の値を羅列したリストファイルを指定することも出来ます。ファイル指定の詳細は、《C-ファイル指定》を参照。
 SSRファイルの記述法は少々説明が必要ですので、ちょっと本腰入れていきましょうかっ!


SSRファイル
 SSRファイルに記述されたスクリプトは、いったん「不変文字列」と「変換式」の二種類に分けて処理されます。「不変文字列」とは、文字通りループ中でも常に変わらない部分です。それに対し「変換式」は、 [!!] で囲まれた「ループ中に変化する(と思われる)」部分になります。この変換式をうまく活用することで、様々な置き換えを可能とします。
 変換式は、その変換形式によっていくつかの種類に分かれます。以下にその一覧を示します。
式号※ 概要 ※
[! n !]nの16進数値を出力。ただし、出力はバイト数を0でそろえた16進数(仮に「3E7」なら、「03E7」)になります。
[! Ln !]nの10進数値を出力。
[! #n !]nの16進数値(ReverseEndian)を出力。基本は[! n !]と同じですが、演算値(n)のエンディアンを反転してから出力する点が違います。バイト列の場合、表記上リトルエンディアンで記述しますので、そういったときに用いることが多いでしょう。
[! #Ln !]nの10進数値(ReverseEndian)を出力。これも、エンディアン反転を行う[! Ln !]に同じです。
[! @,n !]@ファイル名のn行目の文字列を出力。演算で算出される数値と違い、規則性のない名称などは、一覧にしたファイルから引き出す方が効率的です。その際に、この形式を用います。「@ファイル名」には、文字列を羅列したリストファイルを指定します。ファイル指定の詳細は、《C-ファイル指定》を参照。
[! Rn,文字列 !]指定した「文字列」を、n回出力。他と異なり、ワードリピート機能と呼ばれます。アドレスコーディングにおけるポインタ変換演算子([: :])などを、条件に応じて複数回重ねたい場合に効果的です。感覚的には、リピートファイル中でさらにリピートをかけるような扱いになります。なお、不変文字列に[!などを使いたい場合は、[ !などとして、間に空白を挟んで対処して下さい。展開時に、自動で空白を除去して連結します。
実例を示します。
[subject]主人公:dir
  [subject]死亡フラグ:toggle,0x700004,09,00
  [subject]デバッグモード:toggle,[:0x710000:],01,00
  [replace]0x790000,SSCファイル
[subject]ヒロイン:dir
  [subject]死亡フラグ:toggle,0x700008,0A,00
  [subject]デバッグモード:toggle,[:[:0x710000:]:],01,00
  [replace]0x790004,SSCファイル
[subject]脇役:dir
  [subject]死亡フラグ:toggle,0x70000C,0B,00
  [subject]デバッグモード:toggle,[:[:[:0x710000:]:]:],01,00
  [replace]0x790008,SSCファイル
上記のようなスクリプトを[repeat]を用いて最適化すると、
[repeat]SSRファイル,0,3,1

SSRファイル.SSR
[subject][! @キャラ名リスト, $Val !]:dir
  [subject]死亡フラグ:toggle,0x[! 0x700004+ $Val*4 !],
           [!# 0x09+$Val !],00
//////////////////// ↑実際は一行で書きます
  [subject]デバッグモード:toggle,[!R($Val+1), [: !] 0x710000
           [!R($Val+1), :] !],01,00
//////////////////// ↑実際は一行で書きます
  [replace]0x[! 0x790000+ $Val*4 !],SSCファイル

キャラ名リスト.LST
主人公
ヒロイン
脇役
のようになります。元のスクリプトに残るのは、
[repeat]SSRファイル,0,3,1
の一行のみです。このとき、[repeat]のオプションに「0,3,1」と指定しているので、ループ変数($Val)は「0→1→2→終了」と変化していきます。これを踏まえた上で、上記SSRファイルの詳しい説明に移ります。
[subject]不変文字列。
[! @キャラ名リスト, $Val !]ループ毎に、「キャラ名リスト.LST」の任意の行に置き換わります。行数を示す部分には「$Val」とあるので、「0行目→1行目→2行目」を順々に引っ張ります。
:dir不変文字列。
[subject]死亡フラグ:toggle,0x不変文字列。
[! 0x700004+ $Val*4 !]最終的に16進数値を出力する計算式です。不変の数値0x700004に、$Val*4を足しあわせた16進値。つまり「700004→700008→70000A」と変化していきます。さらにこれを16進数として認識させるために、直前に不変文字列として「0x」を配してあります。
,不変文字列。
[!# 0x09+$Val !]最終的にエンディアン反転をかけた16進数値を出力する計算式です。ちなみにこの例では1バイト固定ですので、実際はエンディアン反転をかける意味はありません。
,00不変文字列。
[subject]デバッグモード:toggle,不変文字列。
[!R($Val+1), [: !]文字列[:を、$Val+1回出力します。つまり、「[:→[:[:→[:[:[:」と変化します。ちなみに、「$Val+1」を括っている()は、範囲を分かりやすくするために付けただけなので、無くても構いません。
0x710000不変文字列。
[!R($Val+1), :] !]文字列:]を、$Val+1回出力します。つまり、「:]→:]:]→:]:]:]」と変化します。
,01,00不変文字列。
[replace]0x不変文字列。
[! 0x790000+ $Val*4 !]前述の計算式[! 0x700004+ $Val*4 !]と同様です。
,SSCファイル不変文字列。
 変数などのプログラミング的な考えが混じるため、変換式の理解にとまどうかと思いまが、似たようなスクリプトが何十も繰り返されるスクリプトであれば、[repeat]を使うことで大幅に手間を省くことができるでしょう。
さて、ここまでが増加値(開始値・終了値)を用いた場合の説明ですが、「ループ値リストファイル」を用いた場合の説明を付け足しましょう。

リストファイル.LST
1
2
5
7
8
このようなリストファイルを用意した場合、ループ変数$Valは、[1→2→5→7→8]と変化して、5回ループを回ったところで処理を終えます。なお、リストファイル中の空白行は無視されます。


難解度「AAA+」
静的展開
 [repeat]は[replace]と違い、SSGを展開するときに全てのオプションが評価されます。これはどういうことかというと、
[replace]_[:0x400000:], SSCファイル
[repeat]SSRファイル,0,1,1

SSCファイル.SSC
[subject]体力:calc,0x00,0,99

SSRファイル.SSR
[subject]体力:calc,0x[! [:0x400000:] !],0,99
といった二つの置き換え命令の処理結果は、必ずしも同じになりません。それどころか、[repeat]命令の方は不正な使用法を用いています。
 これは、[repeat]命令のオプションであるSSRファイルの解析処理が、SSG展開時に行われるためです。上記の例では、電卓項目のアドレスを、ポインタ変換演算子([::])を用いてメモリから所得しています。しかし、この「メモリから所得」するタイミングが、[repeat]と[replace]では異なるのです。
 [replace]は、項目にアクセスしたときに(アドレスを)計算する動的展開」を行いますが、[repeat]はSSG展開時に計算する静的展開」を行います。このため、上記の例ではSSGを展開した瞬間のメモリ内容しか反映されず、結果としてSSG展開の瞬間に対象のプロセスが起動されていなければ正常な値は返ってきません。また、内部処理の関係上、たとえ展開の瞬間に対象のプロセスが起動されていても、正確な値は所得出来ないと思った方がよいでしょう。  

では、具体的に上記の例のどこが悪かったのでしょう?

0x[! [:0x400000:] !]
 ズバリ、SSRファイルにおける上の部分です。変換式([! !])の中身はSSG展開時に「静的展開」されます。しかし、それ以外の箇所は[repeat]が展開する箇所ではないため、「静的展開」処理は行われません。つまり、
0x[! [:0x400000:] !]  //×
[:0x400000:] + [! !]  //○
この2行が意図するところは同じでも、2行目の使用法は適性で、1行目の使用法は不正なのです。(なお、2行目は適性ですがSSRにする意味がありません。)
まとめ
変換式[! !]の中に、ポインタ変換演算子[: :]があるときは、不正!




ねくすと せくしょん⇒

≪せくしょん ばっく