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.

Unity 2022.3.21f1 IL2CPP backend v1.1.79 frida-il2cpp-bridge

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.

Key Insight
Because we read from the live process after initialization, we never need to deal with the AES encryption or the serialization format. The data is already parsed into native C# objects in memory.

Identity

Assembly Assembly-CSharp Base class MonoBehaviour Access 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

StepWhat HappensDetails
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. sEquipEffectTypeseEquipEffectTypes[]

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...
Version Note
The IL2CPP dump (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

FieldTypeStructRecords
ship_dictDictionary<int, ...>GeneralShipTable763
skill_dictDictionary<int, ...>GeneralSkillTable633
admiral_dictDictionary<int, Dictionary<int, ...>>GeneralAdmiralTable440
item_dictDictionary<int, ...>GeneralItemTable1,474
gemstone_dictDictionary<int, ...>GeneralGemstoneTable60
gemstoneslot_dictDictionary<int, ...>GeneralGemstoneSlotTable20
gemstone_skill_dictDictionary<int, ...>GeneralGemstoneSkillTable6
gemstone_collection_dictDictionary<int, ...>GeneralGemstoneCollectionTable90
stage_dictDictionary<int, ...>GeneralStageTable4,903
boss_dictDictionary<StageType, Dictionary<int, ...>>GeneralContentsBossTable9,000
wave_dictDictionary<int, List<...>>GeneralWaveTable15,115
building_mapDictionary<int, ...>GeneralBuildingTable84

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:

FieldStructPurpose
research_dictGeneralResearchTableResearch tree — major stat source
mastery_dictGeneralMasteryTableMastery bonuses — confirmed power source
medal_dictGeneralMedalTableMedal system stat bonuses
stat_dictGeneralStatTableBase stat definitions
enchantcard_dictGeneralChaosEnchantCardTableEnchant card system
chips_dictGeneralChipsTableChip equipment system
hardware_dictGeneralHardwareTableHardware upgrades
software_group_dictGeneralSoftwareTableSoftware upgrades
collectionbook_dictGeneralCollectionBookTableCollection book bonuses
option_dictGeneralPluginTablePlugin / option system
dispatch_level_dictGeneralDispatchLevelTableShip dispatch missions
parts_dictGeneralPartsTableParts system
allies_level_dictGeneralAlliesLevelTableAllies 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.

Algorithm AES-128-CBC Key source "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);
Why Memory Reading Wins
Decrypting and deserializing the .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.

Engine Unity 2022.3.21f1 Backend IL2CPP (C# → C++ → ARM64) Binary 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.

ClassPurposeNotes
TableDataManagerSingleton data hubAll game tables, 60+ dictionary fields
GeneralShipTableShip definitionsMotherships, drones, escorts, enemies, bosses
GeneralSkillTableSkill definitionsAttacks, abilities, passives, triggers
GeneralAdmiralTableAdmiral definitionsHero units, ship pairings, buffs
GeneralItemTableEquipment/itemsWeapons, armor, accessories, consumables
GeneralStageTableStage progressionRewards, enemy scaling, hierarchy
GeneralContentsBossTableBoss scalingPer-mode boss power/HP, nested by StageType
GeneralWaveTableWave compositionEnemy formations per stage
GeneralBuildingTableBase buildingsProduction, storage, tech tree
GeneralMemoryCardTableMemory card effectsPer-level equip + hold values (struct)
GeneralMemoryCardSettingTableMemory card metadataRarity, name, conditions
PropertyTypeUniversal effect enum132+ stat/effect types used everywhere
GradeTypeRarity enumCommon through Exotic (0–7)
BattleManagerBattle orchestrationCombat loop, damage calculation
DamageInfoDamage data structurePer-hit damage info passed through combat
GlobalINIDataUtility methodsEffect formatting, value display
Typo Alert
The method 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*Table types

Reference Types (Classes)

Dictionary stores a pointer. Must follow the pointer to read actual field data. Adds one level of indirection.

  • TableDataManager itself
  • Nested dictionaries and lists
Why This Matters
When using frida-il2cpp-bridge, the library handles this distinction automatically. But for raw memory scanning (the legacy Windows approach), you must know whether a type is a struct or class to calculate the correct memory offset for each field.