「RELEASE THE SPYCE secret fragrance」は、株式会社オルトプラスが2019年にリリース1したゲームで、すでにサービスが終了しています2。
このゲームにはiPhone版とAndroid版がありますが、本記事はAndroid版アプリを対象とします。この記事は「RELEASE THE SPYCE secret fragrance」の画像と音声データの抽出を目的とするものですが、CRI middlewareを採用する他のスマートフォンゲームでも同様の方法が使えるかもしれません。
著作権に関する注意
この手順を実施して得た画像や音声をインターネット等で配信することは、著作権(公衆送信権)の侵害となる可能性があり、処罰の対象となる可能性があります。私的利用の範囲に留めましょう。
前提条件
- このゲーム (jp.co.altplus.spyce) をインストールしたAndroid端末を所持していること
 - その端末のUSBデバッグを有効化していること
 - adbコマンドが使えること
 - gitコマンドが使えること
 - python3がインストールされていること
 - pipがインストールされていること
 - OS: Ubuntu 20.10 (これ以外でも可能だと思いますが、試していません)
 
作業用ディレクトリを作る
mkdir spyce
export SPYCE=$(pwd)/spyce
必要なツールをダウンロードする
Android Backup Extractor
バックアップしたアプリを解凍するツールです。
https://sourceforge.net/projects/adbextractor/
ZIPファイルがダウンロードされます。abe.jarというファイルが含まれていますので、このファイルを適当な場所に保存してください。
UnityPack (画像抽出用)
UnityPack形式のファイルから画像データ(PNG形式)を抽出するためのpythonスクリプトです。
クローン
cd ${SPYCE}
git clone https://github.com/HearthSim/UnityPack.git
cd UnityPack
git checkout f8cdc2516538d189606a76986ad2d71c3fad5f8b
パッチ当て
リリフレの画像はETC2というフォーマットになっていますが、UnityPackは対応していません。
そこで、ETC2に対応させるためのパッチを当てます。
次の差分データを etc2.patch として保存してください:
diff --git a/bin/unityextract b/bin/unityextract
index 3056b0c..3e12ac2 100755
--- a/bin/unityextract
+++ b/bin/unityextract
@@ -135,6 +135,7 @@ class UnityExtract:
 					image = d.image
 				except NotImplementedError:
 					print("WARNING: Texture format not implemented. Skipping %r." % (filename))
+					raise
 					continue
 
 				if image is None:
diff --git a/unitypack/engine/texture.py b/unitypack/engine/texture.py
index 35a3b21..652ed17 100644
--- a/unitypack/engine/texture.py
+++ b/unitypack/engine/texture.py
@@ -94,6 +94,8 @@ IMPLEMENTED_FORMATS = (
 	TextureFormat.DXT5,
 	TextureFormat.DXT5Crunched,
 	TextureFormat.BC7,
+        TextureFormat.ETC2_RGB,
+        TextureFormat.ETC2_RGBA8,
 )
 
 
@@ -158,7 +160,9 @@ class Texture2D(Texture):
 	def image(self):
 		from PIL import Image
 		from decrunch import File as CrunchFile
+		import etcpack
 
+		mode = None
 		if self.format not in IMPLEMENTED_FORMATS:
 			raise NotImplementedError("Unimplemented format %r" % (self.format))
 
@@ -171,11 +175,19 @@ class Texture2D(Texture):
 		elif self.format == TextureFormat.BC7:
 			codec = "bcn"
 			args = (7, )
+		elif self.format == TextureFormat.ETC2_RGB:
+			codec = "etc2"
+			args = (1, )
+			mode = 'RGB'
+		elif self.format == TextureFormat.ETC2_RGBA8:
+			codec = "etc2"
+			args = (3, )
 		else:
 			codec = "raw"
 			args = (self.format.pixel_format, )
 
-		mode = "RGB" if self.format.pixel_format in ("RGB", "RGB;16") else "RGBA"
+		if not mode:
+			mode = "RGB" if self.format.pixel_format in ("RGB", "RGB;16") else "RGBA"
 		size = (self.width, self.height)
 
 		data = self.image_data
