2013年11月24日日曜日

[ネタバレ] ECCO THE DOLPHIN 2の翻訳が英語と日本語訳で全く違う件について

ECCO THE DOLPHIN 2は私が特別に好きなゲームの一つである。
このゲームのラストでプレイヤーに提示される「SECRET PASSWORD」の意味が今の今まで分からず、
ふとしたきっかけで検索したら、以下のページを見つけた。

名作アルバム -『エコー・ザ・ドルフィン』-

セガの名作アルバムである。現在トップページからどのような経路で到達できるかは不明。
このコンテンツの中で興味深い記述を見つけた。
ローカライズを担当された長谷川亮一氏のインタビューだ。

P2
……私は機械的な翻訳は嫌だったので、無機質にならないように、血の通ってる翻訳がしたいと常々思っていました。だから、ただの翻訳だけでなく、プラスアルファとしてストーリーを語ったり、ルールの説明をしたり、かなりの情報を詰め込んだつもりです。
(引用ここまで)

P3
普通の本とか見て方眼紙に点を打つんですよ。(中略)向こうは何を打ってるのか全然わかってないわけですよ(笑)
(引用ここまで)

という事で、かなりの意訳が成されているらしい。

おもいでがいっぱい
今回驚かされたのはメッセージを長谷川さんが書かれていたことです。あのメッセージは谷川俊太郎顔負けの名訳だと思っています。
(引用ここまで)

感想の所には上記のようなメール内容も載せられていた。
私も本当に頑張ってクリアして、エンディングのメッセージには涙を流した。
小さな平仮名とカタカナだけの文章でありながら、今見ても胸が苦しくなる、本物の名訳である。

という訳で、エンディングの翻訳文と原文を比較してみた。

以下原文
Epilogue

The Queen retreats
within her machine.
When ecco leaves she
escapes in the form of
a Vortex larva.

The Queen makes her way
to the sunken city of
Atlantis. She finds the
time machine and escape
into Earth's past.

When the Queen arrives
in the prehistoric era,
She encounters creatures
that she cannot rule.
She has no choice but to
live within the life
cycles of the earth.

And, through the eons,
the Vortexkind integrate
with the life on earth.

Ecco finds the time
machine in the City of
forever. Instead of
destroying it, he uses
it and disappears into
the tides of time.

以下翻訳文
エピローグ

エコーは ながい たびのはてに
とうとう タイムマシンをみつけました。

マシンをはかいすれば すべてが
おわるのです。

、、、ほんとうにおわるのでしょうか、、、

かれは アステライトのことばを
おもいだします、、、
かれのそんざいじしんが
じかんの ぶんきてんなのです。

かれは なかまの イルカのことばを
おもいだします、、、

かれの あたまのもようは
うんめいの しるしなのです。

、、、ほんとうにおわるのでしょうか、、、

かれは すべてをおえる ただひとつの
ほうほうを みつけました、、、

タイムマシンを はかいするかわりに
エコーは ときのとびらをひらくと、
えいえんの じかんのながれのなかに
きえていったのです、、、

最後の部分以外がまるで違う文章に置き換えられている。
英語版は最後に何が起きたのかを、説明的な文章で解説している一方で、
日本語版は、エコーの決断を詩的かつ叙情的に表現している他、
何故エコー自身が何故時間の流れの中に消えていく必要があったのか(エコーの存在自身が時間の分岐点だから、という理由に納得が行くかどうかは別として)を説明している。

英語版のボルテックス・クイーンを倒した際に現れる謎の生物が何だったのかを解説の私による翻訳を載せる。
エピローグ

クイーンは彼女のマシンの中へと退却した。
エコーが去る時、彼女はボルテックスの幼生へと姿を変えて逃げた。

クイーンはアトランティスの水没都市への道を開いた。
彼女はそこでタイムマシンを見つけ、
過去の地球へと逃亡した。

クイーンが有史以前の時代に到着した時、
彼女は彼女には支配出来ない生物たちに遭遇した。
彼女はやむなく地球上のライフサイクルの中で生きていく他無かった。

