Skip to content

Repository files navigation

E7 Codex — build tooling

The pipeline behind e7codex.com: a static, browsable archive of Epic Seven character/artifact art with a live in-browser Spine model viewer. This repo is the tooling — it turns raw, extracted game assets into the finished static site. It ships no game assets; you bring your own.

What's here: the indexer, the asset-staging + pose-rendering pipeline, the SCSP→Spine converters (with attribution — see CREDITS.md), and the site UI.

What's NOT here (by design): any Epic Seven assets, the cached spine-player runtime, and the community name databases. The steps below fetch or generate each of those locally.

How it fits together

raw .scsp/.sct/.atlas  ──►  converters  ──►  staged Spine JSON  ──►  site/assets/<slug>/
   (you supply)            (this repo)        + decoded .png            │
                                                                        ▼
                          build_index.py  ──►  site/data/*.json  ◄── names from
                                                                      community DBs
                          render_poses.js ──►  pose.png thumbnails
                                                                        │
                                                                        ▼
                                              site/index.html + viewer.html

Prerequisites

  • Python 3.11+ (pip install lz4 pillow texture2ddecoder)
  • Node 18+ (npm install — pulls puppeteer + sharp for thumbnail rendering)
  • Your own extracted Epic Seven assets (see step 2)

Setup

1. Cache the Spine runtime (one-shot)

spine-player is not redistributed here. Fetch stock 3.8 from Esoteric and apply the one-line screenshot patch:

curl -sSL -A "Mozilla/5.0" -o site/spine-player.js  https://esotericsoftware.com/files/spine-player/3.8/spine-player.js
curl -sSL -A "Mozilla/5.0" -o site/spine-player.css https://esotericsoftware.com/files/spine-player/3.8/spine-player.css
copy site\spine-player.js  tools\spine-player.js
copy site\spine-player.css tools\spine-player.css

Then, in site/spine-player.js (around line 11071), enable buffer readback so the viewer's screenshot button works:

// find:    var webglConfig = { alpha: config.alpha };
// change:  var webglConfig = { alpha: config.alpha, preserveDrawingBuffer: true };

Optional — Spine-2.1.x shear-free scale inheritance. Spine 2.1.x tracked world scale as scalars and rebuilt a clean rotation×scale matrix per bone, so a non-uniform-scaled rotated parent never sheared its children. spine-player 3.8 composes full 2×2 matrices, which do accumulate shear there — exploding long-aspect weapon meshes on some 2.1.27 combat rigs into needles. The converter tags affected rigs with "e7v21x": true; a stock player ignores the flag (the needle bug remains). To honor it, in site/spine-player.js find the start of Bone.prototype.updateWorldTransformWith — just after var parent = this.parent; (stock 3.8 follows it with if (parent == null) {) — and insert this gated branch before that if:

if (this.skeleton.data.e7v21x) {
    var sk = this.skeleton;
    var cosD = spine.MathUtils.cosDeg, sinD = spine.MathUtils.sinDeg;
    if (parent == null) {
        var wr = rotation, wsx = scaleX * sk.scaleX, wsy = scaleY * sk.scaleY;
        this._e7wr = wr; this._e7wsx = wsx; this._e7wsy = wsy;
        this.a = cosD(wr) * wsx; this.b = -sinD(wr) * wsy;
        this.c = sinD(wr) * wsx; this.d = cosD(wr) * wsy;
        this.worldX = x * sk.scaleX + sk.x;
        this.worldY = y * sk.scaleY + sk.y;
        return;
    }
    var pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
    this.worldX = pa * x + pb * y + parent.worldX;
    this.worldY = pc * x + pd * y + parent.worldY;
    var pwr = parent._e7wr || 0;
    var pwsx = parent._e7wsx != null ? parent._e7wsx : 1;
    var pwsy = parent._e7wsy != null ? parent._e7wsy : 1;
    var tm = this.data.transformMode;
    // Normal(0)/NoScale(3)/NoScaleOrReflection(4) inherit rotation;
    // Normal(0)/NoRotationOrReflection(2) inherit scale.
    var inhRot = (tm == 0 || tm == 3 || tm == 4);
    var inhScale = (tm == 0 || tm == 2);
    var wr = inhRot ? pwr + rotation : rotation;
    var wsx = inhScale ? pwsx * scaleX : scaleX;
    var wsy = inhScale ? pwsy * scaleY : scaleY;
    this._e7wr = wr; this._e7wsx = wsx; this._e7wsy = wsy;
    this.a = cosD(wr) * wsx; this.b = -sinD(wr) * wsy;
    this.c = sinD(wr) * wsx; this.d = cosD(wr) * wsy;
    return;
}

You also need the loader to carry the flag through: find skeletonData.imagesPath = skeletonMap.images; and add skeletonData.e7v21x = skeletonMap.e7v21x; after it. The branch is gated on the flag, so 3.8.99 rigs and any bone without non-uniform scale under rotation are untouched.

2. Bring your own data

This is the step you do yourself. You need:

  • Raw rigs — the game's .scsp skeleton files (+ .sct textures, .atlas) for portraits and combat models. Extracting these from the client is on you; the community tool for it is EpicSevenAssetRipper.
  • Names / slugs — point the indexer at the public community databases (ceciliabot, epic7rtastats). See CREDITS.md. These are not bundled.
    • Optional, self-sufficient: if you have your own decrypted game data + keys set up (see the voice pipeline below — same voice_keys.json), run python tools/build_names.py to pull names + rarity/attribute/role straight from the game into data_external/names_from_db.json. The indexer layers it ahead of the community DBs. This also writes unreleased_units.json (units the game still labels "Unknown Hero"); the indexer drops those — the project does not publish unannounced/datamined units. Both files are gitignored.
    • Artifacts (same setup): python tools/build_artifacts.py decodes equip_item.db into data_external/artifacts_from_db.json, layered ahead of the community Artifacts.json for name/rarity/role. Gitignored.
    • Localization (same setup): python tools/build_i18n.py decodes each language's text.db into per-language display-name overlays (site/data/lang/<lang>.json) that power the site's language switcher (10 game languages + unofficial Vietnamese). It needs the same keys/data as build_names.py, plus hand-maintained UI-chrome translations (data_external/i18n_ui/<lang>.json, not bundled). Without the overlays the switcher still renders and every name falls back to English.

Tell the pipeline where your data lives in one place: copy tools/voice_keys.example.json to tools/voice_keys.json (gitignored) and set raw_dir (your extracted output/), img_dir (decoded images), and voice_dir (voice scratch). tools/paths.py resolves these for every tool, so there's no code to edit; omitted keys default under dump_dir.

3. Convert + stage

python tools/prepare_assets.py --all          # portraits → site/assets/<slug>/
python tools/prepare_combat_assets.py --all    # combat rigs (optional)

tools/scsp_to_json.py auto-detects the rig version (2.1.27, 3.8.x, or 4.2.x) and dispatches to the right converter — the vendored third-party ones for 2.1.27 / 3.8.x, and tools/skel42_to_json38.py for Spine 4.2 rigs (an E7-wrapped stock .skel, converted down to 3.8-compatible JSON so the same spine-player renders every rig).

4. Render pose thumbnails

node tools/render_poses.js        # bakes site/assets/<slug>/pose.png

Optionally, bake tighter character-only hub thumbnails (the site falls back to pose.png when these are absent). Smart-crop is baked into the PNG at render time:

node tools/render_thumbs.js       # site/assets/<slug>/thumb.png (FX/backdrop stripped, smart-cropped)

5. Build the data index

python build_index.py --img <your_img_dir> --raw <your_raw_dir> --out ./site

6. Run locally

cd site
python -m http.server 8765
# visit http://localhost:8765/

Note: site/index.html, viewer.html, and 404.html reference favicon-16.png, favicon-32.png, and apple-touch-icon.png, which are not shipped. Drop your own icons into site/ or remove the <link rel="icon"> tags — the site works either way (the browser just shows a default favicon).

Optional: voice pipeline

The site can show per-hero voice-actor credits and a clickable catalog of voice clips. This is an optional add-on with extra prerequisites, and like the rest of the repo it ships no game data and no keys — you supply both.

You'll need:

  • vgmstream — download a release build from vgmstream and unzip it into tools/vendor/vgmstream/ (so tools/vendor/vgmstream/vgmstream-cli.exe exists).
  • FFmpeg — install it and make sure ffmpeg is on your PATH.
  • Your own keys + data — copy tools/voice_keys.example.json to tools/voice_keys.json (gitignored) and fill in the values from your own install: dump_dir (+ optional raw_dir / img_dir / voice_dir), your outer-XOR key file (outer_key_file), and the default XXTEA key (default_xxtea_key). For sync_voice_banks.py, optionally set game_sound_dir (your read-only <game install>/data.unpacked/sound). None of these are provided here.
  • Your own voice banks — the FMOD .bank files, under <sound_dir>/<lang>/*.bank.

Then:

# Credits + a fallback line catalog from your local data files → site/data/voices.json
python tools/build_voices.py

# (optional) copy banks OUT of your game install into the voice scratch tree,
# with a download-settle wait + per-language freshness gate (never writes to the game folder)
python tools/sync_voice_banks.py --extract       # syncs + chains extraction for changed langs

# Decode the voice banks → OGG + a per-language catalog
python tools/extract_voice_audio.py --langs en --slugs c1001     # subset / pilot
python tools/extract_voice_audio.py --langs en ja ko --all       # full

extract_voice_audio.py reads banks from / writes OGGs to your configured voice_dir (from voice_keys.json, default <dump_dir>/_voice_work); override per-run with --sound / --out (or the E7_VOICE_SOUND / E7_VOICE_OUT env vars). The generated audio and JSON stay local — they're gitignored and not part of this repository.

Deploying

site/ is a self-contained static bundle — serve it from any static host (GitHub Pages, Cloudflare Pages, Netlify, a plain web server, etc.). The Spine assets under site/assets/ are large; for production you'll likely want to offload them to object storage and point the viewer at that host instead of serving them inline. That wiring is left to you.

Credits & licensing

See CREDITS.md for full attribution and LICENSE for terms. In short: the E7 Codex code is MIT; the vendored converters keep their original authors' terms; spine-player is Esoteric Software's; and Epic Seven assets belong to Smilegate / Super Creative and are not part of this repository.

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages