2012年12月31日月曜日

Star Wars The Force Unleashed II


日本語化

archive: 普通のzip
font: .dds, a2f  and swf
 pak2.lp/Game/Disc/UI/Hud/Fonts/PCDX/ .dds , a2f
 pak3.lp/Game/Global/UI/Flash/fonts_en..gfx (swf)
text: utf-8 plain
 pak3.lp/Game/Global/UI/GlobalText.utf8 , GlobalTextPC.utf8

一部フォントは文字コードにアイコンを振ってるのでフォント作成時に混ぜる必要アリ



2012年12月30日日曜日

Crusader Kings II

中華MODでできました

日本語化テスト

※中華MODが1.07bまでしかないので、steam等で1.08まで上げてるとゲームに入ったところで落ちます。

2012年12月29日土曜日

Star Wars The Force Unleashed

日本語化可能と思われ
整合性チェックとかなければ。

archive: *.utf16_pc.lp http://forum.xentax.com/viewtopic.php?f=10&t=3823
text: utf-16 tsv
font: .dds/.a2f

hdr:
 char magic[8] // "2D2Rtlum"
 char unk[8]   // 0 fill
 char type[4]  // "font"
 char unk2[12] // 0 fill
 float tex_w
 float tex_h
 int unk // font width?
 int unk // font height?
 int num
 int unk // 34
 int n_glyph
 int dataofs // glyph start dataofs + x10

short indexes[num]  // map charcode=>glyph
short pad[] // 4byte align

struct glyphs[n_glyph]
 float u0,v0
 float u1,v1
 float u2,v2
 float u3,v3

(u0,v0)  (u3.v3)
 +--------+
 |        |
 |        |
 |        |
 +--------+
(u1,v1)  (u2,v2)

※codeが255までしか無いの(Tahoma14.rtf)はちょっと違うが気にしないことにする

Tahoma14.dds
 ru_Tahoma14.dds
ここでロシア語用のフォントにLatin-1とロシア文字の両方が含まれている。
このことから1byte文字の切り替えではなく、Unicode対応であることがわかる
もちろんテキストデータを見るのが確実だが。



7.62 mm: High Calibre

かな化可能と思われ。

archive: packer/unpacker
text: 暗号化plain text de/encriper (同上) .txt .ini
font: BMP/fonts .ttf .fnt

.fnt:
header:
 int unk  // 0
 int unk  // 256
 int unk  // 256
struct glyphs[0xe0] // 20-ff
 int w
 int h
data:
 まだしらべてないお




2012年12月27日木曜日

2012年12月25日火曜日

Death to Spies

日本語化可能

Death to Spies (初代)
を利用してさらにトリッキーなことをすれば日本語化可能。
ツール整備が面倒なので訳す人がいれば用意する。

日本語化MODを公開する(2)



ゲーム日本語化 Advent Calendar 2012


日本語化リクエスト募集で募集したリクエストの中から

Deep Blackです。





        ∧∧       
       ヽ(・ω・)/   ズコー  
      \(.\ ノ
    、ハ,,、  ̄


本命はSyndicateです。




Syndicate

日本語化可能。



日本語化MOD

font: The Chronicles of Riddick: Escape from Butcher Bayと同じ
text:
archive: see below


All.xcd:

FOOTER:
int num
struct ent[num]
 int ofs
 int zero1 // maybe ofs hi
 int size
 int zero2 // maybes size hi
 int namelen
 char name[namelen]
 char pad[0-3] // x20 fill 4byte align

int footer_pos
END OF FILE

each file:

hdr:
 char magic[16] // 'MOS DATAFILE2.0\0'
 int v0  //0
 int v1  //0
 int v2
 int v3
 int v4
 int v5
 int num 
 int v7  //
 
struct ent[num-1]
 char typename[16]
 int v0
 int next
 int unk0
 int pos
 int size
 int num2  // used at data
 int unk2

typename:

XCD_ATTRIB
XCD_INDEXES
XCD_DATA
XCD_SOUNDNAME
XCD_AICATEGORY


XCD_INDEXES:
struct idx[num2]
 int id; // incremental
 int ofs; // ofs in XCD_DATA
 short size
 short unk

XCD_DATA:
struct msg[]
 short zero // 0
 short flg  // if 1, leads msg
 short len
 short zero[2]; // 0
 short strz[len-3] // null terminated UTF16

日本語化MODを公開する


ゲーム日本語化 Advent Calendar 2012


日本語化リクエスト募集で募集したリクエストの中から

Deep Blackです。



メタスコア39点
何千本あるかわからないSteamの中でワースト14 (15はShellshock 2)

見つかったレビュー
結論:糞ゲーだった。返金を要求するレベル。
普通なら
こんなの訳す奴いねえwwww
とスルーするところだが、
製品版には日本語リソースが含まれているという噂なので解析してみた


まず "Deep Black xentax" でぐぐると

XeNTaX • View topic - Deep Black Game Localization File (global.pack)

がトップに出てくる。

まあわざわざ英語版ゲームをやろうって人は読めるだろうが

誰かこのファイルunpack/repackしてくんね? 

一日後

ほいよ。unpackするquickbms scriptだお

二ヶ月後 

まだ要る人おるかもしれんから
テキストの変換とpackerおいとくわ

と言うわけでフォント以外は解析済だったので手間が省けた

日本語化MODを公開する(2)

Deep Black: Reloaded (DEMO)

日本語化可能

日本語化MOD

archive:
 http://forum.xentax.com/viewtopic.php?f=35&t=7214
 http://forum.xentax.com/viewtopic.php?f=10&t=8675
 http://aluigi.altervista.org/papers/bms/biart7.bms
text: global.pack/  localization_db.loc *.bsrt
 format: http://forum.xentax.com/viewtopic.php?f=10&t=8675
 import/exporter http://forum.xentax.com/viewtopic.php?f=35&t=7214
font: CommonGuiSceneSet.pack  .dds .abc

abc font format:
int ver // 6
int unk // 1
float f1 // width?
float f2 // 0
float f3 // 0
float f4 // width?
int num
short indexes[num];  // charcode-1 to glyphs map
int num2
struct glyphs[num2]
 float u0  // tex coord  0.0..1.0
 float v0
 float u1
 float v1
 short xofs  // -1 .. 4
 short w
 short h
 short zero  // always 0

2012年12月22日土曜日

中華MODのしくみ

ゲーム日本語化 Advent Calendar 2012

前回、ゲームプログラムには元々日本語(マルチバイト文字)表示に対応しているものと、Latin-1(1バイト文字)表示にしか対応していないものがあり、対応するにはプログラムの変更が必要という話をした。
ソースコード(実行ファイルの元)がある場合は、ソースコードを変更してそのソースから変更版の実行ファイルを作ればよいが、無い場合はどうするか。

ゲームプログラムの実行ファイルから、文字表示に関する部分を突き止め、その部分を乗っ取って自作のマルチバイト対応コードに置き換える、ということをする。


EXE書き換え


実行ファイルを書き換える。
Morrowind Code Patch
とかたぶんそう。

特殊ローダ


通常、エクスプローラからメニューやダブルクリックすると、ゲームプログラムをメモリにロードして実行する。

しかし特殊なローダを使うと、ロードだけして実行しない。
ここで無理やりDLLを読み込ませ、さらにメモリ上のゲームプログラムの文字表示関連部分を変更して、それから実行する。
ゲームが開始してパラメータやイベントなど文字表示を行う処理になると、変更された文字表示処理に飛ぶ。

通常
+------------+
|            |
| ゲーム.exe |
|            |
|   本体     |
|     |      |
|     |      |
|  テキスト  |
|     |      |
| 1文字取出  |
|     |      |
|     |      |
| 文字表示   |
|            |
+------------+

ローダー経由
+------------+
|            |
| ゲーム.exe |
|            |
|   本体     |
|     |      |
|     |      |
|  テキスト  |
|     |      |
|     *------------+
|     |      |     |
|     |      |     |
|     *---------+  |
|            |  |  |
+------------+  |  |
|自作DLL     |  |  |
|            |  |  |
| 文字表示 --|--+  |
|            |     |
| 1文字取出--|-----+
|            |
+------------
*を書き換える


ゲームプログラムを改変する、というとなんだか怖そうだが、怪しいMODを使わない人でも、実は使ったことがあるはず。

Steamで販売されているゲームには、元々Steamに対応しているものと、していないものがある。
また、「ゲームを追加/非Steamゲームを追加」すると、非steamゲームをsteamのリストに登録してsteam経由でプレイできる。
steam経由でプレイすると、右下にsteamアイコンが出て、画面キャプチャやチャット等のsteam機能を使えるようになる。


これはsteamが特殊ローダとして働き、非steamゲームを改変してsteam機能を追加している。
例えばキー入力を乗っ取ってSHIFT+TABでsteamモードに入ったり、画面描画完了を乗っ取ってsteamアイコンを表示してから本当の画面描画完了をしていると思われる。

※コメントで指摘されましたがOrignの勘違いでした。



※と思ったけどやっぱsteamにもDLLいました



偽DLL


中華MODの中には、XXXX.dll を XXXX.dllとXXXX_ori.dllとかに差し替えるものがある。

ゲームプログラムは通常、Windows DLLやDirect X DLLなど、外部のDLLをロードするように作られている。
差し替えた状態でゲームプログラムがDLLをロードする時、本来のDLLではなく偽DLLを読む。
このタイミングで偽DLLの初期化が呼ばれるので、ここでメモリ上のゲームプログラム本体を書き換える。
また、オリジナルのDLLをロードして、オリジナルDLL内の関数の場所を調べておく。

ゲーム中にDLLが呼ばれたら、偽DLLはオリジナルのDLLの該当関数に飛ばす。

通常
    +------------+
    |            |
    | ゲーム.exe |
    |            |
    |            |
  +--- DLL呼出1  |
  | |            |
+-+--- DLL呼出2  |
| | |            |
| | +------------+
| | | DLL        |
| | |            |
| | | *初期化    |
| | |            |
| +-- DLL関数1   |
|   |            |
+---- DLL関数2   |
    |            |
    +------------|

* はロード時に自動で呼ばれる

偽DLL
    +------------+
    |            |
    | ゲーム.exe |
    |            |
    |            |
  +--- DLL呼出1  |
  | |            |
+-+--- DLL呼出2  |
| | |            |
| | +------------+
| | | 偽DLL      |
| | |            |
| | | *本体改変  |
| | |  元DLLロード
| | |            | 
| +----------------+
+--------------------+
    |            | | |
    +------------| | |
    | 元DLL      | | |
    |            | | |
    | *初期化    | | |
    |            | | |
    | DLL関数1 ----+ |
    |            |   |
    | DLL関数2 ------+
    |            |
    +------------|

ここでのポイントは「元々ゲームプログラムがロードするDLL」という点である。
どのDLLを差し替えるかはさしたる問題ではない。
「ゲーム中にDLLが呼ばれたら、オリジナルのDLLの該当関数に飛ばす」処理を書くのが、関数がたくさんあるDLLだと面倒なので、関数が少ないDLLが好まれる。

Direct X のDLLを差し替えているからといって、 Direct X の機能を変更しているとは限らない。


これらの「ゲームプログラム改変MOD」は、技術的にはウィルスの類と同じである。
乗っ取った後、キー入力や通信を盗み見たりの悪さをするか、文字を表示するかの違いでしかない。
改変してプロテクトやDRMのチェックを無効にするのも同様である。

他にも書き換える手段はあるが、ウィルスと異なり、見つからないようこっそり書き換えたり、書き換えたあと存在を隠すことが目的ではないので、詳細は述べない。


「ゲームプログラムの実行ファイルから、文字表示に関する部分を突き止め、その部分を乗っ取って自作のマルチバイト対応コードに置き換える」と言ったが、

  1. 既存プログラムの該当処理部分を突き止める、つまりDRMクラックできる技量と、
  2. さらにそれを乗っ取る、つまりウィルスを作れる技量と、
  3. 自作のマルチバイト対応コードを書く、つまりゲームプログラム(の一部)を作れる技量とを併せもっている必要があり、
  4. なおかつ、ゲーム本体のバージョンが変わると置き換える場所も変わるので、面倒くさい。
(ソースコードがあれば3.だけでよい)

日本にもそこまでやっている人が何人かいる(逆に言えば何人かしかいないと思われ)。

TObject
Obvilion日本語化パッチ
FOJP

俺はヘタレなので、置き換え場所を探すのが簡単なものしかやったことは無い。

ところでFOJPのソースコードが公開されていた。
ゲームプログラムの文字表示部分を突き止めるのではなく、変態(ほめ言葉)的なことをしている。

2012年12月21日金曜日

Monkey Island™ 2 Special Edition: LeChuck’s Revenge

The Secret of Monkey Island とだいたい同じ

text: monkey2.pak/localization .info のフォーマットが少し違う

The Secret of Monkey Island: Special Edition

かな化できそうな予感

archive: http://forum.xentax.com/viewtopic.php?f=10&t=8183
font: monkey2.pak/fonts  .png .font
text: localization  uitext.info   Latin-1 binary


Planet Alcatraz

かな化可能

archive: http://forum.xentax.com/viewtopic.php?f=16&p=32441

text: .csv (plain)

font: .efn and .dds

char magic[4] // 'BMFN'
int  fontnamelen
char fontname[fontnamelen+1] // zero terminate
int  texnamelen
char texname[texnamelen+1] // zero terminate
int ver // 1 = version?
int height //
int width //

struct font[256]
 short x
 short y
 char  w
 char  xofs // -1 .. 1
 char  h
 char  yofs  // -1 .. 1

2012年12月20日木曜日

文字表示のしくみ

ゲーム日本語化 Advent Calendar 2012


ゲームプログラムがテキストから文字を取り出すとき、1byte系の言語なら1byteが1文字と簡単である。
しかしCJK対応するには、1byteの文字なのか、後続のデータを含めて2-3byteで1文字なのかを判定して取り出す必要がある。

また、フォント処理でも、1byte系言語なら256の表を用意して
'A' => 文字コード41h => 表の41h番目のフォント
という簡単な処理だが、CJK対応には、たくさんの文字から適切にフォントを探すようなプログラムが必要になる。


1byte対応ゲームである
DOOM 3 のソースコードが公開されているので、具体例を見てみる。

DOOM3

フォントデータや、テキストから1文字を切り出す部分が1byte(文字コード0-255)を前提としたつくりになっている。

フォント情報の内部形式

neo/renderer/RenderSystem.h

// font support 
const int GLYPH_START   = 0;
const int GLYPH_END    = 255;
const int GLYPH_CHARSTART  = 32;
const int GLYPH_CHAREND   = 127;
const int GLYPHS_PER_FONT  = GLYPH_END - GLYPH_START + 1;

typedef struct {
 int     height;   // number of scan lines
 int     top;   // top of glyph in buffer
 int     bottom;   // bottom of glyph in buffer
 int     pitch;   // width for copying
 int     xSkip;   // x adjustment
 int     imageWidth;  // width of actual image
 int     imageHeight; // height of actual image
 float    s;    // x offset in image where glyph starts  // テクスチャ座標
 float    t;    // y offset in image where glyph starts
 float    s2;
 float    t2;
 const idMaterial * glyph;   // shader with the glyph
 char    shaderName[32];
} glyphInfo_t;

typedef struct {
 glyphInfo_t   glyphs [GLYPHS_PER_FONT];  // 256文字固定
 float    glyphScale;
 char    name[64];
} fontInfo_t;

フォントのロード

フォントデータには個数や文字コード情報がなく、コード順に256文字並んでいることを前提にしている

bool idRenderSystemLocal::RegisterFont( const char *fontName, fontInfoEx_t &font ) {
...
 fileSystem->ReadFile( name, &faceData, &ftime );
  fdOffset = 0;
  fdFile = reinterpret_cast<unsigned char*>(faceData);
  for( i = 0; i < GLYPHS_PER_FONT; i++ ) {  // 256文字固定
   outFont->glyphs[i].height  = readInt();
   outFont->glyphs[i].top   = readInt();
   outFont->glyphs[i].bottom  = readInt();
   outFont->glyphs[i].pitch  = readInt();
   outFont->glyphs[i].xSkip  = readInt();
   outFont->glyphs[i].imageWidth = readInt();
   outFont->glyphs[i].imageHeight = readInt();
   outFont->glyphs[i].s   = readFloat();
   outFont->glyphs[i].t   = readFloat();
   outFont->glyphs[i].s2   = readFloat();
   outFont->glyphs[i].t2   = readFloat();
   int junk /* font.glyphs[i].glyph */  = readInt();
   //FIXME: the +6, -6 skips the embedded fonts/ 
   memcpy( outFont->glyphs[i].shaderName, &fdFile[fdOffset + 6], 32 - 6 );
   fdOffset += 32;
  }
...
}

テキストの表示

int idDeviceContext::DrawText(float x, float y, float scale, idVec4 color, const char *text, float adjust, int limit, int style, int cursor) {
  ...

  while (s && *s && count < len) {
   if ( *s < GLYPH_START || *s > GLYPH_END ) {
    s++;
    continue;
   }
   glyph = &useFont->glyphs[*s]; // 1byte取り出し
       // 文字コード -> 文字形(glyph) は単純な表参照

   ...

    float yadj = useScale * glyph->top;
    PaintChar(x,y - yadj,glyph->imageWidth,glyph->imageHeight,useScale,glyph->s,glyph->t,glyph->s2,glyph->t2,glyph->glyph);

    if (cursor == count) {
     DrawEditCursor(x, y, scale);
    }
    x += (glyph->xSkip * useScale) + adjust;
    s++;
    count++;    // 1byte進める
   }
  }
...
}