そして、長い時の流れを経て、
ボルテックスの種は地球上の生命の中に統合されてしまった。

エコーは永遠の街でタイムマシンを見つけた。
彼はそれを破壊する代わりに、
それを使い、時の潮の中へと消えていったのだ。

以上である。ボルテックス・クイーン撃破時に現れ、
CITY OF FOREVERで登場する「かれら」はボルテックスの幼生が地球上の生物の系統の中に統合されたものだったのである。
これらの情報が欠けてしまっているのは残念ではあるが、心に訴えかける素晴らしい名訳であると思う。

ちなみに、最後のSECRET PASSWORDは以下の様な経緯で出来た代物らしい。

おもいでがいっぱい
エンディングのパスワードの件、真相をお教えしましょう。
実はあのパスワードを解析プログラムに入れると、総プレイ時間やミスの数、パズルを解いた効率の良さなどがかなり詳細に分かるのです。
これで「ゲームを一番上手くクリアできた人コンテスト」が出来るようにと開発元が仕込んでくれたのですが、残念ながら日本ではコンテストの企画が通らず(集計が大変だ、というのが理由です。今みたいにネットで応募、とか出来ませんしね)、あのパスワード画面だけが残ってしまった、という訳なのです。

(解析プログラムはフロッピーに入れてマスターROMと一緒に保管したので、今でもセガのどこかにあるんじゃないかなぁ?)
(引用ここまで)

この記述を見る限りは、無印の話なのか2の話かは判然としないが、
私は2しかやっておらず、このパスワードも2で表示されたので恐らく2の話だろう。

2013年11月5日火曜日

C++の名前空間のネストと識別子の衝突と無名名前空間について

私は普段DXライブラリを使っているのだが、 先日、以下の様なコードを書いた所エラーが出た。 ちなみに簡略化された全く意味のないコートであることに留意されたし。

#include "lua.hpp"
#include "DxLib.h"

namespace Lua{
	int Func( lua_State* pLuaState ){
		return 0;
	}
}
namespace DxLib{
	namespace Lua{
	}
}
void Test( void ){
	lua_State* p = NULL;
	Lua::Func( p );
}

エラーの内容は以下のとおり。

error C2872: 'Lua' : あいまいなシンボルです。
'Lua' である可能性があります。
または 'DxLib::Lua'

DXライブラリは、 DxLib.h 内で、名前空間 DxLib を using 宣言している。 このため、名前空間 DxLib::Lua と 名前空間 Lua をコンパイラが区別する事ができず、 コンパイルエラーとなっている。

C++において、普通に宣言した識別子は、無名名前空間(グローバル名前空間とも呼ばれる)に所属している。 無名名前空間に属している事を明示するためには、識別子をスコープ演算子から書き始める。 この記法を用いて修正したコードは以下のとおり。(関数 Test のみ)

void Test( void ){
	lua_State* p = NULL;
	::Lua::Func( p );
}

本当なら、関数 Func は名前空間 Lua にのみ存在するので、参照自体は出来ると思うのだが・・・ もしかしたらVC++ 2010でコンパイル出来ないだけで、他のコンパイラだと通るかもしれない。C++怖い。

ちなみに、以下の様な、識別子を使わない名前空間も無名名前空間と呼ぶ。

namespace{
	...
}

この名前空間内に記述した識別子は、同じソースコード内でしか参照できなくなる。 C++では、 static 修飾子よりも無名名前空間を使用することが推奨されている[要出典]

2013年11月4日月曜日

Luaを使ってクラスっぽい物を生成してCから使う方法 修正版

2013.11.04 追記

以前の投稿が激しく誤った情報を載せていたので修正。

Luaでクラス風のオブジェクトのメンバ関数を呼ぶ際は、 lua_pushvalue 関数を用いて、オブジェクトそのものを引数としてスタックにプッシュしてやる必要がある。

