はじめに
Cocos2d-xで開発する時に、言語は何を使っているでしょうか?
C++が多いような気がしますが、手っ取り早く開発するには、Lua、JavaScriptも捨てがたいかと思います。
最近ではCocos Code IDEを使えば開発もし易いですね。
ただ、スクリプト言語で開発する場合、そのままだと生のスクリプトファイルが
アプリのパッケージに含まれてしまうため、コードの解析が容易になってしまいます。
これは企業で開発している場合は致命的ですし、個人開発でも避けたい問題です。
この問題を解決するには、スクリプトファイルを暗号化するのが良さそうです。
Cocos2d-xのcocosコマンドには、スクリプトファイルのコンパイル・暗号化のコマンドが用意されており、
コマンド一発で暗号化できます。
luacompile コマンド
Luaの場合、luacompile
というコマンドを使います。
リファレンスはここです。
このコマンドは、Luaスクリプトをコンパイルし、バイトコードに変換してくれるコマンドで、
オプションを指定することで暗号化までやってくれます。
Luaスクリプトのコンパイル
まずは暗号化はせず、コンパイルだけで実行してみます。
mv src _src
cocos luacompile -s _src -d src
luacompileコマンドでは、読み込み元ディレクトリ(-s)と、出力先ディレクトリ(-d)を指定するので、
srcディレクトリを*_srcにリネームしてから実行しています。
実行後にsrc*ディレクトリを見ると、.luacという拡張子のファイルが出来ているはずです。
これがバイトコードに変換されたファイルになります。
バイトコードで実行してみる
それではAndroidでゲームを実行してみましょう。Androidで!
cocos run -p android
ちなみにAppDelegate.cppの中でsrc/main.lua
を指定しているのですが、
このファイル名は**.luacに変更しなくても問題なく動作します。
詳細はCocos2dxLuaLoader.cppを見ればわかりますが、
.lua、.luac**のどちらか存在している方のファイルを実行しているからです。
さて、勘のいい方はお気づきでしょうが、iOSではLuaバイトコードは実行できません。
これはCocos2d-xの問題ではなく、LuaJITがarm64に対応していないということが原因のようです。
Luajit bytecode not supported on arm64?
(追記)Cocos2d-x 3.6でLuaJIT2.1にアップデートされたようです。未確認ですが、これによりarm64に対応されているはずです。
試しにXcodeでターゲットをiOSではなくMacにしてみると、問題なくロードすることができます。
luacompile
では、コンパイルせずに、テキストのまま暗号化することもできるため、その方法を後述します。
Luaスクリプトの暗号化
次はコンパイルしたファイルをさらに暗号化してみましょう。
ちなみに暗号化はXXTEAというアルゴリズムを使っているようです。
C++だとxxtea.cppというソースで複合化を実行しています。
cocosコマンドは、Pythonスクリプトの中でXXTEAの暗号化処理が実装されています。
暗号化するには、luacompile
コマンドに、-e
というオプションを付けるだけです。
cocos luacompile -s _src -d src -e
これでバイトコードをさらに暗号化してくれます。簡単ですね。
暗号化キーを指定しないと、デフォルトのキー(2dxLua)になってしまうので、
暗号化キーを指定しましょう。-k
でキーを指定します。
cocos luacompile -s _src -d src -e -k 1234567890ABCDEF
xxtea.cppを見ると、NULL文字で埋めて16桁にしているようなので、16桁で指定するのが良さそうです。
-s(encryptsign)というオプションもあるのですが、これはデフォルト(XXTEA)のままでも良いと思います。
暗号化する前にファイル内に指定した文字を埋め込んで、複合化の時にその文字と一致するかを
チェックしているようです。
さて、暗号化ではキーを指定するので、複合の際も同じキーを指定する必要があります。
AppDelegate.cppのAppDelegate::applicationDidFinishLaunching
に、
以下のコードを追加しましょう。
bool AppDelegate::applicationDidFinishLaunching()
{
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);
lua_State* L = engine->getLuaStack()->getLuaState();
lua_module_register(L);
// If you want to use Quick-Cocos2d-X, please uncomment below code
// register_all_quick_manual(L);
// ここから追加 ////////
const char *key = "1234567890ABCDEF";
const char *sign = "XXTEA";
engine->getLuaStack()->setXXTEAKeyAndSign(key, (int)strlen(key), sign, (int)strlen(sign));
// ここまで追加 ////////
if (engine->executeScriptFile("src/main.lua")) {
return false;
}
return true;
}
setXXTEAKeyAndSign
でキーをセットすることで、Luaファイルをロードする際に、
複合化の処理が実行されるようになります。
Luaの中でrequireしたファイルはどうなるの?と疑問に思うかもしれません。
Cocos2d-x LuaBindingsでは、requireが実行される際、Cocos2dxLuaLoader.cppの中の
cocos2dx_lua_loader
という関数を実行して、ファイルを読み込んでいます。
この関数の中で複合化処理が実行されるため、requireしたファイルも問題なく複合化されます。
逆に、setXXTEAKeyAndSign
をでキーをセットしている場合は、暗号化されていないファイルは
reqiureでエラーになります。
テキストのまま暗号化
iOSでは、Luaバイトコードはロードできないと述べました。
そのためiOSでアプリをリリースしたい場合は、テキストのまま暗号化のみを行うことになります。
コンパイル処理をスキップするには、--disable-compile
オプションを指定します。
cocos luacompile -s _src -d src -e -k 1234567890ABCDEF --disable-compile
これで暗号化のみが実行されます。
こうしておけば、暗号化してもiOS/Android両方で実行できるようになります。
おわりに
スクリプトファイルの暗号化については、知りたい方も多いかと思うのですが、
意外に情報が少なかったため、書いてみました。
以上です。