Liminalia

書き戻し(lim backport)

TyranoScript(TyranoBuilder)の作品を lim import で .lim にし、そのあと .lim を正本にして直していく作品で、 .lim に加えた変更を .ks へ書き戻すための道具。.lim 側で行き詰まったときに、それまでの変更を持って TyranoScript に戻れるようにする。

書き戻しは packages/ks-import(backportTyrano)と CLI にあり、書き出すゲームには入らない。

使い方

1. 切り替えの時点で、基準を作る

lim backport [プロジェクト] --make-base <基準のフォルダ> --tyrano <TyranoScript のプロジェクト>

TyranoScript のプロジェクトを作品の取り込み設定(プラグインの ./import。lim import と同じもの)で取り込み直し、 書き戻しに要るものを基準のフォルダに置く。

基準のフォルダの中中身
ks/取り込み元の data/scenario の写し(system/ なども含めた一式)
lim/その .ks を取り込んだ .lim
import-sourcemap.json対応表(下の「対応表」)
import-report.md取り込みの報告
base.json作った日時、取り込み元の場所、取り込み元の git の状態(コミット、ブランチ、data/scenario の未コミットの変更)、基準を作ったエンジンの git の状態

作品の今の .lim(paths.scenario)が lim/ と同じかも確かめて出す。切り替える時点なら同じはずで、違えば作品の .lim を lim/ に合わせてから切り替える。 基準は切り替えの時点で一度だけ作り、作品と一緒に残しておく(作り直すと、それまでの .lim の変更が「基準からの変更」でなくなる)。

2. 書き戻す

lim backport [プロジェクト] --base <基準のフォルダ> [--tyrano <TyranoScript のプロジェクト>] [--out <フォルダ>] [--write [--force]]
  • 基準と作品の今の .lim から .ks 一式を --out(既定は .liminalia/backport/scenario)に作る。data/scenario の形で、変わらないファイルも含む
  • 報告(backport-report.md)を --out の隣に書く。書き戻せなかった箇所、消された .lim(.ks を消す候補)、確かめの結果
  • 最後に必ず確かめる: 書き戻した .ks を同じ取り込み設定でもう一度取り込み、作品の今の .lim と、行の並び、本文、命令の意味で比べる (空行、コメント、字下げ、引数の順、既定値と同じ引数は比べない)。違う箇所は報告に出る
  • --tyrano を省くと base.json に記録した取り込み元を使う。取り込み元は確かめの取り込みで data/scenario 以外(プラグインの設定、立ち絵の画像)を読むだけ
  • --write のときだけ、書き戻した .ks を取り込み元の data/scenario に書く。書き戻せなかった箇所があるか確かめが一致しないときは何も書かない。 取り込み元の .ks が基準と違う(切り替えたあとに TyranoScript 側でも直した)ファイルは書かずに報告する。どちらも --force で書く

終了コードは、すべて書き戻せて確かめが一致し、書かなかったファイルが無ければ 0。

対応表

lim import は、書き出した .lim の各行が .ks のどのファイル、どの行から来たかを import-sourcemap.json (シナリオの出力先の一つ上。importTyrano の結果の sourceMap にも入る)に書く。取り込みの結果(.lim の中身)は変わらない。