今回はメンバ関数内でオブジェクトのメンバを書き換えていないため、これでも正しく動作するが、 もしメンバ関数内でメンバを書き換える場合は、オブジェクト自身を return し、 lua_replace 関数を用いて返り値を元のオブジェクトに上書きしてやる必要がある。詳細は別途記事を書く予定。
2013.11.04 昨日はそうしないとうまく動かなかった筈ですが今日やったら動きました。 lua_pushvalue でコピーしたテーブルの内容を書き換えた場合って、参照元も書き換わるのかな… よく分からんのでもし分かる人がいらっしゃったらコメントとかで教えて下さい。

main.cpp

#include <lua.hpp>
#include <stdio.h>

int main( void ) {
	lua_State* L = luaL_newstate();	// Luaステートを生成
	luaL_openlibs( L );				// 標準ライブラリを読み込み

	// スクリプトの読み込み
	if( luaL_dofile(L, "script.lua") ){
		// エラー処理
		printf( "%s\n", lua_tostring( L, lua_gettop(L) ) );
		lua_close( L );
		return 0;
	}

	// 犬を生成
	lua_getglobal( L, "Dog" );					// グローバルなテーブル"Dog"をスタックに積む
	lua_getfield( L, lua_gettop( L ), "new" );	// トップに積まれたテーブルのメンバ"new"をスタックに積む
	lua_pushstring( L, "Neko" );				// 名前はNeko。犬なのにNeko。
	lua_pcall( L, 1, 1, 0 );					// 実行。関数と引数はスタックから除去され、返り値が積まれる
	lua_remove( L, -2 );						// 返り値の下にあるDogを除去
	int idx_dog = lua_gettop( L );				// Nekoのスタック上の位置を取得

	// 猫を生成
	lua_getglobal( L, "Cat" );					// グローバルなテーブル"Cat"をスタックに積む
	lua_getfield( L, lua_gettop( L ), "new" );	// トップに積まれたテーブルのメンバ"new"をスタックに積む
	lua_pushstring( L, "Inu" );					// 名前はInu。猫なのにInu。
	lua_pcall( L, 1, 1, 0 );					// 実行。関数と引数はスタックから除去され、返り値が積まれる
	lua_remove( L, -2 );						// 返り値の下にあるCatを除去
	int idx_cat = lua_gettop( L );				// Inuのスタック上の位置を取得
	
	printf( "%d object\n", lua_gettop( L ) );	// スタックのサイズが正しい事を確認

	// メンバ関数showを呼び出し
	lua_getfield( L, idx_dog, "show" );	// 関数をスタック
	printf( "%d object and function\n", lua_gettop( L ) );
	lua_pushvalue( L, idx_dog );		// 引数selfをスタック
	printf( "%d object, function, and argument\n", lua_gettop( L ) );

	// 実行
	if( lua_pcall( L, 1, 0, 0 ) ){
		// エラー出力
		printf( "%s\n", lua_tostring( L, lua_gettop( L ) ) );
	}
	
	printf( "%d objects\n", lua_gettop( L ) );
	
	// メンバ関数showを呼び出し
	lua_getfield( L, idx_cat, "show" );	// 関数をスタック
	printf( "%d object and function\n", lua_gettop( L ) );
	lua_pushvalue( L, idx_cat );		// 引数selfをスタック
	printf( "%d object, function, and argument\n", lua_gettop( L ) );

	// 実行
	if( lua_pcall( L, 1, 0, 0 ) ){
		// エラー出力
		printf( "%s\n", lua_tostring( L, lua_gettop( L ) ) );
	}
	
	printf( "%d object\n", lua_gettop( L ) );

    lua_close( L );	// 終了
    return 0;
}

script.lua

-- Dog
Dog = {}
Dog.new = function( name )
	local obj = {}
	obj.name = name
	obj.show = function( self )
		s = string.format( "%s the DOG.", self.name )
		print( s )
	end
	return obj
end

-- Cat
Cat = {}
Cat.new = function( name )
	local obj = {}
	obj.name = name
	obj.show = function( self )
		s = string.format( "%s the CAT.", self.name )
		print( s )
	end
	return obj
end

2013年11月1日金曜日

