Converts .stc
tables into .csv
files.
During login sequence, game client queries Index/version
endpoint to check if client is up-to-date.
If data_version
in response differs from the version
in client preferences, update is initiated.
The following function is used to get the URL:
from hashlib import md5
def get_data_file_full_url(version: str) -> str:
return f'http://dkn3dfwjnmzcj.cloudfront.net/data/stc_{version}{md5(version.encode()).hexdigest()}.zip'
After completing the download, the archive is placed at <internal storage>/Android/data/com.sunborn.girlsfrontline.en/files/stc_data.dat
and then contents extracted in <internal storage>/Android/data/com.sunborn.girlsfrontline.en/files/stc
directory.
※ Game client preferences location is /data/data/com.sunborn.girlsfrontline.en/shared_prefs/com.sunborn.girlsfrontline.en.v2.playerprefs.xml
+---+---+---+---+---+---+
| id | lbs | rows |
+---+---+---+---+---+---+
+---+===================+
| c | column types |
+---+===================+
+=======================+
| jump table |
+=======================+
+=======================+
| data |
+=======================+
where:
id
is au16
integer representing ID of this table, usually matches the file name, e.g.5000.stc
will have ID of 5000lbs
, is au16
length of the last 65536 byte block, not countingid
and itselfrows
is au16
number of rows in this tablec
is au8
number of columns in a rowcolumn types
is a sequence ofu8
integers withc
length, each value represents column type in a row as follows:- 1 =>
i8
- 2 =>
u8
- 3 =>
i16
- 4 =>
u16
- 5 =>
i32
- 6 =>
u32
- 7 =>
i64
- 8 =>
u64
- 9 =>
f32
- 10 =>
f64
- 11 =>
string
with the structure as follows:where+---+---+---+======+ | a | len | str | +---+---+---+======|
a
isis_ascii
flag,str
is ASCII or UTF-8 encoded
- 1 =>
jump table
is a sequence of twoi32
andu32
integers (row_id
and absoluteoffset
) of every 100th row starting with first row (with 0-based indexing: 0th, 100th, 200th, etc)data
is a sequence ofrows
, each row havec
columns
※ Little-endian ordering is used
STC tables themselves don't define their name or column names, a way to acquire them is to dump headers using il2cpp dumper.
il2cpp metadata is encrypted, method unknown, use GameGuardian to dump memory and acquire decrypted file.
※ Do this on an offline virtual device in case some security measure gets tripped
※ Enable Hide GameGuardian from the game
option before dumping memory
After dumping headers:
- look for
CmdDef
enum to find table names - look for classes starting with
Stc
in the name to find column names
Essentially is a .jsonl
file that is gzip compressed and XOR encrypted.
The decryption function is as follows:
key = b'c88d016d261eb80ce4d6e41a510d4048'
def xor(buffer: bytearray, key: bytes):
for i in range(len(buffer)):
buffer[i] ^= key[i % len(key)]