{
  "version": 1,
  "files": {
    "chapter1.lim": {
      "source": "chapter1.ks",
      "sourceHash": "…(.ks の SHA-256)",
      "limHash": "…(.lim の SHA-256)",
      "lines": [
        {"kind":"command","from":3,"to":3,"tags":[1]},
        null,
        {"kind":"text","from":8,"to":9}
      ]
    }
  }
}
  • lines[i] は .lim の i+1 行目の出どころ。空行、@use など .ks に対応しない行は null
  • from / to は .ks の行の範囲(1 始まり、両端を含む)。本文の行は本文の始まりから [p] まで([r] で行をまたげば複数の行)
  • tags は出どころのタグの位置(compile の命令の並びの添字)。本文の行には無い。まとめて一行にする変換([keyframe]〜[endkeyframe] など)は、まとめたタグをすべて持つ
  • kind は text(本文)、command(命令、@set など)、label、if elif else end([if] 系と、選択肢のタグ以外の cond=)、 choice option choice-end(選択肢。選択肢のタグの cond= は option の行の if= で、if end の行は出ない)、 script([iscript]〜[endscript])、define(マクロ)、comment(// TODO ks:)
  • マクロを集めたファイル(macros.lim)は source が null で、行の対応を持たない

仕組み

  1. 基準の .lim と今の .lim を行ごとに比べる(字下げは見ない)。同じ塊の中で消えた行と足した行は、種類が同じで似ているものを書き換えの組にする
  2. 対応表で基準の .lim の行を .ks の行の範囲にまとめる(範囲の重なる行は一つにする。本文には直前の #名前 の行を含める)。 どの行にも対応しない .ks の行(TyranoBuilder の [tb_start_text] [_tb_end_text]、書き出さないタグ、空行、コメント)はそのまま残す
  3. .ks を前から書き直す。変わらないところは一字一句そのまま。変わったところは次のとおり
  4. 書き直した .ks を取り込み直して、今の .lim と比べる
.lim の変更.ks
本文の行の書き換えその本文の部分だけを書き換える。話者の行、[p]、字下げ、テキストブロックの囲み、[r] での行の分け方は保つ
本文の行の追加前後の書き方に合わせて置く(テキストブロックの中。話者が変われば #名前 の行を足す。話者の行の書き方は基準の .ks のもの)
本文の行の削除その行と、その行のための #名前 の行を消す。あとの行の話者が変わらないよう、要れば #名前 を足す
話者の変更#名前 の行を替える、足す
本文の中の書き方{r 親|ルビ} は基準の書き方(|親《ルビ》、[srb text=…]親[erb]、[ruby text=…]。その行→ファイル→作品の順に多いもの)、{click} {br} は [l] [r]、{= 式} は [emb exp=…]、{speed 0ms …} は [nowait]〜[endnowait]、{speed …} は [delay speed=…]〜[resetdelay]、{b} {i} {c} {size} は [font …]〜[resetfont]、{mark} は [mark]〜[endmark]、{@命令} は命令の書き戻し
命令の行の書き換え元のタグの引数だけを書き換える(引数の間の空白、引用符、並び、変換しない引数は保つ)
命令の行の追加同じ命令になった近くのタグ(同じファイル、無ければほかのファイル)を手本に作る。本文の間に足したときは、テキストブロックを閉じてから置き、あとで開き直す
命令の行の削除そのタグを消す(行がそのタグだけなら行ごと)
ラベル*名前 を書き換える、足す、消す(*名前|見出し の見出しは保つ)
@if @elif @else @end[if exp=…] [elsif exp=…] [else] [endif](式は game. → f. などに戻す)。cond= から出た @if はそのタグの cond
選択肢選択肢の行は [glink](など)一つ。文言、飛び先の書き換えはその引数、追加は隣の選択肢を手本に、@choice の @end は [s]。出す条件 if=(式) はそのタグの cond(足す、書き換える、消す。式は @if と同じく戻す)。[link]〜[endlink] の選択肢は、その間の文と [link] の引数(cond を含む)を書き換え、追加は改行([r])で終わる隣の [link] の行を手本にする
@set[eval] / [tb_eval] の exp(TyranoBuilder の [tb_eval] は編集画面の欄 name val も合わせる)
@script の追加[iscript]〜[endscript]
コメント(//)の追加; のコメント
新しい .lim新しい .ks(作品の書き方。TyranoBuilder の作品ならテキストブロックで囲む。[_tb_system_call] は書かない)
消した .lim.ks は消さずに、消す候補として報告する

命令の行を .ks のタグに戻すときは、取り込みの変換(組み込みの変換、標準の変換表 STD_MAPPINGS、作品の ./import の mappings と tags)を 逆に解くのではなく、手本のタグの引数を一つずつ(近づかなければ二つ組で)書き換えて取り込みの変換に通し、目当ての行に近づくものを選んでいく。 変換し直した結果が目当ての行と意味で同じになれば成功。書き換えの候補(.ks の引数名)は、変換表の params、組み込みの変換の引数名、手本のタグにある引数から作る。

書き戻せないもの

書き戻せなかった箇所は報告の「書き戻せなかった箇所」に出し、.ks は元のまま残す(足した行は ; lim backport: … のコメントとして残す)。確かめでも違いとして出る。

  • 取り込み設定が [iscript] を命令に置き換えた行(scriptRules)の書き換え。@script(取り込めなかった JS)の中身の書き換え
  • @define(マクロ)の追加と、マクロを集めたファイル(macros.lim)の変更。.ks の [macro] を手で直す
  • 一つの .ks の行から出た複数の行の、一部だけの書き換え(一行に [p] を二つ書いた本文の片方、[cond=…] で出た @if の中への行の追加など)
  • 手本も変換表も無い命令(取り込みで一度も出てこず、組み込みの変換、変換表にも無いもの)と、取り込みで汎用の形(@タグ名 引数…)にしたタグの書き換え
  • 一つのタグから作れない .lim の書き方(.ks では二つのタグに分かれるもの)
  • 選択肢の文言のうちルビや命令を含むもの

これらは、取り込み設定の backport(逆向きの変換)で足せる。命令名ごとに、.lim の行(と元のタグ)から .ks のタグの字面を返す関数を書く。 返したタグも取り込みの変換に通して確かめ、違っていれば手本として引数の書き換えを続ける。

const profile: TyranoImportProfile = {
  backport: {
    // @stamp image=a.png → [stamp_show storage="a.png"]
    stamp: (line, original) => original ?? "[stamp_show]",
  },
};

気をつけること

  • 書き戻しは、基準の .ks を今の取り込みの変換でも取り込み直して基準の .lim と比べる。基準を作ったあとにエンジンや取り込み設定の変換が変わっていれば 報告の「注意」に出る(そのときの確かめの違いには、変換が変わった分が混じる)
  • 確かめは .lim どうしの比較なので、.ks の見た目(TyranoBuilder の編集画面での並び)までは確かめない。--write の前に、報告と書き戻した .ks の差分を見る
  • 本文の行の書き方の揺れ(エスケープの仕方)、命令の引数の順と時間の書き方(1s と 1000ms)、既定値と同じ引数の有無は、同じものとして扱う
  • 切り替えたあとに TyranoScript 側で直したファイルは、--write が書かない。両方を直したときは、書き戻した .ks と取り込み元を手で合わせる
  • 取り込みの変換が失う情報([glink] の色、大きさ、位置、[playbgm] の html5 など)は、元のタグと手本のタグからそのまま引き継ぐ
  • 選択肢のタグの cond= を選択肢の行の if=(式) にする前の取り込みで作った基準では、基準の .lim に選択肢の前の中身の無い @if / @end がある。 作品の .lim をそのままにすると確かめで違いになるので、その選択肢を if=(式) の形に直す(--make-base で取り込み直した .lim のとおり)。 直したあとの書き戻しは、@if / @end の削除と if= の追加が同じタグの cond にまとまり、.ks は変わらない