Architecture
How Darkstar stores and loads every piece of static game data. The TableDataManager singleton, its 60+ dictionary fields, AES-128-CBC encryption, and how we bypass it all by reading directly from memory.
TableDataManager
Every piece of static game data flows through TableDataManager, a
MonoBehaviour singleton accessed via get_Instance(). It holds 60+
dictionary and list fields spanning every game system — ships, skills,
admirals, equipment, stages, bosses, waves, buildings, gemstones, and more.
On startup the game decrypts .bytes files (AES-128-CBC, key derived from
"tempbreadplugin"), deserializes the contents, and populates these
dictionaries. Our extraction reads them directly from memory, bypassing encryption
entirely.
Identity
TableDataManager.Instance
Package com.funstay.android.global.idlerpgv1
Binary libil2cpp.so (79.6 MB, ARM64)
Singleton Pattern
The singleton is a standard Unity MonoBehaviour pattern. The class exposes a static
get_Instance() property that returns the single live instance. All game
systems reference this instance to look up data by ID at runtime.
// Pseudo-C# from IL2CPP dump
public class TableDataManager : MonoBehaviour
{
public static TableDataManager get_Instance();
// 60+ dictionaries, e.g.:
public Dictionary<int, GeneralShipTable> ship_dict;
public Dictionary<int, GeneralSkillTable> skill_dict;
public Dictionary<int, Dictionary<int, GeneralAdmiralTable>> admiral_dict;
public Dictionary<int, GeneralItemTable> item_dict;
public Dictionary<int, GeneralStageTable> stage_dict;
public Dictionary<StageType, Dictionary<int, GeneralContentsBossTable>> boss_dict;
public Dictionary<int, List<GeneralWaveTable>> wave_dict;
public Dictionary<int, GeneralBuildingTable> building_map;
public Dictionary<int, GeneralGemstoneTable> gemstone_dict;
// ... and many more
}
Data Loading Pipeline
| Step | What Happens | Details |
|---|---|---|
| 1. Read | Load encrypted .bytes asset files |
Bundled in the APK under assets/ |
| 2. Decrypt | AES-128-CBC decryption | Key derived from string "tempbreadplugin" |
| 3. Deserialize | Parse binary data into C# structs | Custom deserializer populates struct fields |
| 4. Index | Insert into dictionaries keyed by ID | Dictionaries on the singleton instance |
| 5. Resolve | Parse CSV strings into arrays, resolve localization IDs | e.g. sEquipEffectTypes → eEquipEffectTypes[] |
What We Extract
12 tables + 28 enums
The extraction tool (extract_game_data.ts) reads 12 data tables
plus 28 enum types from the running game process. A Python launcher
(extract_game_data.py) orchestrates the Frida session and splits the result
into per-table JSON files under data/game_reference/v1.1.79/.
Combat
- Ships
- Skills
- Admirals
Gear
- Equipment / Items
- Gemstones
- Gemstone Slots / Skills / Collections
World
- Stages
- Bosses
- Waves
- Buildings
Extraction Method
We use Frida with the frida-il2cpp-bridge library to attach
to the running game process. The bridge resolves IL2CPP class metadata at runtime, giving
us access to every field on every object instance without needing to reverse-engineer
memory offsets manually.
// Simplified extraction flow (TypeScript / frida-il2cpp-bridge)
const Il2Cpp = await import("frida-il2cpp-bridge");
const domain = Il2Cpp.Domain.reference;
const assembly = domain.assembly("Assembly-CSharp");
const TDM = assembly.image.class("TableDataManager");
const instance = TDM.method("get_Instance").invoke();
// Read a dictionary field
const shipDict = instance.field("ship_dict").value;
// Iterate entries and extract struct fields...
dump.cs) was generated from v1.1.64.
The game is now on v1.1.79. Class names and method signatures are
likely still valid, but field offsets, enum values, and new classes may differ.
The Frida extraction works against the live binary, so it picks up any changes
automatically.
Dictionary Fields
60+ fields
The following are the primary dictionary fields on TableDataManager that
we extract. Each dictionary maps an integer ID (or composite key) to a game data struct.
Extracted Tables
| Field | Type | Struct | Records |
|---|---|---|---|
ship_dict | Dictionary<int, ...> | GeneralShipTable | 763 |
skill_dict | Dictionary<int, ...> | GeneralSkillTable | 633 |
admiral_dict | Dictionary<int, Dictionary<int, ...>> | GeneralAdmiralTable | 440 |
item_dict | Dictionary<int, ...> | GeneralItemTable | 1,474 |
gemstone_dict | Dictionary<int, ...> | GeneralGemstoneTable | 60 |
gemstoneslot_dict | Dictionary<int, ...> | GeneralGemstoneSlotTable | 20 |
gemstone_skill_dict | Dictionary<int, ...> | GeneralGemstoneSkillTable | 6 |
gemstone_collection_dict | Dictionary<int, ...> | GeneralGemstoneCollectionTable | 90 |
stage_dict | Dictionary<int, ...> | GeneralStageTable | 4,903 |
boss_dict | Dictionary<StageType, Dictionary<int, ...>> | GeneralContentsBossTable | 9,000 |
wave_dict | Dictionary<int, List<...>> | GeneralWaveTable | 15,115 |
building_map | Dictionary<int, ...> | GeneralBuildingTable | 84 |
Additional High-Value Tables (Not Yet Extracted)
TableDataManager has 60+ dictionary fields. We currently extract 12. These are the highest-priority candidates for future extraction passes:
| Field | Struct | Purpose |
|---|---|---|
research_dict | GeneralResearchTable | Research tree — major stat source |
mastery_dict | GeneralMasteryTable | Mastery bonuses — confirmed power source |
medal_dict | GeneralMedalTable | Medal system stat bonuses |
stat_dict | GeneralStatTable | Base stat definitions |
enchantcard_dict | GeneralChaosEnchantCardTable | Enchant card system |
chips_dict | GeneralChipsTable | Chip equipment system |
hardware_dict | GeneralHardwareTable | Hardware upgrades |
software_group_dict | GeneralSoftwareTable | Software upgrades |
collectionbook_dict | GeneralCollectionBookTable | Collection book bonuses |
option_dict | GeneralPluginTable | Plugin / option system |
dispatch_level_dict | GeneralDispatchLevelTable | Ship dispatch missions |
parts_dict | GeneralPartsTable | Parts system |
allies_level_dict | GeneralAlliesLevelTable | Allies leveling system |
Encryption Details
The game's static data files use AES-128-CBC encryption. The key is
derived from the string "tempbreadplugin". This encryption protects the
data at rest (on disk and in the APK), but once the game loads and initializes, the
decrypted data lives in plain memory as native C# objects.
"tempbreadplugin" (string-derived)
File format .bytes asset bundles
Bypass Read from memory post-initialization (no decryption needed)
// Data loading sequence (conceptual)
byte[] encrypted = LoadAssetBundle("table_data.bytes");
byte[] key = DeriveKey("tempbreadplugin"); // AES key derivation
byte[] decrypted = AES_CBC_Decrypt(encrypted, key);
TableData parsed = Deserialize(decrypted); // Custom binary format
TableDataManager.Instance.ship_dict = BuildDict(parsed.ships);
.bytes files independently would require
reimplementing the game's custom serializer. Reading from the live process after
initialization gives us the exact same data, already parsed, with zero reverse
engineering of the binary format.
Binary & Runtime
The game is built with Unity 2022.3.21f1 using the IL2CPP scripting backend. This means
all C# game code is compiled to C++ and then to native ARM64 machine code. The resulting
binary is libil2cpp.so at 79.6 MB.
libil2cpp.so (79.6 MB)
Metadata global-metadata.dat (class/method/field names)
Package com.funstay.android.global.idlerpgv1
Target arch ARM64 (aarch64)
IL2CPP Structure
IL2CPP preserves full type metadata in global-metadata.dat. This is what
enables tools like Il2CppDumper to produce a complete C# class dump, and what
frida-il2cpp-bridge uses at runtime to resolve classes, fields, and methods by name
rather than by raw offset.
Static Analysis
Il2CppDumper extracts dump.cs (31 MB) from the
binary + metadata. Contains every class definition, field offset, method signature,
and enum value. Our dump is from v1.1.64.
Runtime Analysis
Frida + frida-il2cpp-bridge attaches to the live process, resolves IL2CPP metadata at runtime, and reads object fields by name. Works against any game version without needing an updated dump.
Key IL2CPP Classes
These are the most important classes for data extraction and game analysis.
| Class | Purpose | Notes |
|---|---|---|
TableDataManager | Singleton data hub | All game tables, 60+ dictionary fields |
GeneralShipTable | Ship definitions | Motherships, drones, escorts, enemies, bosses |
GeneralSkillTable | Skill definitions | Attacks, abilities, passives, triggers |
GeneralAdmiralTable | Admiral definitions | Hero units, ship pairings, buffs |
GeneralItemTable | Equipment/items | Weapons, armor, accessories, consumables |
GeneralStageTable | Stage progression | Rewards, enemy scaling, hierarchy |
GeneralContentsBossTable | Boss scaling | Per-mode boss power/HP, nested by StageType |
GeneralWaveTable | Wave composition | Enemy formations per stage |
GeneralBuildingTable | Base buildings | Production, storage, tech tree |
GeneralMemoryCardTable | Memory card effects | Per-level equip + hold values (struct) |
GeneralMemoryCardSettingTable | Memory card metadata | Rarity, name, conditions |
PropertyType | Universal effect enum | 132+ stat/effect types used everywhere |
GradeType | Rarity enum | Common through Exotic (0–7) |
BattleManager | Battle orchestration | Combat loop, damage calculation |
DamageInfo | Damage data structure | Per-hit damage info passed through combat |
GlobalINIData | Utility methods | Effect formatting, value display |
GetMemoryCardSettingTalbes() has a typo in the game code —
"Talbes" instead of "Tables". This is the actual method name in the binary and must be
used as-is when hooking via Frida.
Struct vs Class Types
In IL2CPP, there is a critical distinction between value types (structs) and reference types (classes). This affects how we read data from memory.
Value Types (Structs)
Stored inline in the dictionary's internal array. No pointer indirection — the data is right there in the buffer.
GeneralMemoryCardTable- Most
General*Tabletypes
Reference Types (Classes)
Dictionary stores a pointer. Must follow the pointer to read actual field data. Adds one level of indirection.
TableDataManageritself- Nested dictionaries and lists