星空のメモリアの例でのVNのローカライズ

オタクはVNを読まないと言ってはいけません。 したがって、 ビジュアルカードが1つあります(vndbのプロファイル)

ところで、非常に良い、8.06の評価、一定量の変態を気にしない人に読むことをお勧めします。

まあ、一般的には、この記事についてではありません。

英語のパッチがあり、中国語があります。 ロシア語はありません。 不公平。 修正してみましょう。

あなたが必要になります:







リサーチ



新しいゲームを始めましょう。最初の文- 「俺は彼女が好きだった」を見てください 。 英語版では- "私は彼女が好きだった" 覚えておいて、これはまだ私たちにとって有用です。 ゲームを閉じて、フォルダに移動します。 そこには、 「Memoria.hcb」「MemoriaEN.hcb」という疑わしいファイルがいくつかあります。 最初のものから始めましょう。 16進エディターで開くと、最初の4バイトが表示されます。 非常によく似たもの。 ..En.hcbで確認し、これがファイルサイズからビッグエンディアンの2029バイトを引いたものに過ぎないことを確認します。 最後の2029バイトは、ゲームが閉じられたときに呼び出されるコードによって占有されます(画像を滑らかにシェーディングし、別れのテキストを表示します)。 原則として、これを変更する必要があるかもしれませんが、そのようなオプションではローカライズは必要ありません。



今、 「私は彼女が好きだった」というフレーズを探しています。







複数の行を連続して書き込むと、特定のパターンが表示されます。



これもまたある種のオフセットであるとします。 さて、それに従ってみましょう。 「俺は彼女が好きだった」というフレーズの直後にいる まあ、ほとんど。 最初の数文字は、ある種の混乱に変わりました。







最初の5バイトは同じ06 XX XX XX XXに置き換えられます 「私は彼女が好きだった」 、またはむしろ、0Eの始まりは、このアドレスに隠れているだけです。 行の後、ある種の魔法が始まります。 シーケンス08 08 08 02 E3 89 04 00 02 7F 98 04 00は、マウスクリックまたはキーストローク-次の行への遷移の確認を待つことが実験的に確立されました。 これはまだ便利です。



要約すると、次の構造が得られます。

元のスクリプトでは:

0E <サイズ、1バイト> <テキスト、サイズバイト、エンコードされたShift-JIS > <13バイト、プレーヤーがテキストを読んだことの確認>

<いくつかのチェック、それらに応じて<06次の行または別のストーリーラインへの移行>>



英語のパッチでは、古典的なトリックが使用されました-特定の場所にコードを貼り付ける必要があるが、アドレス指定をノックダウンすることができない場合、ファイルの最後にジャンプを入れて、私たちは仕事をして戻ってください。



したがって、元の行の5バイトは、<06> <ファイルの元の終わりよりも少し離れたアドレス>に変更されます。

<0E> <その13バイトチェックにジャンプ>があります



テキストのリッピング



急いで、スクリプトからテキストをリッピングするユーティリティが書かれました

ラジエーター
#include <conio.h> #include <iostream> void main() { FILE *en; FILE *jp; FILE *out; en = fopen("MemoriaEN.hcb", "rb"); jp = fopen("Memoria.hcb", "rb"); out = fopen("out.txt", "w"); fseek(en, 0x8c827, SEEK_SET); fseek(jp, 0x8c827, SEEK_SET); unsigned char todo_en[50]; unsigned char todo_jp[50]; unsigned char size_en; unsigned char size_jp; char str_en[255]; char str_jp[255]; unsigned int off_en; unsigned int off_en2; unsigned int off_en3; bool r = true; do { fread(&todo_en, 1, 1, en); fread(&todo_jp, 1, 1, jp); if ((todo_en[0] ) != 6 || (todo_jp[0] ) != 14) break; fread(&size_jp, 1, 1, jp); fread(str_jp, size_jp, 1, jp); fread(&off_en, 1, 4, en); fseek(en, off_en, SEEK_SET); fread(&todo_en, 1, 1, en); if ((todo_en[0] ) != 14) break; fread(&size_en, 1, 1, en); fread(str_en, size_en, 1, en); //fwrite(str_jp, size_jp, 1, out); //fwrite((const char *) "\n", 1, 1, out); fwrite(str_en, size_en, 1, out); fwrite((const char *) "\n", 1, 1, out); fwrite((const char *) "\n", 1, 1, out); fread(&todo_en, 1, 1, en); if ((todo_en[0] ) != 6) break; fread(&off_en, 1, 4, en); if (off_en == 0x0045793e) break; off_en2 = ftell(en); fseek(en, off_en, SEEK_SET); fseek(jp, ftell(en), SEEK_SET); do { fread(&todo_jp, 1, 1, jp); fread(&todo_en, 1, 1, en); if (todo_en[0] == 6 && todo_jp[0] == 14) break; } while (!feof(en)); fseek(en, -1, SEEK_CUR); fseek(jp , ftell(en), SEEK_SET); } while (true); fclose(jp); fclose(en); fclose(out); }
      
      







コードは完全に最適ではありませんが、数秒で2.5メガバイト(またはミビバイト?)のテキストを生成し、その作業に対処します。 中央の2行のコメントを外すと、テキスト-日本語-英語のバイリンガルが収集されます。