日本語化MOD

このように1byteコードのみにしか対応していないゲームは、基本的にかな化はできるが漢字日本語化はできない。
しかしソースコード(プログラムの元)が公開されている場合、プログラムを変更することによって日本語表示が可能になる。

フォント情報の内部形式


ここでは元のフォント情報内部形式はそのままに、

glyphs[0-254] は元のまま、
glyphs[255] に 独自のmy_glyphs_tをぶち込むという
少々トリッキーなことをしている。


neo/renderer/myfont.h

typedef struct {
 int     height;   // 文字コードとして代用
 int     top;   // top of glyph in buffer
 int     bottom;   // bottom of glyph in buffer
 int     pitch;   // width for copying
 int     xSkip;   // x adjustment
 int     imageWidth;  // width of actual image
 int     imageHeight; // height of actual image
 float    s;    // x offset in image where glyph starts
 float    t;    // y offset in image where glyph starts
 float    s2;
 float    t2;
 const idMaterial * glyph;   // shader with the glyph
 int     id;
 //char    shaderName[32];
} my_glyphInfo_t;

typedef struct {
 int num;  // グリフ数を管理
 my_glyphInfo_t*  glyphs;
} my_glyphs_t;

フォントのロード

neo/renderer/tr_font.cpp

  fileSystem->ReadFile( name, &faceData, &ftime );
  fdOffset = 0;
  fdFile = reinterpret_cast<unsigned char*>(faceData);
  int mw = 0;
  int mh = 0;
  if (memcmp(fdFile,"BMF\x03",4)==0) {   // Bitmap Font Generator形式に対応
   ...
   
      my_glyphInfo_t* myglyphs = (my_glyphInfo_t*)Mem_ClearedAlloc(sizeof(my_glyphInfo_t)*num);
      int n=0;
      float inv_w = 1.0f/scaleW,inv_h = 1.0f/scaleH;
      for(;p < next;p+=20) {
       int id = get32(p);
       int x = get16(p+4);
       int y = get16(p+6);
       int w = get16(p+8);
       int h = get16(p+10);
       int xoffset = get16(p+12);
       int yoffset = get16(p+14);
       int xadvance = get16(p+16);
       int page = p[18];
       int xSkip = xadvance;
       my_glyphInfo_t* glyph = (id < GLYPH_END)?(my_glyphInfo_t*)&outFont->glyphs[id]:&myglyphs[n++]; // 0-254はそのまま、それ以上はmyglyphsに入れる

       glyph->id = id;
       glyph->height = h+yoffset;
       glyph->top = base-yoffset;
       glyph->imageWidth = w;
       glyph->imageHeight = h;
       glyph->xSkip = xSkip;
       glyph->s = x*inv_w;
       glyph->t = y*inv_h;
       glyph->s2 = (x+w)*inv_w;
       glyph->t2 = (y+h)*inv_h;
       glyph->glyph = materials[page];
       // shaderName not used
       if (mw<xSkip) mw=xSkip;
      }

      my_glyphs_t* t = (my_glyphs_t*)&outFont->glyphs[GLYPH_END];
      t->glyphs = myglyphs; // &outFont->glyphs[255] にmyglyphsをぶち込む
      t->num = n;

テキストの表示

neo/ui/DeviceContext.cpp

 while (s && *s && count < len) {
   if ( *s < GLYPH_START ) {
    s++;
    continue;
   }
  ....
    int ch = *s;
    int l=1;
    if (utf8) {
     if      (ch<0x80) {
     } else if (ch<0xc0) {
     } else if (ch<0xe0) {
      if (!is_leading(s[1])) break;
      ch = ((ch&0x1f)<< 6) | (s[1] & 0x3f); l=2;
     } else {
      if (!is_leading(s[1]) || !is_leading(s[2])) break;
      ch = ((ch&0x0f)<<12) | ((s[1] & 0x3f)<<6) | (s[2] & 0x3f); l=3;
     } // 1文字取り出してbyte数をlに代入
    }
    glyph = get_glyph(useFont, ch); // 対応する文字画像を取り出す
    float yadj = useScale * glyph->top;
    PaintChar(x,y - yadj,glyph->imageWidth,glyph->imageHeight,useScale,glyph->s,glyph->t,glyph->s2,glyph->t2,glyph->glyph); // 文字画像表示部分は元のまま

    if (cursor == count) {
     DrawEditCursor(x, y, scale);
    }
    x += (glyph->xSkip * useScale) + adjust;
    s+=l;
    count+=l; // 1文字分進める

DOOM3 BFG

最近DOOM3 BFG Editionのソースコードが公開されてたので、どう変更されたか見てみる。
(ところでid softwareは最初にDOOMのソースコードを公開してから毎回クリスマスに公開してたのだが、Bethesda Softworksに買収されてから時期が関係なくなった)

フォント情報の内部形式

 struct fontInfo_t {
  struct oldInfo_t {
   float maxWidth;
   float maxHeight;
  } oldInfo[3];

  short  ascender;
  short  descender;

  short  numGlyphs; // 個数を管理している
  glyphInfo_t * glyphData;

  // This is a sorted array of all characters in the font
  // This maps directly to glyphData, so if charIndex[0] is 42 then glyphData[0] is character 42
  uint32 * charIndex;

  // As an optimization, provide a direct mapping for the ascii character set
  char  ascii[128];

  const idMaterial * material;
 };

フォントのロード


bool idFont::LoadFont() {
....
 fd->ReadBig( fontInfo->numGlyphs ); // フォントデータから個数を読んでいる

 fontInfo->glyphData = (glyphInfo_t *)Mem_Alloc( sizeof( glyphInfo_t ) * fontInfo->numGlyphs, TAG_FONT );
 fontInfo->charIndex = (uint32 *)Mem_Alloc( sizeof( uint32 ) * fontInfo->numGlyphs, TAG_FONT );

 fd->Read( fontInfo->glyphData, fontInfo->numGlyphs * sizeof( glyphInfo_t ) );

 for( int i = 0; i < fontInfo->numGlyphs; i++ ) {
  idSwap::Little( fontInfo->glyphData[i].width );
  idSwap::Little( fontInfo->glyphData[i].height );
  idSwap::Little( fontInfo->glyphData[i].top );
  idSwap::Little( fontInfo->glyphData[i].left );
  idSwap::Little( fontInfo->glyphData[i].xSkip );
  idSwap::Little( fontInfo->glyphData[i].s );
  idSwap::Little( fontInfo->glyphData[i].t );
 }
...
}

テキストの表示


int idDeviceContext::DrawText(float x, float y, float scale, idVec4 color, const char *text, float adjust, int limit, int style, int cursor) {
 ...
  while ( charIndex < len ) {
   uint32 textChar = drawText.UTF8Char( charIndex ); // マルチバイトを考慮してcharIndexから1文字取りだし、charIndexを1文字分(1-3byte)進める。 
 ...
    scaledGlyphInfo_t glyphInfo;
    activeFont->GetScaledGlyph( scale, textChar, glyphInfo ); // textCharのUnicodeの文字情報をscale倍してglyphInfoに読み込む。
    prevGlyphSkip = glyphInfo.xSkip;

    PaintChar( x, y, glyphInfo ); // 1文字表示

    if (cursor == charIndex-1) {
     DrawEditCursor(x, y, scale);
    }
    x += glyphInfo.xSkip + adjust;

drawText.UTF8Char( charIndex )
neo/idlib/Str.hneo/idlib/Str.cpp

activeFont->GetScaledGlyph( scale, textChar, glyphInfo )
neo/renderer/Font.cpp


というわけで変更点はだいたい似通っている。

ゲームのソースコードは商売のタネなので基本的には公開されることはないのだが、
デベロッパによっては古いゲームのソースコードをファンサービスで公開することがある。
(それ以上非公開にしていても利益を生むことは無いという判断?)

元々マルチバイト対応してないゲームでも、ソースコードが公開されていれば(そしてライセンス上の問題が無ければ - 普通は問題無い形で公開されている)、表示できるように改造することで日本語化可能になる。

FreeSpace 2/日本語化プロジェクト
Aquaria 日本語化

元々1byteにしか対応していないものを日本語化するにはこのようなプログラムの変更が必要となるので、海外ゲームの日本語版が割高になるのは翻訳も含めてやむを得ない面もある。

しかし元々日本語化可能なゲームであるにもかかわらず、英語版のまま割高で国内販売してるのはフサケンナって思う。

The Testament of Sherlock Holmes

日本語化可能。
FrogwareのSherlock Holmesシリーズと同じ。

追記:
-フォントが微妙に違った



日本語化

font4:
char magic[4] // fa d4 9a 23
int fontsize?
float font_w
float font_h+a
int zero // 0
int num

struct glyphs[num]
 float u0
 float u1
 float v0
 float v1
 float w  // pixels
 float h  // pixels
 float adj // 0 or -0.xx  // AVX_jw
 int code

int num2

struct blocks[num2]
 int startchar
 int endchar
 int startidx
 int endidx

font1 - 512x512
font1_big 1024x2048
megafont2 1024x1024  italic
font_dialog 1024x256

u0,v0           
+---------------+
|   **   |      |
|  *  *  |      |
| *    * |      |
|********|h     |
|*      *|      |
|*      *|      |
|*      *|      |
|--------+      |
|   w           |
+---------------+
               (u1,v1)

2012年12月19日水曜日

日本語化リクエスト募集

日本語化してほしいゲームのリクエストを募集します。
  • - DEMO版がある、または
  • - Indie bundleに入ってた
  • - 1C Complete Packに入ってた
  • - その他俺が持ってるもの
  • - 6650%引以上でセール中
  • - giftする



  • - 日本語表示とツール公開までです。翻訳はシラネ
  • - かな化までとか日本語化困難とわかるだけの場合もあります
  • - ↑なのでgiftは慎重に

コメント欄に書いてください。
同じゲームは最初のコメントの返信にぶら下げてください
翻訳したい人だとポイント高いです

メタスコアと俺の趣味でポイント(俺のモチベーション)が上下します


12/25に公開予定なので〆切りはお察しください
リクエストが無ければ普段通り適当に公開します。


追記: 正月まで(ホリデーセールが終わるまで)延長します。


Secret Files 3



Secret Filesと同じ。
packerのキーはLost Horizonのものを使う

makspr ..\loca.spr loca /key:lh

2012年12月18日火曜日

文字コードの話

ゲーム日本語化 Advent Calendar 2012

ゲームの多言語の対応は大きく4つに分けられる。

  • 1. 英語のみ(ASCII)
  • 2.ドイツ・フランス語などラテン文字の言語(Latin-1,ISO-8859-1)
  • 3. ロシア・チェコ語などその他の1byte(224以内の)文字の言語(ISO-8859-X)
  • 4.中国、日本、韓国など、それ以上の文字数(マルチバイト)の言語(Chinese, Japanese, KoreanをまとめてCJKと呼ぶ) Unicode

ゲームプログラムがテキストから文字を取り出すとき、1byte系の言語なら1byteが1文字と簡単である。
しかしCJK対応するには、1byteの文字なのか、後続のデータを含めて2-3byteで1文字なのかを判定して取り出す必要がある。

また、フォント処理でも、1byte系言語なら256の表を用意して
'A' => 文字コード41h => 表の41h番目のフォント
という簡単な処理だが、CJK対応には、たくさんの文字から適切にフォントを探すようなプログラムが必要になる。


日本語化する上で、ゲームがどこまで対応した作りになっているかが重要になる。

1.英語のみ


このタイプは日本語化は難しい。英字(大文字26+小文字26)を潰して全て「かな」または「カナ」にすることはできなくはないが、英字が使えなくなるので完全に訳す必要がある。
古いゲームやindieの一部に見られる。

2. ドイツ・フランス語など


ドイツ・フランスなど西ヨーロッパの国々は共通の文字コードを使っている。
言い換えると西ヨーロッパの国々が共通して使える文字コードを決めた、と言える。

Steamで 言語: English*, ドイツ語, フランス語, イタリア語, スペイン語

などと特定国に偏っているのは、市場規模の理由もあるが、プログラム対応コストをかけずに翻訳コストだけで対応できるからでもある。

世界展開・マルチプラットフォーム展開する大手のものを除き、多くはここまでの対応である。

ドイツ語化MODが存在する場合、アーカイブ形式とメッセージ形式が解析されたことを意味する。
これに加えてフォント形式を解析すれば、かな化は可能である。
かなの他に、数文字までなら漢字を使うこともできる。古いファミコンゲームで一部だけに漢字が使われてたのと同じ。

3. ロシア・チェコ語など


ロシア語化MODが存在する場合、2.に加えてフォント形式が解析されたことを意味する。
元々ロシア産のゲームは、1byte文字で対応している場合と、unicodeで対応している場合がある。
前者はかな化、後者の場合は日本語化できる可能性がある。

4. 中国語・韓国語など


ゲームが元々マルチバイトに対応しているか、ゲームプログラムを変更して無理やり表示させているMODもある。

中文化MODが存在する場合、アーカイブ形式、メッセージ形式、フォント形式が全て解析されており、マルチバイト文字の表示が可能なことを意味する。

ただし、中文化MODは改変結果のみが配布されており、解析情報は公開していないか公開しているけれども中国語だから見つけにくいのか、
存在するだけでは「表示可能であることが実証された」という意味程度で、日本語化するには改めてフォント形式などを解析する必要がある。

Unicode

マルチバイトの文字コードには、国(言語)ごとに違うコード(Shift-JIS等)と、世界共通コード(Unicode)があるが、現在のゲームは殆どがUnicodeである。

データ上では、Unicodeを1-3byteのマルチバイトに割り当てたもの(UTF-8)と、常に2byte使うもの(UTF-16)がある。
UTF-16はさらにUTF-16LE(Little Endian)とUTF-16BE(Big Endian)のものがある。
メモ帳(notepad)で文字コード指定での保存時に「Unicode」とあるのがUTF-16LEである。

PCゲームのUTF-16は殆どの場合UTF-16LEであるが、ゲーム機からの移植等でまれにUTF-16BEの場合もある。


この中でどのような対応しているかは、テキストの文字コードで判断できる。

ASCII,Latin-1,utf-8は、英数字の部分(20-7f)は同一なので、テキストを見ても区別が付きにくいことがある。

Steam等で再インストールなしで言語を簡単に切り替えられる場合は、試しにドイツ語やフランス語に変えて、ドイツ語用のデータを追加ダウンロードしてみる。

英語しかない場合、テキストに著作権表記の©や、商標権表記の®が含まれていれば、そのコードがLatin-1かutf-8かで判断できる。


フォント画像がtgaやDDSなどの形式で簡単に確認できる場合は、フォント画像からも推測できる。
画像にラテン文字しかない場合はLatin-1である可能性が高い。
つまりかな化できる可能性が高い。

ラテン文字とそれ以外の文字が1枚の画像にある場合は、Unicodeである可能性が高い。
つまり日本語化できる可能性が高い。


Unicode対応していても一筋縄でいかない場合がある。

Unicode一覧を見ると1fffくらいまでで、CJKを除くほとんどの言語をカバーしていることがわかる。

ゲームによってはこの範囲の多言語化しか想定しておらず、CJK文字は3000くらいから始まるので、漢字やかなのメッセージを食わせるとぶっ飛ぶことがある。

このような場合、unicodeテキストの読み込み自体は実装されているので、
100-1fffあたりに漢字やかなを割り当てた「俺コード」をデッチあげて、それに沿ってフォントを作成したりテキストを変換すればよい。


unicode テキストを編集する際に気をつける点が2つある。

BOM


テキストエディタによっては、BOM(Byte Order Mark)という見えない文字がくっつくことがある。
ゲームによってはBOMが有る/無いと動かないことがあるので、元データにあわせる。

BOMは
UTF-16LEなら ff fe
UTF-8なら ef bb bf
である。

素のテキストファイルだから簡単に変更できるのでは?と思って変更したら動かなかった、と言う場合は、
目grepとかわかんね、という人も先頭数バイトくらいは確認してみよう。

外字


ゲームによっては、未使用領域や外字領域を、ボタンや矢印等のアイコンの文字コードとして使っている場合がある。
これは通常のエディタでは見えないので、編集時に注意するか、コンバータで置換するようにする。


The Book of Unwritten Tales: The Critter Chronicles (DEMO)

The Book of Unwritten Talesと同じ
フォントは画像でなくttfのみ

2012年12月17日月曜日

2012年12月16日日曜日

unpackerの探し方

ゲーム日本語化 Advent Calendar 2012

まず最も重要なのは xentax である。

とりあえず

xentax ソフト名
xentax アーカイブの拡張子
xentax アーカイブのシグネチャ文字列

などで検索すればなんとかなる場合もある。

例えば、GOGでinquisitorというソフトがあるが、

inquisitor xentax
でぐぐると

Zork Nemesis/Grand Inquisitor
という別のゲームがヒットするので

inquisitor xentax dat
で絞ってぐぐると

http://forum.xentax.com/viewtopic.php?f=8&t=3886

が見つかる。この記事にある

http://aluigi.org/papers/bms/inquisitor.bms
はリンク切れなので、適当に変更すると

http://aluigi.altervista.org/papers/bms/inquisitor.bms
からunpackerがgetできる。

ここで使われてるのは
quickbms
というソフト用のスクリプトで、

(最近は著作権的に危ういのは消しているようだが)、
誰かがmegauploadあたりに
「このアーカイブわかんね?」
と上げると、

「これでextractできんじゃね?」
と誰かがquickbms script形式でunpackerをあげていることがある。

(スルーされることのほうが多いが)

古いゲームだと

Dragon Unpacker

Game Extractor

gobread

ADVに強いununpakke

などが、マルチフォーマットのunpackerの代表的なもので、
「XXXというゲームのunpackはどーすんの?」
という質問の答えによくあげられる。

gobreadは32bit windowsでしか動かないポイので、Windows x64ユーザは仮想マシンか何か用意しておきたい。
(あとTowWorld関連のWD PackagerもXP 32bitでしか動かないポイので)


その他、比較的新しいゲームについては

Rick’s Game Stuff

ただし、コレで見つかるのはpacker機能のないunpackerだけのことが多く、日本語化するにはさらにpacker, font形式、message形式などが必要になる。


2次元エロゲならunpackしてエロ画像見えたらうれしいだろうが、
一般洋ゲの場合、ゲーム中でいかにエロっぽいキャラクターであろうと、テクスチャだけ見てうれしいものではない。テクスチャを○色に差し替えてrepackできて初めて意味がある。

またunpackerと称するものの中には、アーカイブ形式を理解せず、単にシグネチャを目印に音楽や動画を切り出すだけのものもある。indie bundleでOSTがオマケにつく場合もあるから音楽を抜き出したいというニーズがあることはわかるが、このような似非unpackerでは、日本語化の役には立たない。

packerを作るには、初心者でも使えるGUIのunpackerのようなものよりも、xentax bbsや Game File Format Centralのquickmbs scriptや構造説明のほうがが参考になる。

quickbmsの最新版では、unpackerだけでなくpacker機能も付いたようなので、
quickbms scriptを元にpackerを改めて作る必要も無くなったかも。



重要な点として、同じ開発会社(販売会社ではない)は同じアーカイブ形式もしくはその派生したものを使っていることが多いので、開発会社を調べて、そこが過去に出したゲームのunpakerが使えないか調べてみる。
開発会社は検索の他、steamやgamersgateなどでの購入時にもわかる。

例えば Sniper Elite V2Rebellion開発なので、同じ会社の過去の作品であるAliens vs. Predatorのpacker/unpackerと日本語化手法がほぼそのまま使える。

(Shellshock 2Rogue Warriorも同様に日本語化できるが、メタスコアが伝説レベルのクソゲーなので、翻訳する酔狂な人はいないと思われ)

また、FPSやADV等は、他の開発会社が作成した基本プログラム(ゲームエンジン)を利用していることがあるので、拡張子やシグネチャに見覚えがあったら、他の開発会社のゲームエンジンを使っているか、その会社のゲーム用のunpackerが流用できないか確認してみよう。




当然と言えば当然なのであるが、英語ゲームのローカライズ(○ ○ 語化)のニーズがあるのは英語圏以外なので、ローカライズ手法に関する情報も英語圏以外にあることが多い。ロシアとか中国とか。

xentaxからリンクするような投稿をだれかがしてれば発見できるが、いまのところいい発見方法を見つけていない。


詳しい話は後で述べるか、文字種や文字コードの関係で

中文化 = 日本語化 > ロシア語化 = かな化 > ドイツ語・フランス語 > 英語のみ

理論上、中文化可能であれば、漢字含む日本語化が可能である。

しかし、中文化MODを探す上で、元のゲームに中文化パッチを当てた状態で著作物全体がp2pに流れてたりして、legalな中文化パッチのみを入手するのはむずかしいというか区別が付きにくい。

迂闊に検索すると(日本の著作権上)ヤバイ物をダウンロードしかねない。

俺の観測では
3dmgamechinaAVG(ADV専用)
追記: ali213 と そこの中文化チーム

が中文化MODのみを配布してるようだ。

検索方法が良くわからんので一覧を目視してる。

汉化 というのが中国語化(Chinese localization)という意味らしい。

ココにあるソフトは、日本語化できる可能性が高い。

2012年12月15日土曜日

技術メモ: 実践 目grep


ゲーム日本語化 Advent Calendar 2012


実際に解析してみる。
The Real Texasを例にする。

(以下数値は16進数)



フォルダを見るとglobというサイズの大きいファイルがある。
他にゲームデータらしきファイルは無い。globがアーカイブと思われる。

軽くダンプしてみる


0000010に 78 9c が見える。たぶんzlibヘッダ。

2つのアーカイブを見比べる。



共通のシグネチャやバージョン番号らしきヘッダはないことがわかる。

サイズの一番小さいworld.globを調べる

先頭付近と末尾付近をざっと眺める




どこにもファイル名らしきものは見当たらない。

先頭の

e5 f9 2b 00
be 00 00 00

はよくわからない。後者をファイル数と仮定する。


zlibヘッダの直前に
4c 00 00 00
2c 4e 00 00
が見える。前者がファイルサイズ、後者が展開後サイズとあたりをつける。

ここからバイナリエディタでまじめに見る

int size
int origsize
char data[size]

と仮定して、dataの先頭にsizeを足して飛んでみる


5c にきた。

なんかズレてるので、sizeはデータサイズではなく、origsizeの4byte分を含めたサイズとわかる

int size
int origsize
char data[size-4] // zlib compressed

とりあえずこの仮定でunpackerを作ってみる。
ファイル名は適当に連番をつける。

$fp = fopen($file,'r');

$unk = fgetint($fp);
$num = fgetint($fp);


for($i=0;$i<$num;$i++) {
  $size = fgetint($fp);
  $origsize = fgetint($fp);
  $data = fread($fp,$size-4);
  $data = gzuncompress($data);
  $outfile = $dir.sprintf("%04d.dat",$i);
  file_put_contents($outfile,$data);
  printf("%d %x %d %d %x\n",$i,$pos,$size,$origsize,ftell($fp));
}
fclose($fp);

実行してみる。



うまく展開されているようだ。
アーカイブ名がworld.globなのでマップデータか何かだろう。

ただし、最終位置がアーカイブのサイズと会わない。ヘッダの値をファイル数と仮定したのは間違いだったようだ。
最後まで処理するようにする。

for($i=0;true/*$i<$num*/;$i++) {
  $pos = ftell($fp);
  if ($pos>=$filesize) break;


最後のファイルだけなんか大きいので覗いてみる。




どう見てもファイル名っぽい。

先頭の4a 1e 00 00はファイル数ぽい。
次の20 00 00 00は、ファイル名の長さっぽい。

解説してなかったが、文字列はたいてい

長さ 文字....
文字.....0

のいずれか。

長さ 文字...0

の場合もある。
あるいは、長さちょうどのサイズではなく、4byte等でalignされるように末尾にpaddingが付くこともある。

20 00 00 00 の直後の world... 文字列に20を加えてみると、うまく文字列の末尾になった。


次の文字列開始30 までの間に、
e3 0d 10 00
cc 00 00 00

30からの文字列末尾の次にも、
19 0a 10 00
24 00 00 00

が見える。

ためしに 100de3に飛んでみる


size origsize zlibヘッダ

と圧縮されたファイルの先頭ぽいので、これはオフセットとわかった。
もう一つの値 cc 00 00 00 はよくわからん

int num

struct entry[num]
  int len
  char name[len]
  int ofs  // offset to file
  int unk

改めてファイルの先頭を見てみると



E5 F9 2B 00
BE 00 00 00

と、ofs,unkと同じ構造をしていることがわかる。

2BF9E5に飛んでみると、これは先ほど解析した最後のファイルである、エントリ情報を指しているとわかる。




2BF9E5 はファイルサイズより小さいので、アーカイブ内の何かを指すオフセットだ、と 最初に気づいていれば、先にファイルエントリ情報を見つけられたはずだが、まあこういうこともある。


ここまでの情報をもとに、アーカイブを先頭から読んで連番ファイルに出力するのではなく、ファイルエントリ情報を読んでファイル名を付けて出力するように変更する。

$fp = fopen($file,'r');

$entpos = fgetint($fp);
$unk0 = fgetint($fp);

$data = read_data($fp,$entpos);
$st = new BinStream($data);

$num = $st->getint();

echo $num,"\n";

for($i=0;$i<$num;$i++) {
  $namelen = $st->getint();
  $name = $st->read($namelen);
  $ofs  = $st->getint();
  $unk  = $st->getint();
  printf("%x %x %s\n",$ofs,$unk,$name);
  $data = read_data($fp,$ofs);
  savefile($dir.$name,$data);
}
fclose($fp);

function read_data($fp,$pos)
{
  fseek($fp,$pos,SEEK_SET);
  $size = fgetint($fp);
  $origsize = fgetint($fp);
  $data = fread($fp,$size-4);
  $data = gzuncompress($data);
  return $data;
}

うまくいった。

data.globを展開してみる

なんかエラーだって

エラー部を見てみると

sizeが0の場合はorigsize,dataが続かず、すぐ次のファイルになっていることがわかる。

int size // if 0 , no origsize,data
int origsize
char data[size-4] // zlib compressed

size == 0 の特殊処理を入れる。

function read_data($fp,$pos)
{
  fseek($fp,$pos,SEEK_SET);
  $size = fgetint($fp);
  if ($size==0) return '';

もう一度展開してみる。

うまくいった。完成。

The Real Texas unpacker

The Real Texas

かな化できんこともない

0-7fの英数字しか想定してない気がする

achive: 独自形式 zlib圧縮 unpacker
font: data.glob内 data/font/*.png と *.lua(座標)
text: data.glob内 data/ luaスクリプトに埋め込み

int ofs  // offset to file includes compressed entery
int unk

struct file
 int size  // if 0 , no origsize and data  
 int origsize
 char data[size-4] // zlib compressed


uncompressed entry;

int num

struct entry[num]
  int namelen
  char name[len]
  int ofs  // offset to file
  int unk


2012年12月14日金曜日

技術メモ: ゲームデータの特徴

ゲーム日本語化 Advent Calendar 2012

zipには先頭に'PK'というシグネチャがあるが、
アーカイブ内のゲームデータにも特徴がある。

画像


PNG 89 'PNG' (SJISだと '臼NG')
DDS: 'DDS '
BMP: 'BM'
TGA; 00 00 20 00 (32bitの場合)
JPEG: ff d8 ff XX (JFIFの文字が見えることも)

シグネチャが無くても、非圧縮画像は00(黒)やFF(白)が多発するので見分けが付く。
アルファつき32bitなら 00 00 00 FF とか 4byteごとに規則的に並ぶ。
圧縮DDS(DXT5)は一見圧縮データぽいが、特徴的な00やffの並びが見られる。
画像ぽいデータなら、先頭あたりに画像サイズぽい値あがあるはず。
非圧縮なら、縦x横に1dotあたりのbyte数(1-4)を掛ければ(ヘッダサイズを除いた)ファイルサイズに近い値になるはず。

サウンド


WAV: RIFF....WAVEfmt
OGG: OggS

が圧倒的に多い。日本語化には(どっかの暇人がアテレコでもしない限り)関係ないのでここではスルー。

テキスト


plain textは普通に人間が読めるのでわかりやすい。
メッセージやスクリプトなど

動画

bink(BIK)かogm、mkv等が多い。


その他


バイナリ化されたメッセージやスクリプト、モデルなど。
4byte int や floatぽい値が並ぶなど、無秩序ではなく特徴がある。
メッセージがスクリプト中に埋め込まれている場合などは、メッセージぽいテキストが見える。

圧縮データ


なんかゴチャゴチャしてたら、圧縮か暗号化されている。
zlib圧縮の場合、78 xx という zlibヘッダがある(ないこともある)


アーカイブ解析時に、ファイル情報部の「オフセットぽい値」に飛んでみて、これらファイルの先頭ぽい特徴が見えたら、ファイルの先頭と考えられる。

このオフセット値は、
- ファイル先頭からの絶対値
- ヘッダの直後からの相対値
- データ部の先頭からの相対値

などのバリエーションがある。
とりあえず飛んでみて、少しズレてたらヘッダ分の可能性が高い。2つのファイルでオフセット位置に飛んでみて同様にズレてたら確定。

また、動画やサウンドなら「ファイルサイズぽい値」が大きな値になってるはず。


ところで「デジタルフォレンジック」という分野がある。
「フォレンジック」とは「法廷」や「科学捜査」の意味で、
「デジタルフォレンジック」とは、(関連する部分だけ言えば)故意に隠したファイルやHDD上の痕跡から、証拠となるデータを復元することである。

その手の解析コンテストや勉強会に出てる人のスライドが公開されており、ゲームデータ解析にも通じるところがあるので必見。


2012年12月13日木曜日

技術メモ: アーカイブを覗いてみる


ゲーム日本語化 Advent Calendar 2012

ゲームで使う画像、サウンド、テキスト、スクリプト等は
個別のファイルになっている場合もあるが、たいていは巨大な数個のファイルにまとまっている。

zipそのままで拡張子変えただけという場合もある。

とりあえず16進ダンプして'PK'で始まってたらzipだ。


そうでない場合、アーカイブ形式を調べる必要がある。

アーカイブ形式を自作するつもりになれば想像できるが、
どこかに

- ファイル数

- (ファイル名)
- ファイルサイズ
- データ位置
- (圧縮してるか)
- (展開後のサイズ)

という情報があるはず。

例えばzipではこういう構造になっている。


これらの情報がどこかにあるはず、という前提で16進ダンプを見ていく。
情報はたいてい32bitのlittle endianである。

ファイル数なら
2c 00 00 00
とか小さい値のはず。
オフセットなら
20 00 00 00
..
5e 03 00 00
..
1a 2c 00 00
のように増えていくはず。
ファイルサイズなら、アーカイブのサイズより小さいはず。

+--------------+
|   ヘッダ     |
+--------------+
|ファイル1情報 |
|ファイル2情報 |
|  .....       |
|ファイルN情報 |
+--------------+
|  ファイル1   |
+--------------+
|    ....      |
+--------------+
|  ファイルN   |
+--------------+

というのが最も良くあるパターンである。
ヘッダには識別符号(zipなら'PK')、ファイル数などが含まれる。
アーカイブのバージョンらしき数値があることもある。

ファイル情報はアーカイブ末尾にあることもある。
この場合、アーカイブ先頭のヘッダまたは末尾のフッタなど、たどれる場所に情報開始位置があるはず。

+--------------+
|   ヘッダ     |
+--------------+
| 情報開始位置 |
+--------------+
|  ファイル1   |
+--------------+
|    ....      |
+--------------+
|  ファイルN   |
+--------------+
|ファイル1情報 |
|ファイル2情報 |
|  .....       |
|ファイルN情報 |
+--------------+
または

+--------------+
|   ヘッダ     |
+--------------+
|  ファイル1   |
+--------------+
|    ....      |
+--------------+
|  ファイルN   |
+--------------+
|ファイル1情報 |
|ファイル2情報 |
|  .....       |
|ファイルN情報 |
+--------------+
| 情報開始位置 |
+--------------+
まれにファイル情報がまとまってない場合がある

+--------------+
| ヘッダ       |
+--------------+
|ファイル1情報 |
|  ファイル1   |
+--------------+
|    ....      |
+--------------+
|ファイルN情報 |
|  ファイルN   |
+--------------+

この場合、 オフセット 情報が無く、内部ファイル末尾がそのまま次のファイルの開始になっていることもある。

ファイル名ぽい文字列が手がかりになるが、ファイル名が無い場合がある。

一つは、ファイル名だけ別ブロックにまとまっていて、情報にブロック内のオフセットがある場合。
もう一つは、ファイル名に替わるIDらしき32bit値がある場合。

前述のようにファイルサイズやオフセットは小さい値だから、上位は00のはずである。

2a 4b cd a7

とか00を含まない値の場合、ファイルのチェックサムかファイルIDを疑ってみる。

Xentax Game File Format Central
には、ゲームアーカイブ形式の解析が数多く掲載されている。


2012年12月12日水曜日

技術メモ: 簡単なものをかな化してみる

ゲーム日本語化 Advent Calendar 2012


世間ではAdvent Calendarとかいうのが流行ってるようなので、この機会に日本語化について書いてみる。


最近indie bundleにあった Weird Worlds: Return to Infinite Space を例にする。

- ファイルはアーカイブされず素で入ってる
- フォントは普通の画像形式
- テキストは普通のテキスト
- 1byte英語文字コード(ISO-8859-1)決めうち

UnityやXNAを使わないエンジン自作のindieゲームにありがちなパターン。

フォントの見つけ方

- フォルダ名やファイル名にfont とか gui とか interface とか含まれると怪しい。
- 無ければ全部見てく。(俺はIrfanViewを使用。DDS対応でフォルダ内まとめて見ることができる)

というわけで default\graphics\gui\font_16x32.tga が見つかった。

- 見ると文字が規則的に並んでいる。
- 文字幅が不規則な場合は、別に位置、幅、高さ等の情報を含む別ファイルがあることが多い。
- 下半分のウムラウト文字の余地が無く英数字だけの場合、かな化は困難かも。

ここでは文字が規則的なので終了。


テキストの見つけ方


- 適当な画面をキャプチャしてメッセージをメモる。ここでは[START]に注目



- そのメッセージを含むファイルをゲームフォルダから検索。(俺はサクラエディタを使用)


□検索条件  "START"
検索対象   *.*
フォルダ   f:\opt\Desura\Common\weird-worlds-return-to-infinite-space\
    (サブフォルダも検索)
    (英大文字小文字を区別する)
    (文字コードセットの自動判別)
    (一致した行を出力)

f:\opt\Desura\Common\weird-worlds-return-to-infinite-space\weird.exe(5183,452)  [SJIS]:
 (略)Mod Settings・・・Video Settings・・・START・・・MODS・・・・VIDEO SETTINGS・・・QUIT(略)
f:\opt\Desura\Common\weird-worlds-return-to-infinite-space\default\gamedata\strings.ini(76,17)  [SJIS]: simulator_start START
f:\opt\Desura\Common\weird-worlds-return-to-infinite-space\default\gamedata\strings.ini(133,13)  [SJIS]: start_start START
f:\opt\Desura\Common\weird-worlds-return-to-infinite-space\default\gamedata\strings.ini(143,16)  [SJIS]: mainmenu_start START
4 個が検索されました。



exe内の[START]はたぶんランチャ用で変更できない。
と言うわけで default\gamedata\string.ini が見つかった。

- メッセージを変更してみる

mainmenu_start START

mainmenu_start STARTス

とかにしてみる。


と言うわけで変更されたので合ってる。たぶん他の*.iniもテキストだろう。


かなフォントを作る


英語文字コードでは記号やウムラウトになってる部分をかなにする。

テキストを訳す


半角カナで入力してもいいけど、かなとカナを含むフォントを用意して何かコンバータ作るのが吉。
汎用的なのが公開できればいいな。

作例


この方法で かな化したのがCryostasis

コレを


こうすると

コレが
 こうなる


Weird Worlds: Return to Infinite Space

かな化可能と思われ

font: default/graphics/gui/font_16x32.tga
text: gamedata/*.ini

2012年12月4日火曜日

2012年8月2日木曜日

Jade Empire





日本語化

text: dialog.tlk
char magic[8] // "TLK 4.0"
int zero; // 0
int num; // 1feb5
int unk; // 20h
int ofs; // start of msg data
int unused[2]; //0 0

struct msgent[num]
 int unk; // ff ff ff ff
 int ofs; // ofs to msgdata
 short len;
char msgdata[...]

font: fonts/*.abc,sbm
abc:

struct font[0xffff]
 int ofs ; // offset in sbm
 char idx
 char l_space  //?
 char width
 char r_space // ?

sbm:
 byte block[32*32] // 4 chars in 1 block

idx
 0: bit 0-1
 1: bit 2-3
 2: bit 4-5
 3: bit 6-7