ラベル 日本語化 の投稿を表示しています。 すべての投稿を表示
ラベル 日本語化 の投稿を表示しています。 すべての投稿を表示

2026年7月6日月曜日

The Guild II Renaissance ベータ v4.6 の日本語化MODのこしらえ方

 なんと、16年前の2010年に発売されたThe Guild II Renaissance の開発が再開され、帰ってきました。

まだベータですが開発元はかなりやる気なようで、日を措かず更新が続いています。様々な改善点があり、パッチノートが出るたびにうれしさと驚きの連続です。

その中に、公式以外の言語へのサポートがありました。

そこで実験してみると、有志の方々がリリースなさったv4.211用日本語化ファイルを、従来のような手間をかけずにv4.6betaに適用できることがわかりました。

具体的には Mod として翻訳文を適用させます。そのため、v4.211までと違い、ゲーム本体に上書きする必要がありません。不要になったらすぐに抜去できます。

Mod化の方法は "steamapps\common\The Guild 2 Renaissance\mods_info\Language_Installation_Guide.txt" に記述されています。

概要は以上です。


steamクライアントを前提とした手順は以下の通りです。

  1. すでにゲームバージョンv4.211を日本語化済みだった場合、ゲームの公式最新版(v4.211)をインストールしなおしたり、バックアップを復元したりファイルの整合性をとるなどして、オリジナルのText.dbtを取得する必要があります。
    The Guild2 日本語化Wiki様で配布なさっておられる日本語化済みText.dbtでは原文が失われているためv4.6と比較できず、差分が取れないためです。

  2. ゲームのプロパティを開き、下記画面左の「ゲームバージョンとベータ」メニューを選択し、patch_4.6を選択します。
    なお、元に戻す場合は「デフォルトの公開バージョン」を選択してください。
    この画面では選択するだけでゲームバージョンが変更されてダウンロードが開始されてしまいますので、従量課金回線をご利用の方はご注意ください。

  3. v4.211のオリジナル(英文)のText.dbtとpatch_4.6のText.dbtとの差分を取得してください。この際、Text.dbtファイルの文字コードはUTF16ですので、nkfなどのフィルタをかませるか、または直接UTF16に対応したdiffツールが必要です。

  4. この際、両Text.dbt間で重要なのは、削除箇所や追加箇所でなくて、変更箇所です。
    削除箇所は単純に参照されないでしょうし、追加箇所は(つまり有志版v4.211のText.dbtにない訳文は)オリジナルの英文にフォールバックして表示されますが、変更箇所は変数を表示する個所もあり危険です。修正をお勧めします。

  5. 例えばオリジナルのv4.211とv4.6の違いは以下のようになっていますが、項番2が修正をお勧めする対象です。
    1. v4.6で廃止されたID
      2288
      2289
      2291
      2292
      2293
    2. v4.211とv4.6で同一IDで異なる文字列
      4368
      4369
      4374
      4377
      4380
      4395
      4397
    3. v4.6で追加
      16649~17117

  6. 上記画面左メニューの「インストール済みファイル」を開き、「参照...」ボタンを押してください。
    すると、ゲームがインストールされたフォルダが開きます。

  7. modsというフォルダがありますので開いてください。

  8. ja_testという名前のフォルダを作成して、開いてください。

  9. modinfo.txtというファイルを作ってください。

  10. modinfo.txtを開き、以下の内容をUTF8(ていうかasciiだけど)で保存してください。
    ModArchitecture=4.67

  11. DBというフォルダを作成して、開いてください。

  12. Languages というフォルダを作成し、開いてください。(sを忘れがちです)
    ここまでで mods\ja_test\DB\Languages というパスになっているはずです。

  13. ここ(mods\ja_test\DB\Languages)にThe Guild2 日本語化Wiki様からダウンロードしたText.dbtを保存してください。

  14. そこ(mods\ja_test\DB\Languages)にTables.dbtというファイルを作ってください。

  15. Tables.dbファイルを開き、以下の内容をutf8で保存してください。
    // Table File
    // by ayumi
    
    Table Description:
    "id" INT 0 | "name" STRING 0 |
    
    Data:
    1   "Text"   |
    

  16. 上記で英語版MODのふりをした日本語化MODができました。
    本来ならばさらに#japaneseとして本当の日本語化MODを作れという仕様なのですが、そうするとタイトルロゴが白い矩形になってしまうので現時点ではこのままにしておいたほうが良さそうです。
    betaが外れたらいずれその問題はなくなるかもしれません。

  17. MODを有効にするためにランチャーから起動してください。

  18. Modsを選択してください。


  19. 先ほど作成した"ja_test"をアクティベートしてください。
    上記画面の緑色のボタンを押すとアクティベートされます。

  20. この画面が出たら友達がいないならスキップしてかまいません。

    私にはいません

  21. おめでとうございます。

  22. フォントをインストールしていないのに、訳文も追加していないのに、しっかり日本語化されました。
    翻訳者の皆様、そしてゲーム開発を再開して、多国語化対応に目を向けてくれた皆様、ありがとうございます。
以上、ここまでお読みいただきありがとうございました。


2025年6月3日火曜日

Steam版SagradaのXUnity.AutoTranslatorを用いた日本語化手順

***追記ここから 2026/05/14***

本日付のアップデート(Patch 2.0.2.811)より、本記事でご案内した手順では起動できなくなりました

調査の結果、原因は2つ見つかりました。

  1. Unityのアセットのバージョンが最新リリース版のIL2CPP版では対応していない。
    →NightlyBuild版では対応しているがVisualStudioで自力でビルドして配置する必要があります。
  2. 起動時にSagrada.exeというファイル名を持つプロセスがほかに起動しているかどうかを検索し、存在した場合は自己終了してしまうようになった。
上記の理由のうち、1は別にどうということはありませんが、問題は2です。
BepInExさんの仕様で翻訳対象プロセス名と同じ名前のプロセスを立ち上げる仕様になっているため、単純にBepInExさんのソースコードを変更して起動プロセス名を変更するようにしても正常に動作しません。
ソースを調べてガチャガチャやってみましたが、もしこれに対処しようとするとBepInExさんの大幅な改造が必要だとわかりました。
かといって、もしかしてチート対策なのかもしれないのでDireWolf さんのほうも起動時の仕様を元に戻してくれることは望み薄かと思います。