ここでは、テキストの後のチェックの形式を詳細に勉強し始めなかったので、少しcheしました。英語と日本語のスクリプトを比較して、次の行の先頭のアドレスを取得します。



テキストを戻す



どうにかしてテキストを翻訳したとします。今度は、それを押し戻すといいでしょう。

別のsoftinka、コードはすでに修正されています、エラーの説明は以下に説明されています。



詰め物
 #include <conio.h> #include <iostream> unsigned char buff[0x7fffff]; unsigned int end; //offset of russian strings unsigned char s[1024]; unsigned int retpos; //return here after russian unsigned char cl[13] = {0x08, 0x08, 0x08, 0x02 , 0xE3 , 0x89 , 0x04, 0x00, 0x02, 0x7F, 0x98, 0x04, 0x00}; //prompt for click unsigned char ro = 250; //max len of string unsigned char todo_en; //must be 0x06 or 0x14 unsigned char size_en; //size of engish string unsigned char jmp = 0x06; //jumps to xx xx xx xx in LE unsigned char stl = 0x0E; //indicates new sring int ts; int k; unsigned char d; int parts; FILE *en; FILE *trans; FILE *text; char str1[255]; void main() { en = fopen("MemoriaENO.hcb", "rb"); //non-modifed english script text = fopen("text.txt", "r"); //translated strings trans = fopen("MemoriaEN.hcb", "wb"); //translated script rewind(trans); rewind(text); fseek(en, 0, SEEK_END); end = ftell(en); //begin writing localization here rewind(en); fread(buff, end, 1, en); fwrite(buff, end, 1, trans); fseek(en, 0x45798e, SEEK_SET); do { s[0] = '\0'; ts = 0; do { fscanf(text, "%c", &s[ts]); ts++; } while (s[ts-1] != 0); fseek(text, 4, SEEK_CUR); //get new string, terminated with \0 fseek(trans, ftell(en), SEEK_SET); fread(&todo_en, 1, 1, en); //if ((todo_en) != 14) break; fread(&size_en, 1, 1, en); fseek(en, size_en, SEEK_CUR); retpos = ftell(en); //english string begin + len = return here after russian fwrite(&jmp, 1, 1, trans); fwrite(&end, 4, 1, trans); //jump to the russian line fseek(trans, end, SEEK_SET); if (ts <= ro) { //if length of line < byte, just write it fwrite(&stl, 1, 1, trans); fwrite(&ts, 1, 1, trans); fwrite(s, ts, 1, trans); end += (ts + 2 + 5); } else { //get maximum amount of words, which does not exceed byte //and write it end += (ts + 2 + 5); do { str1[0] = '\0'; memcpy(str1, &s, ro); strrev(str1); d = strcspn(str1, " "); strrev(str1); str1[ro-d] = '\0'; d = strlen(str1); memcpy(s, &s[d], ts - d); ts -= (d + 1); str1[d-1] = 0x00; fwrite(&stl, 1, 1, trans); fwrite(&d, 1, 1, trans); fwrite(&str1, d, 1, trans); fwrite(cl, 13, 1, trans); end += 15; } while (ts > ro); //write the remaining part of line s[ts] = 0x00; fwrite(&stl, 1, 1, trans); fwrite(&ts, 1, 1, trans); fwrite(&s, ts+1, 1, trans); } fwrite(&jmp, 1, 1, trans); fwrite(&retpos, 4, 1, trans); //jump back to english fseek(en, 5, SEEK_CUR); if (retpos == 0x732aaf) break; //exit in case of last line } while (true); fclose(en); fclose(trans); fclose(text); }
      
      









もちろん、コードのもう1つの「傑作」ですが、必要なことを数秒で行います。



走って 翻訳を試してみてください。 最初の数文は問題ありませんが、

そして突然-ランタイムエラー!

ここでも、16進エディタを使用し、問題のある提案を探しています。 ここにある。 行の長さに1バイトが割り当てられ、翻訳が長くなり、結果としてサイズがオーバーフローし、ゲームはテキストが300バイトではなく45バイトであると見なします。したがって、エンジンは、文の途中にある記号を命令として「実行」しようとします。

これらの13バイトについて思い出してください。 そのため、サイズを超えると、テキストを部分に分割し、最初の部分を指定し、

テキストが終わるまで、クリックを待って、2番目を与えます。 もちろん、翻訳を言い換えることもできますが、安全にプレイすることをお勧めします。



合計





ゲームにフォントSegoe UIをインストールして......







確かに、改行は常に最適とはほど遠く、単語の途中でフレーズが途切れることもあります。

ゲームでは、ギャップの手動表示がサポートされています。このため、チルダを目的の場所に配置する必要があります。 彼がチルダを配置するように、「詰め物」を修正する必要があります。 また、ヒーローとメニューの名前はまだ翻訳されていません。 スクリプトで名前を直接変更するのは簡単ですが、メニューについてはまだわかりません。 より多くのリソースを掘り、グラフィックの一部を再描画する必要があるようです。



Notabenoidで翻訳プロジェクトを作成しました。参加したい人は誰でも、1年で一人でそれを行うことはできません。

ノタベノイド



All Articles