etc2.patch を作成したら、このパッチを適用します:
# UnityPackディレクトリ内で実行する
patch -p1 < etc2.patch
インストール
UnityPackをインストールします。
# UnityPack ディレクトリ内で実行する
pip3 install .
acbpy (音声抽出用)
後述するacb2wavが依存するプログラムです。
cd ${SPYCE}
git clone https://github.com/CrescentApricot/acbpy.git
cd acbpy
pip3 install .
hcapy (音声抽出用)
後述するacb2wavが依存するプログラムです。
cd ${SPYCE}
git clone https://github.com/CrescentApricot/hcapy.git
cd hcapy
sed -i "s,.*url =.*,\turl = https://github.com/Cryptomelone/hca2wav.git,g" .gitmodules
git submodule sync
git submodule update
pip3 install .
acb2wav (音声抽出用)
リリフレの音声はACB(Atom CueSheet Binary)という形式のファイルに格納されています。このファイルからwavファイルとして音声を抽出するために、 acb2wav.py というpythonスクリプトが必要です。
cd ${SPYCE}
git clone https://github.com/KOZ39/acb2wav.git
ls acb2wav/src/acb2wav.py
画像と音声の抽出手順
アプリをPCにバックアップする
リリフレを入れたAndroid端末をPCに接続し、adbコマンドでアプリをPCにバックアップします。
cd ${SPYCE}
# spyce.adb というファイル名でアプリをバックアップする
adb backup -app -obb -f spyce.adb jp.co.altplus.spyce
# Android端末に「フルバックアップ」というウィンドウが
# 出ますので「データをバックアップ」をタップしてください。
バックアップファイルをtarファイルに変換する
バックアップしたspyce.adbをtarファイルに変換します。
java -jar abe.jar unpack spyce.adb spyce.tar
tarファイルを解凍する
spyce.tar を解凍します。 apps というディレクトリが作られます。
tar xf spyce.tar
test -d apps && echo "apps created"
画像を抽出する
画像は apps/jp.co.altplus.spyce/ef/Android ディレクトリ配下に格納されているファイルに含まれています。
cd ${SPYCE}/apps/jp.co.altplus.spyce/ef/Android
ls -l
# drwxrwx--x   6 user user  4096  4月  7 00:45 adveffect
# drwxrwx--x 170 user user  4096  4月  7 00:48 advunit
# drwxrwx--x   2 user user  4096  4月  7 00:45 description
# drwxrwx--x   2 user user  4096  4月  7 00:45 eventquestmapeffect
# ...
UnityPackをインストールしたため、 unityextract コマンドを使用可能な状態になっているはずです。このコマンドを使ってPNGファイルを抽出します。
キャラクターの立ち絵をこのディレクトリの下の texturecharacterstanding のファイルから抽出する例を示します。
cd ${SPYCE}/apps/jp.co.altplus.spyce/ef/Android/texturecharacterstanding
unityextract --image *
unityextract コマンドを実行すると、同じディレクトリにPNGファイルが作成されます。
11001.png がモモ、 11002.png が雪先輩といった具合になっています。
Android ディレクトリ配下の他のディレクトリのファイルも大体同じようにPNGファイルを抽出できるようです。
音声を抽出する
音声は apps/jp.co.altplus.spyce/ef/OtherAssets/Sounds ディレクトリ配下の各ファイルに格納されており、ACB(Atom CueSheet Binary)という形式になっています。
ACBファイルから生音声(wav)を抽出するために、 acb2wav.py を使用します。
タイトル「リリースザスパイス シークレットフレグランス」を各キャラクターが読み上げている音声を抽出する例を示します。
export ACB2WAV_PY=~/acb2wav/src/acb2wav.py # acb2wav.pyがあるパス
                                           # に読み替えてください。
cd ${SPYCE}/apps/jp.co.altplus.spyce/ef/OtherAssets/Sounds
cd Title
$ACB2WAV_PY -i . -e acb
# afs2: 18 files in ar
# afs2: aligned to 32 bytes
# afs2: a file offset is 4 bytes
# Title_002.wav
# Title_003.wav
# Title_004.wav
# Title_005.wav
# ...