役に立たずに恐縮ですが、以上、ご報告いたします。
***追記ここまで***

 美しいステンドグラスを作るボードゲームのサグラダ(Sagrada)、DireWolf さんがPC版を販売しています。

長いこと積みゲーでしたがなぜか気が向いてプレイしてみると結構面白かったのですが、残念なことに日本語化されていません。折角ですから PC版のSagrada を XUnity.AutoTranslator で翻訳してもらうことにしました。

ちょっとややこしかったので手順をメモします。

本記事の記述時点の前提は次の通りです。

  • ゲームクライアントバージョン:  steam版 1.4.4.652 (ゲーム内で設定画面を開くと画面下に表示されます)
  • BepInEx: IL2CPP版 6.0.0-pre.2
  • XUnity.AutoTranslator: BepInEx-IL2CPP版 5.4.5

適用するとこんな感じになります。(2025年6月現在)

サンプル画像

Windows版への適用を箇条書きで記録します。(他OS版はwin-x64をmacosと読み替えればよさそうですが検証していません。ごめんなさい)

  1. GitHubよりBepInEx-Unity.IL2CPP-win-x64-6.0.0-pre.2.zipをダウンロードします(もちろん、git cloneしてビルドしても構いません)。
  2. Sagradaがインストールされているフォルダ(Sagrada.exeがあるディレクトリです)にBepInEx-Unity.IL2CPP-win-x64-6.0.0-pre.2.zipの中身をすべて展開します。
    Sagrada.exeがあるフォルダと同じ場所に以下のフォルダまたはファイルが存在する状態になります。
    • 📂BepInEx (フォルダ)
    • 📂dotnet (フォルダ)
    • .doorstop_version (ファイル)
    • changelog.txt (ファイル)
    • doorstop_config.ini (ファイル)
    • winhttp.dll (ファイル)

  3. 一度Sagrada.exeを起動します。
    Steamクライアントから起動しても直接起動しても構いません。
    起動すると黒いウィンドが開いて何やら表示されますが、やがてゲームが起動するはずです。
    正常に起動することを確認したら、ゲームを終了して 📂BepInEx フォルダの中に LogOutput.log および ErrorLog.log という名前のファイルが生成されていることを確認してください。
    正常に起動しなかったり、LogOutput.log および ErrorLog.log のいずれも生成されていない場合はBepInEx-Unity.IL2CPPを正しく展開できていない可能性があります。
  4. GitHubより今度はXUnity.AutoTranslator-BepInEx-IL2CPP-5.4.5.zipおよびTMP_Font_AssetBundles_2025-05-12.7zをダウンロード(またはビルド)します。
    • XUnity.AutoTranslator-BepInEx-IL2CPP-5.4.5.zipファイルの中身は📂BepInExとなっています。
      すでに存在している📂BepInExフォルダを上書きしてください(macosでfinderを利用する場合は置換されてしまうのでフォルダの上書きはしないでください)
    • TMP_Font_AssetBundles_2025-05-12.7z の中身のうち、arialuni_sdf_u2018というファイルを Sagrada.exeがあるディレクトリに展開してください。
  5. 一度 Sagrada.exe を起動し、正常に起動出来たら終了してください。これもSteamクライアントから起動しても直接起動しても構いません。
    この際、先ほどの手順3と違って表示される画面およびLogOutput.logファイルにエラーが出力されますが、この段階では無視してください。
  6. 📂BepInExフォルダの中に📂configというフォルダができているはずです。
    その中にAutoTranslatorConfig.iniというファイルが生成されていますので開き、以下の変更を行ってください(4か所)。
    • [General]セクション
      • Language=en を ja に変更
      • FromLanguage=ja を en に変更
    • [Behaviour]セクション
      • OverrideFontTextMeshPro=(空欄) を 右辺値にarialuni_sdf_u2018を記述
      • FallbackFontTextMeshPro=(空欄) を 右辺値にarialuni_sdf_u2018を記述
以上の手順で、冒頭でお示しした画像の状態になります。

いうまでもありませんが、OverrideFontTextMeshProで指定しているのはフォントファイルなので、arialuni_sdf_u2018でなくともいいのですが、TMP_Font_AssetBundles内では最も豆腐(文字化け)がありませんでした。

以上、ここまでお読みいただきありがとうございました。

2019年3月28日木曜日

Offworld Trading Company v1.21 日本語訳 成果物 (本体,Core Game, Almanac, Jupiter's Forge込み)

これまでの記事のまとめとして、実際にゲームに適用できるMODを作りましたのでこちらからダウンロードしてください。

このMODの日本語化の適用対象は以下に含まれるすべての英文です。
  • 本体 (バージョン1.21.27358)
  • Core Game DLC
  • Almanac DLC
  • Jupiter's Forge Expansion Pack
A)インストール方法についてご説明します。
手順1: MODをインストールするためのフォルダを開いてください。
インストール先は
steamapps\common\Offworld Trading Company\Offworld_Data\StreamingAssets\Mods
です。開いた方は手順2へ進んでください。
これが何を示している呪文なのかさっぱりわからない方は以下の手順に従って開いてください。
  1. Steamクライアントを起動し、ライブラリ画面を表示してください。
  2. Steamクライアントのライブラリ画面中に並ぶ一覧から「Offworld Trading Company」を選択し、そのゲーム名の上にマウスカーソルを合わせ、マウスをクリックしてメニューを表示してください。
  3. そのメニューの一番下に「プロパティ」という項目がありますので選択してください。
  4. すると、「Offworld Trading Company - プロパティ」という題名の画面が開きます。
  5. 画面上部にはタブが並んでおり、左から3番目に「ローカルファイル」というタブがありますので選択してください。
  6. ローカルファイルタブ画面にはボタンが4つ並んでいますので、一番上の「ローカルファイルを閲覧...」というボタンを押下しますと、勝手にフォルダが開きます。
  7. ボタンを押して開かれたフォルダ中にOffworld_Dataというフォルダがあるので開いてください。
  8. Offworld_Dataというフォルダの中にStreamingAssetsというフォルダがあるので開いてください。
  9. StreamingAssetsというフォルダの中にModsというフォルダがありますので開いてください。
  10. 上記の手順で開いたフォルダを、今後「MODインストール先フォルダ」と呼ぶことにしますので覚えておいてください。