BloggerでSyntaxHighlighterを利用する際のTIPS

前の記事で、SyntaxHighlighterを導入してソースコードをBlogger上で表示した。 Luaの表示には公式ウェブサイトからリンクされているサイトの追加ブラシのLua1を利用した。 SyntaxHighlighterを使う際に以下のような問題に遭遇したため、ここにその対処法を示す。
  • Bloggerでは任意のファイルをアップロード出来ない
  • 記事上で、<等の特殊文字がうまく表示できない
  • タブが半角スペースに置換され、インデントがずれる
  • 全ての行の背景が同じ色で表示されるため、少し見辛い
  • shThemeXxx.css と shCoreXxx.css の違いが分からない

Bloggerでは任意のファイルをアップロード出来ない

Bloggerでは、任意のファイルをアップロードする事はできない。よって、JavaScriptやCSSをBloggerのドメインでホスティングする事は出来ず、Alex氏がホスティングしているファイルを参照する事になる。
別にそれはそれで構わないのだが、出来れば自分でホスティングしたい。それに、Syntax Highlighterがサポートしていない形式のコードを扱う時は困る(これも、その作者がホスティングしてくれているファイルを参照すれば問題ないが、精神的によろしくない)。
Google サイトを使った方法や、Google ドライブを利用する方法等があるようだが、いずれも欠点を抱えているようなので、私のオススメはDropboxを利用することである。
ここで注意したいのが、DropboxでのファイルのURLを生成する方法は二種類あることだ。
Publicフォルダに置いたファイルは「公開リンクをコピー」することで、そのURLを取得出来る。この方法で取得するURLには一意なユーザIDと、Publicフォルダ内におけるディレクトリ情報が含まれる。複数のハンドルネームを使っている場合等、「このファイルとあのファイルが置かれているアカウントは同じアカウントだ」とバレたくなければ使わないほうがいいだろう。ただし、内部的に相対パスを利用するファイルではこちらの方法が必須となる。
尚、生成されるURLは https://dl.dropboxusercontent.com/u/userID/filename だ。
Publicフォルダ以外に置いたファイルは「Dropbox リンクを共有」の機能を使用することで、URLを生成することができる。こちらの方法で生成したURLは、どのアカウントのファイルなのかを含め、隠蔽される。相対パスは利用できないが、よりセキュアであると言える。
生成されるURLは https://www.dropbox.com/s/fileID/filename だ。
参考:

記事上で、<等の特殊文字がうまく表示できない

HTMLでは一部の特殊文字は文法上の記法に利用されるため、エスケープシーケンスが必要となる。 置換にはこのサイトを利用した。
HTML特殊文字変換|コードをホームページに載せる時に便利 | すぐに使える便利なWEBツール | Tech-Unlimited 他にもあるかも知れないし、ローカルで出来るようなプログラムがあった方が良いかもしれない。

タブが半角スペースに置換され、インデントがずれる

Bloggerでは、タブが半角スペースに勝手に置換されるため、ソースコードのインデントがずれる。 プレビューではちゃんと表示されるのに、保存、公開した途端に置換されるのが曲者だ。 投稿時の編集のモードをHTMLにし、タブを &#9; に置換して記述することで、この現象を避けることが出来る。 ただし、一度編集のモードを作成にすると、せっかく置き換えたタブが半角スペースに戻ってしまうため注意。 公開後、きちんと投稿後の記事を確認した方が良いだろう。

全ての行の背景が同じ色で表示されるため、少し見辛い

これは、SyntaxHighlighterのCSSを編集することで改善できる。 .syntaxhighlighter .line.alt1 と .syntaxhighlighter .line.alt2 のそれぞれに背景色を設定してやればよい。 詳細は Syntax Highlighterの背景色と行間を変更する | ジャイアントモリンキーのjavaテック を参照。

shThemeXxx.css と shCoreXxx.css の違いが分からない

shCoreXxx.css は shCore.css と shThemeXxx.css を合成したものである。 参考: そらとぶくじら。 SyntaxHighlighter

以上。