手順2: MODをダウンロードしてください。
Avocado.zipというファイル名になっています。
zipファイルを開くと、中にはAvocadoというフォルダが入っています。

手順3: ダウンロードしたzipファイル中にあるAvocadoフォルダを、MODインストール先フォルダコピーしてください。

ここまでで本体及びPractice Challengesの日本語化が完了しました。
本体およびJupiter's Forgeのチュートリアルを日本語化する場合は次の手順に進んでください。
チュートリアルの日本語化が不要な方は以下の手順は不要です。
お疲れさまでした

手順4: 手順3でコピーしたAvocadoフォルダを開いてください。
手順5: Avocadoフォルダ中にあるCopyToTutorial.batというファイルをダブルクリックしてください。
手順6: なんだか黒い画面が開いていっぱい文字が出てきた挙句、「続行するには何かキーを押してください . . .」と表示されますので、何かキーを押してください。

以上でMODのインストールを完了しました。
お疲れさまでした。
※Macの方は手順5,6を行わず、Avocado/Data/*.xmlをすべて Avocado/../Hidden/{Tutorials|IoTutorial}/Data/にコピーまたはリンクを張って下さい。

B) アンインストール手順についてご説明します。
手順1: Avocadoフォルダを開いてください。
手順2: Avocadoフォルダ中にあるRemoveFromTutorial.batというファイルをダブルクリックしてください。
手順3: なんだか黒い画面が開いていっぱい文字が出てきた挙句、「続行するには何かキーを押してください . . .」と表示されますので、何かキーを押してください。
手順4: Avocadoフォルダを削除してください。

以上でアンインストールを完了しました。
お疲れさまでした。
※Macの方は手順2,3を行わず、Avocado/Dataにあるのと同一名のXMLファイルをすべてAvocado/../Hidden/{Tutorials|IoTutorial}/Data/からxargsコマンドを活用するなどして削除してください。

C) MODのゲーム内への適用方法について
ゲームのマニュアルをご覧ください。

D) フォントの指定方法について
手順1: Avocadoフォルダを開いてください。
手順2: Avocadoフォルダ中にあるfont-setting.xmlというファイルをメモ帳で開いてください。
手順3: font-setting.xml内に記述されている行に<FallbackTO>で囲まれた行がありますので、その囲まれているところをお好きなフォント名に変更してください。
詳細はfont-setting.xml内に記述されている説明文をお読みください。
手順4: font-setting.xmlファイルの編集を終えたら、セーブしてからメモ帳を閉じてください。
手順5: MODをリロードしてください。

以上でフォントの変更を完了しました。
お疲れさまでした。

E) 想定問答集
  • Q: なぜ一部のDLCだけを含んだ日本語化なのか。
    A: このゲームが欲しくて買ったわけではなく、Humble Bundleでバンドルされていた内容がたまたまそれだったからです。
  • Q: インストール手順が分からない。
    A: 直ちにインストールを中断し、じっと誰かが何とかしてくれる日までお待ちください。いつかインストールできる日が来ることを心よりご祈念申し上げます。
  • Q: 動かない。
    A: 直ちにアンインストールしてください。
  • Q: フォントが変わらない。
    A: 直ちにアンインストールしてください。
  • Q: 訳文が不自然。
    A: 私がやった作業は英文を機械翻訳にかけただけだからです。
    むしろ、あなたのような言い出しっぺの方が翻訳作業に従事しやすい環境を整えるために翻訳作業シートを作り、作業シートからXML MODを自動生成するツールをこしらえたわけですのでありますから、ぜひご活用いただきたく存じます。
    なお、こちらでworkshopで公開なさっておいでの「Japanese locale / 日本語化 (β)」中の訳文が用語の統一や自然な日本語であることで大変優れておりますので、勝手ながら ver1.21で流用できる訳文は流用させていただいております。問題があるようでしたらご連絡ください。
  • Q: 聞いているのではなく自然な訳文にしろということである。日本語もわからないのか。
    A: 直ちにアンインストールしてください。
  • Q: 訳文はともかく、改行位置がおかしい。
    A: 今回は自動生成ツールの設定で自動強制折り返し位置を有効にしたデータをお配りしているからです。
    ご自分で改めてXMLを自動生成しなおせば自動強制折り返しを無効にもできるし折り返し位置も変更できます。
    そもそも表示するフォントの文字の大きさと表示先の画面パーツの大きさによって訳文そのものを調整しない限り、この問題は解決しません。
    前述の翻訳作業シートおよびXML MODを自動生成するツールをご活用ください。

2019年3月27日水曜日

Offworld Trading Company MODの作り方

もはや日本国内ではプレイヤーはいなさそうですが、まずはこちらの映像をご覧ください。
上記の内容だけで表題の件は完了なのですが、その内容を含めて今回のVer1.21の日本語化に当たってOffworld Trading Company(以下OTC)のMOD作成でわかったことを、今後の備忘録もかねて記録しておきたいと思います。

・このゲームではMODといっても実装方法に二種類用意されている。

もともとあるシステム上のデータを改変する場合は、予め定義されているXMLファイルの値を変更したうえでMODとして配置します。ゲームシステムの改変というより、ゲームシステム上で動くパラメータの変更に適しています。
この場合は、どのXMLで何が定義されていて、その定義はどういう意味を持っていて、どういう場合に使用されて、何の追加が許されて何が許されないのかという、ゲーム開発元が決めたルールだけがすべてとなりますので、そのオレオレルールの知識が不可欠となります。このため、プログラム作成によるMODより敷居が高いと思います。最低限のルールとして、zType要素は全システムでユニークな識別子であること、です。これを基準に
日本語化するMODも、実際には単純に表示データの改変をするだけですから、こちらの範疇に入ります。

一方、ゲーム製造元会社様が用意したインタフェイスに従ってバイナリを書くと、それを読み込んで実行してくれるという機能も実装されています。
このゲームもその辺の掃いて捨てるほどあるUnityゲームの一つにすぎません。
ですので、こちらの方法では、いつも通りの手順でプログラムを作成すればゲームシステムの改変や新規機能の追加、OSとの連携など、かなりのことができます。むろん、その辺のMOD使用可能なUnityゲームと同様に、できないことは全くできません。
XMLの改変とバイナリプログラムは組み合わせることによって、同時に一つのMODとして統合して運用することができます。
たとえば、日本語化をした際に日本語表示用にもっときれいなフォントで表示させたい、といったような用途では、プログラムでゲーム中で使用されるフォントを変更する必要があります。そこへ日本語化データのXMLを組み合わせるとプログラム+XMLのMODが一つ出来上がるという寸法です。
本記事では、最終的にその具体例を示す所存です。

・MODは一種類しか同時にゲームシステムに適用できない。

この理由により、MODがアンロードされても改変結果が破棄されないようなMOD(この具体例は後でプログラム作成を伴うMODをご説明する際に触れる予定です)を除き、他のMODに影響を与えるMODを作成することが困難です。

・このゲームは何かというと本体とは別のMODとして実装されている。

たとえば、チュートリアルもMODですし、シナリオもMODですし、練習戦(Practice Challenges)もMODです。
従って、前項の理由により、日本語化MODと同時に適用できませんから、それぞれのMODを個別に日本語化しなければなりません。

・MODの配置先は内部的に二か所に分かれている。

これはユーザMODと公式MODの配置先を分けたかったものと見えます。
実際には自作MODをどちらに配置したところで動作は同一です。
大きな違いは、どちらに設置したかによってMODのrootディレクトリが変わることです。
このゲームはゲームプログラム中でMODのrootディレクトリをハードコーティングで決め打ちしています。
この際、Windowsではドライブレターという概念がありますので、ユーザMODと公式MODの配置先が別ドライブだった場合、基本はユーザMOD用ディレクトリにデータを置いておき、必要最低限部分だけを公式MODに反映したうえで絶対パスで別ドライブにあるユーザMODのデータを参照させようという芸当ができません。当然相対パスでも別ドライブを参照できません。

従って、日本語化MODのようにあちこちのMODに日本語化データをまき散らす必要がある場合、公式MOD用のディレクトリにおとなしくインストールしたほうが有利になります。

どちらに配置すれば何にアクセスできるのかを見極めて配置先を決定する必要があります。

・MODの開発に必要な元ネタの場所とMODの配置先

さて、上記まで説明したので、具体的に何がどこにあるか、またはどこへ配置すべきかに触れておきます。
  1. 元ネタのXML
    [user]\Documents\My Games\Offworld\Mods\Hidden\Reference\Data
  2. プログラムで使用されているライブラリのソース
    [user]\Documents\My Games\Offworld\Mods\Hidden\Reference\Source
  3. ユーザーMODの配置先
    [user]\Documents\My Games\Offworld\Mods\[任意のディレクトリ名]
  4. ユーザMODで変更したXMLファイルの配置先
    [user]\Documents\My Games\Offworld\Mods\[任意のディレクトリ名]\Data
  5. ユーザMODで作成したプログラム(クラスライブラリDLL)の配置先
    ユーザMODの配置先と同様。
  6. クラスライブラリから参照するファイルの配置先(MODの設定ファイルなど)
    特に固定化されてはいません。
    C#からアクセスできるところであれば世界中どこでも。
  7. 公式MODの配置先
    [steamインストールdir]\steamapps\common\Offworld Trading Company\Offworld_Data\StreamingAssets\Mods
  8. チュートリアルMODの配置先
    [steamインストールdir]\steamapps\common\Offworld Trading Company\Offworld_Data\StreamingAssets\Mods\Hidden\Tutorials
  9. Jupiter's Forge Expansion Packチュートリアルの配置先
    [steamインストールdir]\steamapps\common\Offworld Trading Company\Offworld_Data\StreamingAssets\Mods\Hidden\IoTutorial
  10. Practice Challenges(練習戦)の配置先
    なし。
    標準ではUnityのアセットを直接参照しています。但し、scenario.xmlでMODディレクトリの参照先を変更できますので、scenario.xmlを改変し、そこにMODディレクトリを明示したMODをまず生成(ややこしい)したうえで、そのMODディレクトリに改変したいデータを設置することで改変が可能となります。

・MODの異常はすべてoutput_log.txtに記録される。

これはOTCに限らずUnity上で動くゲームすべてに言えることですが、Japanese locale / 日本語化 (β)を作った作者さんへのコメント欄が「動かない」「ナントカしろ」挙句の果てに「対応しろ」ばっかりだったので本当に気の毒なのであえて特記します。
特に気の毒なのは、output_log.txtを見れば一瞬で分かることなうえに加えて、すぐに自分で直せるような内容なのにもかかわらずこの有様なのはもうまことに気の毒でしょうがない。
私も他ゲームになりますが、私がsteam上で公開しているMODでも変な要望や願望や斜め右上の妄想を持ち込んでくる人はいますが、こんな苦情しか言えないような低レベルな人が大量に湧き出てきてしまっている光景は気の毒で気の毒で仕方がありません。率直に言ってこんなひどいのが集まってるのを見たことがない。
本当に気の毒で、それで個人的にはやりもしない、実際に日本国内のプレイヤーもいなさそうなゲームの翻訳に手を付ける気になったわけで、本記事もそれがもととなって書かれていますので敢えて項目として挙げました。
上記はおくとしても、このことは大変重要です。釈迦に説法かもしれませんが、うまく動かない時はやみくもに手元のMODをいじらないで、このログファイルをまず確認して何が起きているのかを把握したうえで次の行動に移ることが肝要です。

・XML MODの改変方法は4通りあり、それぞれファイル名の命名規約が異なる。

以下の三通りがありました。
  1. オリジナルのXMLを丸々書き換える
    これはオリジナルのXMLの項目数やエントリ名をそのままに、中身だけ変更する場合に適用可能です。
    具体的には、日本語化を例にとると、ほかの内容はそのままに、Englishの項目の中身だけを全部日本語に置き換えてしまうといった場合に使用できます。
    この場合のファイル名の命名規則は「オリジナルと同じ」であること、です。
  2. オリジナルのXMLの特定項目だけを変更したXML
    これは必要なzType要素を持つ要素だけに対して変更を加えた内容のXMLファイルを作成したいときに使用します。
    たとえば、Practice Challenges MODの参照先ディレクトリだけを変更したファイルを作成する場合、ほかのzTypeは変更する必要がないのでMODとしては不要だからファイルに記載したくない、という場合に使用します。不要部分も一緒に持ってきちゃう場合は項番1の方法で構いません。
    この場合のファイル命名規則はオリジナルの拡張子を除いたファイル名に「-change」を追加します。
  3. 全く新しいzType要素を追加したXML
    これはオリジナルのXMLにない項目を追加したい場合に使用します。
    この場合のファイル命名規則は、オリジナルの拡張子を除いたファイル名に「-add」を追加します。
  4. 既存のzType要素を削除したい。
    これはまずremove.xmlというファイル名のxmlを用意し、Entry要素1つに対してType名を1記述します。削除対象が複数ある場合はEntry要素を削除したいType名の数だけ記述します。
    ファイル命名規則はremove.xmlという名前で決め打ちです。
  5. 全く新しいXMLファイル名を用意したい
    ゲームシステム上で読み込むファイル名が決め打ちなのでXMLファイルの変更だけでは困難です。

・プログラム(クラスライブラリ)MODの基本

Unity上で動作しているMono上で動作します。
Unityのバージョンが5.3のため、Monoの.NET相当バージョンは3.5です(古いゲームだから仕方がないけど・・・うんざりするんだよなあ)
従って、プロジェクト生成時やコンパイル時にそのように指定しないと、Monoが理解できないILを生成されたり、うっかりUnity上では用意されていないライブラリを使ってしまってあとから丸々作り直し、なんてことになります。
まあ、同じ3.5でもMonoとUnityと.NET間で微妙に使えなりクラスライブラリなどの差異があることはあえて触れるまでもないでしょう。XMLまみれの本ゲームにおいてはXmlDocmentはあるけどXDocumentがないヨ、とかね。

さて、どういう手続きを踏めばゲームから呼び出されてくれるのかというと、クラスにModEntryPointAdapterクラスを継承するだけで呼び出されることが可能な状態になります。

そのうえで例によってModEntryPointAdapterで定義されたInitialize()やOnGUI()、Update()などの仮想関数をオーバーライドすることによって、その関数がゲーム本体から呼ばれます。
いうまでもありませんが、クラスライブラリのDLLは最低限、UnityEngineおよびCSharp-Assembly-firstpassを参照している必要があります。
以上までが基本です。
あとはUnityで出来ることとCSharp-Assemblyに定義されたゲーム会社が用意するインタフェイスを通してできることは何でもできますので、この記事で説明が必要なことはもうありません。

・実際にOTC用のプログラムMODを作ってみましょう。

今回の日本語化MODですと、ゲームシステムで使用されているフォントが全部欧文用であり、日本語用のフォントが入っていません。
そのため、unityによってfallbackされて、巡り巡ってOSのフォントまでさかのぼり、最終的にWindowsの場合はMSゴシックあたりに日本語のデータがあるということで、そのフォントで表示されます。
フォールバックしてくれる仕組みが備わっているので日本語としてちゃんと表示されること自体は誠にご同慶の至りではありますが、固定ピッチなので若干間延びして見えてしまいます。

そこで、フォントを変更するMODを作ってみたいと思います。

しかしながら、そのままだとXMLファイルにはフォントの定義が一切ないので、changeもaddもremoveもできません。

そのため、プログラムを作り、フォントをどうにかしたいと思います。

まず、ゲームのアセットに入っているフォントを使用しないで新しいフォントを用意して、各GUIパーツに対してフォントを適用しなおす・・・なんてことをやりたければやればいいと思いますが、この際は折角フォールバックする仕組みがあるのですから、ゲーム内で使用されているフォントのフォールバック先を定義してしまえばお手軽です。

直接アセットバンドルを覗けばいいんですが、あんなのチマチマとひとつづつ確認してたら日が暮れちゃいますし、なんか問題もありそうなので、まずフォントを列挙するMODを作ります。これならだれもケチはつけられんでしょう。

というわけで、こういう形で作ってみました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class EnumFonts : ModEntryPointAdapter
{
  public override void Initialize()
  {
    base.Initialize();
    EnumFonts();
  }
  void EnumFonts()
  {
    Font[] fontarr = Resources.FindObjectsOfTypeAll<Font>();
    if (fontarr == null)
    {
      return;
    }
    int count = 0;
    foreach (Font font in fontarr)
    {
      count++;
      log(count.ToString() + ": " + "font.name: " + font.name);
      foreach (string fn in font.fontNames)
      {
        log(count.ToString() + ":   fallback to:" + count.ToString() + ": " + fn);
      }
    }
  }
}

説明するまでもありませんが、念のため何をしているのか説明します。
  1. ModEntryPointAdapterを継承したクラスを作る
  2. Initialize()関数を経由してゲーム内リソースに含まれるフォントを列挙する関数をcallする
  3. 結果をoutput_log.txtに出力する

ここまででこの項目の表題は完了してしまったわけですが、とりあえず進めます。
このMODをビルドして前項でご説明したMOD配置先に配置してOTCを一回起動します。
すると現今では次のフォントが採用されていることが分かります。
  • Arial
    皆さんおなじみ、Unityの標準フォント。
  • TitilliumWeb-SemiBold
    フォールバック先はTitillium Web
  • FiraSans-Bold
    フォールバック先はFira Sans
  • TitilliumWeb-Regular
    フォールバック先はTitillium Web
  • FiraMono-Bold
    フォールバック先はFira Mono
  • boston_traffic
    フォールバック先はBoston Traffic

以上のフォントのフォールバック先を片っ端から書き換えてしまえば目的のMODの完成です。
直接MS UI Gothicとか直接代入しても目的は果たせますが、それではややいかがなものかと思われますので、フォントの設定を外部ファイル化してみたいと思います。
それが以下になります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
public class FontFallbackModifier : ModEntryPointAdapter
{
  const string MODNAME = "Avocado";
  Dictionary<string, List<string>> targetFontNamesDic = new Dictionary<string, List<string>>();
  public override void Initialize()
  {
    base.Initialize();
    ReadFontSettingXML();
    ModifyFallbackFontnames();
  }
  void ModifyFallbackFontnames()
  {
    Font[] fontarr = Resources.FindObjectsOfTypeAll<Font>();
    if ( fontarr == null )
    {
      return;
    }
    foreach ( Font font in fontarr )
    {
      if ( !targetFontNamesDic.ContainsKey( font.name ) )
      {
        continue;
      }
      if ( targetFontNamesDic[ font.name ].Count == 0 )
      {
        continue;
      }
      font.fontNames = targetFontNamesDic[ font.name ].ToArray();
    }
  }
  void ReadFontSettingXML()
  {
    targetFontNamesDic = new Dictionary<string, List<string>>();
    string fname = Application.streamingAssetsPath + "/Mods/" + MODNAME + "/font-setting.xml";
    if ( !File.Exists( fname ) )
    {
      log( fname + " is missing" );
      return;
    }
    var xml = new XmlDocument();
    xml.Load( fname );
    XmlElement root = xml.DocumentElement;
    XmlNodeList eles = root.GetElementsByTagName( "Font" );
    if ( eles == null )
    {
      log( "eles is null" );
      return;
    }
    foreach ( XmlElement ele in eles )
    {
      if ( ele == null )
      {
        log( "ele is null" );
        continue;
      }
      XmlNode s = ele.SelectSingleNode( "Source" );
      if ( s == null )
      {
        log( "s is null" );
        continue;
      }
      if ( s.InnerText.Trim().Length == 0 )
      {
        log( "s is empty" );
        continue;
      }
      XmlNodeList fts = ele.GetElementsByTagName( "FallbackTo" );
      if ( fts == null )
      {
        log( "fts is null" );
        continue;
      }
      string key = s.InnerText.Trim();
      foreach ( XmlNode d in fts )
      {
        if ( d.InnerText.Trim().Length == 0 )
        {
          log( "d is empty" );
          continue;
        }
        string value = d.InnerText.Trim();
        if ( targetFontNamesDic.ContainsKey( key ) )
        {
          targetFontNamesDic[ key ].Add( value );
          Debug.Log( s.InnerText + "+=" + value );
        }
        else
        {
          targetFontNamesDic.Add( key, new List<string>() { value } );
        }
      }
    }
  }
  void log( string s )
  {
    Debug.Log( "Avocado: DEBUG: " + DateTime.Now.ToString() + ": " + s );
  }
}
フォントを変更する箇所より設定ファイルを読みだすほうがコード的に長くなってしまいました。もっとも、ほとんどエラーチェックですが。

実は一つ問題があって、MODがロードされるタイミングによっては、MODを再ロードするまでフォントの変更が画面上に反映されないということがあります。

原因はいろいろ考えられますが、現状の動作を見る限りでは、今回作ったMODではInitialize()でMODがロードされたときに一回だけ実行する関数内でフォントの変更をしていますが、このInitialize()がcallされる前にOTC本体側の画面の構築が完了する場合があるようで、そのケースで発生する事象のようです。

試しに、Initialize()関数内で無理やり5秒ほどSleep()して止めてしまうと初回起動時からフォントが正しく変更されていました。まあ、この手法はあまりにもばかばかしいので数回テストしただけでやめちゃいましたから本当にそれでいいのかは疑問符つきです。
今度は逆にUpdate()関数内で1000フレームほど経過してからフォント変更処理を走らせてみると、文字全部がいったん崩れてしまいますが、マウスカーソルを当てるなどして色変更などの処理が走る(再描画処理が走る)パーツについてはフォントが切り替わることは確認しました。
だからと言って、各画面内パーツに片っ端からInvalidateをやらせる処理をゴリゴリ書くほどフォントを変えたいというわけでもないし、フォントはゲーム起動後アンロードされないので、別MODを起動するとか言語設定を他に変えてから戻すとかマップエディタを起動して戻るとかで対応可能ですからやりません。

これは日本語化XMLにも同様の事が言えます。
MODで用意したXMLがロードされる前に画面の構築処理が走る場合があるようで、再ロードしない限り画面上に翻訳文が反映されないという現象がありますが、おそらくこれと同根ではないかと思います。

要するに、(Unity用語の)シーンの切り替えによって画面の再生成が行われればいいんだろうと思います。

この問題は認識済みで、かつ対応する気はサラサラ、これっぽっちも、微塵もありませんので、予めご了承ください。

なお、このMODのソースファイルは、以前ご紹介したスプレッドシート上にある日本語翻訳文をXML化するツールに同梱してありますので、必要ならご参照ください。

さて、随分長くなってしまいました。成果物だけクレクレ様もおられるでしょうし次の記事でXMLとプログラムをまとめたMODとして実際にゲームに適用できるようにした現物と、ゲームへの適用方法についてご説明いたします。

ここまでお読みいただいてありがとうございました。
ま、世界で一人でもいたら奇跡ですわね。

2019年3月22日金曜日

Offworld Trading Companyの和訳に関してもう少し突っ込んで調べて(機械翻訳も全文して)みた

ゲームそのものには興味は失せていますが、ゲーム自体よりもこういうほうを調べたりするほうが楽しいので少々表題の件について調べてみました。

ついでと言っては何ですが、例によってGoogle Spreadsheetに翻訳作業所を用意し、これまた例によってシートからゲームに適用できる形式に変換するツールも作ってみました。
ツールの使い方は・・・見ればすぐわかります。画面上に日本語で書いてますから。
まあ、直感でやってください。
ソースはついてます。なお、エラーチェックなんぞしてません。
これまた毎度恒例ですが、翻訳作業所の編集権は制限しております。
まあ、どうせ誰も困らないのでどうでもいいですが、制限する理由について気になった方は過去の当ブログをご参照いただければ幸いです。

このシートにはworkshopで公開なさっておられるJapanese locale / 日本語化 (β)の訳文も反映しましたが、問題があればお知らせください。

もうworkshopのmodも最終更新が2年まえですから、もう更新は望めないでしょうし、いまさらHumble Bundleで手に入れてしまった以上、やれることはやっとこうという感じでありますので、一つご容赦ください。

現時点でのシートの状況は、本体、Core Game, Almanac DLC, Jupiter's Forge Expansion Pack中の(要するにHumble Bundleでバンドルされていた)すべての英文(7,363センテンス)の機械翻訳を完了し、原文と和文でのタグの不整合がないように全文をチェックを完了しています。
用語の統一、自然な日本語への再翻訳、ゲーム内での表示妥当性の全文のチェックはしていませんし、する気もございません。

以下、結果だけクレクレ様には何のかかわりもありません。
お読みいただいてありがとうございました。

さて、全部手動で日本語化をやるとした場合、どうするかを書き連ねてまいります。
まず、言語の追加は、特にそれ用の手段は用意されていませんでした。
そのため、表示される文字列を改変するMODとして作成し、そのMODをゲームに適用しなければなりません。

このゲームではMODといっても、DLLを作らなくても予めひな形として用意されているXMLファイルを書き換えるだけでも立派にMODとして適用できます。
元ネタは
マイドキュメント\My Games\Offworld\Mods\Hidden\Reference\Data\ フォルダに格納されています。

日本語化MODを作成する場合は、そのフォルダに格納されているXMLファイル中にあるすべての"English"という要素を片っ端から日本語に書き換えればOK。
但し、元ネタを書き換えてもそのままでは適用されません。
書き換えたら、たとえばOreOreLangという名前のMODにすると決めた場合、単純に
マイドキュメント\My Games\Offworld\Mods\OreOreLang\Data\ フォルダ
に書き換えたファイルをコピーしてあげれば立派にMODとして成立します。
イタリック体で書いたMOD名でゲーム内から参照できます。
なお、中身がカラッポでもマイドキュメント\My Games\Offworld\Mods\内部にフォルダさえあればMODとしてゲーム内から選択可能になります。

特にフォントや体裁にこだわらなければ、上記の手順だけでチュートリアル以外は日本語化できます。

Englishを直接書き換えるんじゃなくてJapaneseとして選択可能にしたい!という場合は、language.xmlも書き換える必要がありますが、まあ、その辺はお好み次第です。
(ツールではlanguage.xmlも自動生成します)

いずれにせよ、片っ端から日本語化する場合、現在のバージョンでは20ファイル、7,363もの文章を翻訳する必要があります。

しかし、ここで罠があります。
いくら頑張ってもチュートリアルには日本語化MODを適用できません。
なぜならば、チュートリアルもまたMODだからです。

このゲームはMODはゲーム中に1つしかロードできません。
従って、チュートリアルがロードされると、ほかのMODはゲーム内から破棄されます。
そのため、日本語化したデータが適用されません。

実は最初、それが分からなくてちょっとしたプログラムでMODを作って何とかならんか、とごちゃごちゃやってみたのですが、どうやってもチュートリアルに入ると自作のプログラムMODが実行されなくなってしまいます。
どうもアンロードされてしまっている様子でした。

そこで、さらによく調べてみたら、チュートリアルもMODだったというオチでした。
これはXMLでのMODも同じ挙動と考えられます。
これでお判りいただけたかと思いますが、先にチュートリアルとBold体で「等」を強調したわけがお分かりいただけたかと思います。
いうまでもありませんが、チュートリアル以外でもMOD扱いであればいくら翻訳を頑張ってもこのままでは反映はされません。

では、せめてチュートリアルだけでもどうするか、という具体的な話ですと、逆手(?)を取って、チュートリアルもMODである以上、MODとしての日本語化したXMLファイルをチュートリアルMODに追加してあげればいいわけです。

追加といっても、単純にチュートリアルMODのフォルダに日本語化したXMLをコピーするだけです。
具体的には、チュートリアルMODは
Steam\steamapps\common\Offworld Trading Company\Offworld_Data\StreamingAssets\Mods\Hidden\Tutorials\Data
にありますので、そこに先ほど作成した20個(language.txtを改造した場合は21個)のXMLファイルをすべて(じゃなくてもいいんですけど何が必要なのか調べるのか面倒なので)コピーしてあげれば日本語化されます。
イオ関連のチュートリアルの場合はTutorialsフォルダと並んで設置されているIoTutorialフォルダです。

練習戦の日本語化の場合、一ひねりが必要です。これもMODなのですが、デフォルトではMODフォルダが未指定なのです。Unityのアセットだけを参照しているわけですね。
ではどうするか。
はい、ここまでお読みいただいている方には簡単にお察しいただけます通り、単にMODフォルダを指定してやればいいだけです。
MODフォルダを指定する定義はsenario.xmlにて記述されています。
実際に、そのなかで<zMod/>なんて書いてある要素に対して片っ端から日本語化されたテキストを突っ込んであるフォルダ名を入れちゃえばちゃんと日本語化されました。
これは横着してTutorialフォルダを指定しないほうがいいと思います。言語以外にもチュートリアル用の特殊な設定が適用されてしまいますので。

まずは上記までで、一通り日本語化するための手順は網羅的にご説明できたかと思います。

さて、日本語化しても、今度は表示上の問題があります。
チュートリアルの登場人物のセリフはばかに長いので、適切な長さで改行させないと猛烈に小さな字で表示されてしまいます。

このゲームでは表示領域内に収まらない場合はフォントを小さくして対応している箇所があるのですが、日本語はホワイトスペースで区切らない言語のため、自動折り返しができず、横に猛烈に長い文章となってしまい、横幅が収まるようにフォントを小さくされてしまうので、結果的に文字が猛烈に小さくなってしまうというわけです。

一応、念のために無幅空白(U+200BとかU+200Cとか)を一文字ごとに挿入したらどうなるかという実験をしてみましたが、無幅空白では折り返されませんでした。ワードラップ適用対象はU+0020だけのようです。残念です。

従って、見栄えを求めるなら自分自身で改行位置を決定する必要があります。
むろん、見栄えを求めなければ文字がやたらめったら小さいだけでハングアップしたりするようなことは今のところなさそうですので、まあ不幸中の幸いです。

とりあえずコピペだけで20+1のファイルに和訳用データを落とせる仕組みを作りましたので、とりあえずはこのゲーム代として支払った額分はしっかり楽しめました。

なお、翻訳者は(これまで手掛けたゲーム翻訳すべてで)常に大募集中でございます。
ご協力を賜れば幸甚です。
ま、過去にひっとりもいないのですけど。

お読みいただき、まことにありがとうございました。

2019年3月21日木曜日

Offworld Trading Company V1.21.27358で日本語化mod2017年7月31日版を有効にする方法

先日、Humble BundleにてSTRATEGY BUNDLE 2019として8個ものストラテジゲームがバンドルされているという絢爛豪華なラインナップがありました。
まあ、8個目を得るには$15必要なのですが、それで得られるのがCiv6なので(3,4,5を所有して5の出来の悪さにがっかりして二度と新作は欲しくないので)遠慮して、残りの七つを(積みゲーになることが分かっていながら)頂戴してしまいました。

まずはPlague Inc.をやってみましたが、あまりの底の浅さに全病原体を解放したところでギブアップ。なんであんなのが話題になったのかさっぱりわからない。

お次にOffworld Trading Companyというのをやってみました。
Steam workshopに日本語化MODが登録されていたので、さっそくsubscribeしてみましたが、さっぱり日本語化されません。

仕方がないので英語のままざっくりとtutorialを終えましたが、やっぱり日本語化modがあるなら使ってみたい。
そこで、なぜmodが動かないんだろうと軽く調べてみました。

幸いゲーム本体がunity製だったのでoutput_log.txtを見りゃ何とかなるだろうと思ってみてみたら、ちゃんとエラーが出ていたので、何とかなりました。
使用できない原因は、単純に日本語を適用したい先のIDがなかったり変更されたりしているだけのようです。
数ヶ所の変更だけで使用可能になりましたので、日本語化なさった上にmodまで制作された方へのリスペクトとしてとりあえずメモしておきます。

具体的には、Steam/steamapps/workshop/content/271240/824084983/text-game-change.xmlファイルを次のように改変しました。

作業対象となるzType
削除TEXT_HUDHELP_BLACK_MARKET_NO_TIMER
追加<Entry>
<zType>TEXT_HUDHELP_BLACK_MARKET_COOLDOWN</zType>
<Japanese>Cooldown: {0}</Japanese>
</Entry>
削除TEXT_HUDINFO_NEEDED_TO_LAUNCH
削除TEXT_EVENTTURN_TRANSPARENT_ALUMINUM_OR_FINANCIAL_INSTRUMENTS
追加TEXT_EVENTTURN_PERPETUAL_MOTION_OR_FINANCIAL_INSTRUMENTS
削除TEXT_EVENTTURN_TRANSPARENT_ALUMINUM_OR_FINANCIAL_INSTRUMENTS_TITLE
追加TEXT_EVENTTURN_PERPETUAL_MOTION_OR_FINANCIAL_INSTRUMENTS_TITLE
なお、作業欄はzTypeだけでなくエントリごと行う前提です。
上記を行って、日本語化がチュートリアルやスカーミッシュゲームに対して適用されたところまでは確認しました。

まあ、このゲーム自体は英語版でのチュートリアルを一通りやった感触としては、破壊工作とかおなか一杯気味なのでこのまま積んでおくことになりそうです。

それにしても、これ一人で全部訳したのかあ、とつくづく頭が下がる思いです。
そのため、敬意を表してここに駄文を記す次第です。

2019年1月12日土曜日

The Guild II:Renaissance + MegaModPack 0.95用日本語化

あけましておめでとうございます。
なんでいまさらThe Guild II:Renaissance + MegaModPack 0.95なのか。
それは、以前0.8用の翻訳に手を付けてしまったからです。。。
自業自得です。
そういえば0.8用の翻訳も正月早々にやったんでした。

で、超めんどくせーと思いながらMegaModPack 0.82とMegaModPack 0.95の追加・変更されたテキストを翻訳してみることにしました。

とりあえず差分を比較してみた結果、追加607件、変更221件、削除72件、合計900件もの変更がありました・・・

結局翻訳は手動でやるしかないわけで、追加変更分を涙目になりながら翻訳シートを埋めてみたのがこちらです。ま、ほとんど機械翻訳なんですけどね。
後期中年者から初期老齢者の境目になるとコピペ900回とかかなりツラい。
Weblioさん、Google Translateさん、いつもありがとうございます。
Google Translateさん、奇妙な造語をするクセ、なんとかしてください。

しっかし、あれですな。
久しぶりにシートを眺めると、機械翻訳のままのところ、本当にひどい。
以前からわかっちゃいるけど、訳文、18,000行くらいあるしね。一人じゃ無理だし、そもそも全然需要がないからいいよね。

で、成果物はこちら0.82用との絡みでファイル名をText095.dbtに変えてあります。ダウンロードしたらText.dbtにリネームして使ってください。
ついでに、気に入らない翻訳があったら是非直すのにご協力いただければ幸甚です・・・まぁねえよな

そんなわけで本年もよろしくお願いいたします。


おまけ:
シートから直接dbtを作るスクリプト
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python3
 
#
# MegaModPack0.95用翻訳「元」シートから0.095用のText.dbt を生成するpython3 script.
# input:
#       1. 0.95用シートのtsv
#          tg2tsv_095.tsv
# output:
#       0.95用のText.dbt
#           Text095.dbt
#
 
import re
 
def make_dbt():
  regpattern = r"^\"*(\d+)\"*\t\"*(.*)\"*\t\"*(.*)\"*\t\"*(.*)\"*\t\"*(.*)\"*\t\"*(.*)\"*\t\"*(.*)\"*\t\"*(.*)\"*\r*\n$"
  reg = re.compile( regpattern )
   
  with open( r"tg2tsv_095.tsv", "r", encoding="utf-8" ) as fin:
    dic = dict()
    for line in fin:
      m = reg.match( line )
      if not m :
        continue
      index = m.group(1)
      id = m.group(2)
      en082 = m.group(3)
      ja082 = m.group(4)
      vanilla = m.group(5)
      en095 = m.group(6)
      ja095 = m.group(7)
      type= m.group(8)
      if ja095 == "" and en095 != "" :
        raise TypeError('Japanese translation is not completed!!')
      dic[ int(index) ]  = [ index , id, ja095 ]
 
  keys =  sorted(dic.keys())
  isfirst = True
  with open( r"Text095.dbt", "w", encoding="utf-16" ) as fout:
    for key in keys:
      if isfirst == True :
        isfirst=False
        fout.write("//Table File\n//by RUNEFORGE Game Studio\n//Translated by ttgcameback.blogspot.com\n\nTable Description:\n\"id\" INT  0   |\"label\" STRING  0   |\"english\" STRING  0   |\n\nData:")
        fout.write("\n")
      arr = dic[key]
      line = arr[0]
      line +="   \""
      line += arr[1]
      line +="\"   \""
      line += arr[2]
      line += "\"   |\n"
      fout.write(line)
 
make_dbt()