A player skill lives in three Nightreign files: c0000.anibnd (TAE events), c0000_a6x.anibnd (clips) and c0000.behbnd
(the player behaviour graph). An Elden Ring skill needs its TAE converted and its clips copied. A Dark Souls III weapon art
also needs its clips retargeted to the NR skeleton and usually new slots in the graph.
Checked against code: every command and spec format below. Not checked: the order of a full build (taken from the proof table
in DS3-ARTS-IMPORT.md), the hks edits, the param rows and the FX and sound steps, which are still stage scripts. Nothing here
was run or tested in game for this page.
EB means py -3.12 -I -B S:/eclipse-build/eb.py. Shared tools only: ../build/use-shared-tools.md.
What you need
- The tools:
EB tool build skillport behedit ds3retargetprints each folder (SkillPort.exe,BehEdit.exe,Ds3Retarget.exe). - Absolute paths for every file argument. All three resolve a relative path against their own folder, so a relative
output lands inside the shared tool folder. SkillPort resolves against
SKILLPORT_CWDif that variable is set. - The current release's
c0000.anibnd.dcx,c0000_a6x.anibnd.dcxandc0000.behbnd.dcxas the base. If another stage of the same release already changes one of them, build on that stage's file and declarebase_stageinstage.toml. - For an ER skill: the ER anibnd with the section, and SkillPort's
rules.json. The rules file is not in eclipse-build. It was made bylearn_rules.pyinS:/eclipse-0084-stage/ds-weapons-084/tool/rules. - For a DS3 art: the DS3
c0000anibnds, and a spec JSON fortae_ds3.py.
Port an Elden Ring skill
-
Look at what is there:
SkillPort ls <bnd.dcx> [name filter] SkillPort dump <anibnd> <out.tsv> [section ...]dumpwrites one line per event: section, anim, header, clip, start, end, type, group type, params hex. -
Write a spec file with one line per TAE section:
add <section>copies the ER section into the live binder (it refuses a section the live binder already has),convert <section>converts a live section in place. A section is named likea933. -
Convert:
SkillPort port <rules.json> <spec.tsv> <ER anibnd> <live anibnd> <out anibnd> <log.tsv>The comment at the top of
Program.csleaves out<rules.json>and<log.tsv>. The code inPort.csneeds both. Every entry the spec does not touch is kept byte for byte and checked on read-back. -
Copy the clips:
SkillPort hkx-add <out anibnd> <dst anibnd> <src anibnd>=<clip name> ...Clip bytes are not changed. A duplicate is refused.
-
Add effect resources if the skill needs them:
SkillPort res-add <out> <dst bnd> <src bnd>=<entry> | <loose file> ...Each entry gets the next free id of its file extension. A name the destination already has is refused.
-
Fix single events where the automatic rules are not enough (next section).
SkillPort validate <rules.json> <ER anibnd> <NR anibnd> re-checks the rules against the sections both games ship. It is a
check of the tool, not of your port.
What port changes
From the header of Port.cs. These are the rules FromSoftware followed when it converted its own ER player TAE for Nightreign,
measured on the 191 skill sections both games ship:
- A whole-body ER event 605 becomes NR event 10150
{0, from, to, flags 0x101}with the same times, in group type 1005. A part 605 (index 3 to 6) stays 605. - NR's own blend windows (10150 with
10and flags0x200or0x300) are copied from an NR anim that plays the same clip. If there is none, they are placed around the first 605 window. - The parameter length of each event type becomes NR's. An attack event (type 1) grows from 16 to 32 bytes.
- Value rules from
rules.json, for example SpEffect 45 becomes 31 in event 66. - In event 712, an ER-only slot value
(02,03)becomes the event's most common other slot value. - A floor sound in event 129: ER sound type 8 id X becomes NR type 15 id X + 99999999.
Dated note (memory nr-er-tae-conversion, 2026-11-07): with these rules 93.3 % of NR's own events come out byte-identical.
The rest is gameplay retuning that FromSoftware did by hand.
Edit single TAE events
All four take <in anibnd> <out anibnd> <spec.tsv>. Spec lines are tab separated; # starts a comment.
| Command | Spec line | Rules |
|---|---|---|
event-add |
section anim start end type params-hex |
Refuses an event the anim already has, so a second run fails loudly. |
event-del |
same six columns | Every line must match exactly one event. An event group left empty is removed. |
event-edit |
section anim oldStart oldEnd type oldParams-hex newStart newEnd newParams-hex |
Old times match within 0.0001 s; old parameters must match exactly; each line must match one event. |
anim-copy |
section dstAnim srcAnim |
The destination anim must be a slot that imports another anim's HKX. It becomes a copy of the source's events and imports the source's HKX. |
Take the columns for a line straight from a SkillPort dump. SkillPort get <bnd> <out dir> <entry file name> ... writes
binder entries out as files.
Port a DS3 weapon art
1. Retarget the clips
Write a config JSON for Ds3Retarget. Keys, from the Config class (names are not case sensitive, comments are allowed):
| Key | Meaning |
|---|---|
Ds3Binders |
the DS3 c0000.anibnd (skeleton) and the c0000_aNx / _dlcN binders with the clips |
NrAnibnd |
Eclipse's c0000.anibnd (skeleton) |
NrA6x |
Eclipse's c0000_a6x.anibnd (compendium and template clip) |
Template |
NR clip whose track layout the output copies. Default a713_040000.hkx |
Out |
output folder |
Identity |
bones that copy the DS3 world rotation as is |
TransBones |
bones whose translation follows DS3. Default Master, RootPos |
RootScale |
root motion scale. 0 uses the leg-length ratio |
Helpers |
NR helper bone to its driving bone |
LearnMax |
default 200 |
Clips |
list of { "Src": ..., "Dst": ..., "Speed": 1 } |
Then:
Ds3Retarget bones <config.json>
Ds3Retarget build <config.json>
Ds3Retarget pack <config.json> <a6x in> <a6x out>
Ds3Retarget info <anibnd> <clip.hkx> [more binders]
bones writes the bone match to <out>/rep/bones.txt. build writes <out>/hkx/*.hkx and reports. pack adds or replaces the
built clips in a copy of the a6x binder. --out <dir> on any of them overrides the config's Out, which is how a script
builds twice into two folders. Two diagnostics: DR_LIST=1 lists the binder files, DR_ROOT=1 prints root motion of the DS3
clip and the retargeted clip.
2. Convert the TAE events
-
Dump the DS3 section and an NR reference with
SkillPort dump. -
Map the DS3 events with
tools/arts-py/tae_ds3.py:py -3.12 -I -B tae_ds3.py <spec.json> <ds3 dump.tsv> <nr dump.tsv> <SpEffectParam.tsv> <fxr list> <out events.tsv> <log.txt> [fx map.tsv]<fxr list>is a text file offNNN.fxrnames that Eclipse's sfx binders hold. An FX event whose FXR is missing is dropped.[fx map.tsv]hasold<TAB>newlines for DS3 FXRs this stage ports.- Spec keys the script reads:
anims(each withsrc[section, anim],dst[section, anim],clip, optionalheaderandnoFp),judges,bulletJudges,spMap,soundMap,addedSpEffects,artFx,artSpEffects. - Every dropped event is written to the log. Read it.
-
Write the events into the anibnd:
Ds3Retarget tae-set <in anibnd> <out anibnd> <events.tsv>TSV columns:
section anim header(std | imp:<anim id>) clip start end type group params-hex. It replaces the whole event list of each anim the TSV names. A line!new <aNNN> <aTTT>first creates sectionaNNNas an empty copy of sectionaTTT. -
Run
SkillPort portwithconvert <section>lines for the new sections. It applies the ER to NR rules above, which is what turns the kept 605 events into 10150 and grows the attack events.
What tae_ds3.py does with DS3 events, from its header:
- Most types exist in NR with the same layout and are kept byte for byte.
- Attack (1) and bullet (2): the behaviour judge is remapped by
judges/bulletJudges. A judge mapped to null drops the event. - FX (96): padded to 32 bytes; dropped if the FXR is in no Eclipse sfx binder.
- SpEffect (66, 67): dropped if the id is not in SpEffectParam or
addedSpEffects. Event 331 is a pair (SpEffect with FP, SpEffect without FP); the second slot is cleared. - Sound (128) becomes NR's player form 129. DS3 armour-material sounds (type 9) are dropped.
- Types 117, 130 and 229 are dropped.
Use the frozen S:/eclipse-0086-stage/ds3-retarget-086/tool/tae_ds3.py only to reproduce 0.0.86's events. It keeps sound
event 128, and those arts were silent in game.
3. Add graph slots
HKLib cannot read Nightreign's player graph, so BehEdit edits the file's bytes directly and proves on read-back that every original byte it did not mean to change is the same.
BehEdit probe <behbnd>
BehEdit show <behbnd> <CMSG name regex>
BehEdit find <behbnd> <animation name regex>
BehEdit apply <behbnd in> <spec.tsv> <behbnd out> <log.txt>
Run probe first. It must report a byte-identical round trip. show and find tell you which selector (CMSG) and template
clip generator to copy.
apply spec lines, applied in order, # comments:
clip <CMSG name> <template clip-generator name> <new animation name>: a copy of the template clip generator that plays the new animation, appended to the CMSG.section <src section aSSS> <dst section aDDD> [animation regex]: for every clip generator of sectionaSSS, a copy that playsaDDD_<same anim id>, added to every CMSG that lists the source.
A line that contains a TAB is split on tabs only, because some names hold a space. A line without a tab is split on blanks.
Other commands:
| Command | Arguments | What it does |
|---|---|---|
interval |
<behbnd in> <state machine name> <event name> <behbnd out> <log> |
sets the four interval event ids of that wildcard transition to -1 |
selfix |
<behbnd in> <state machine name> <state name> <selector name> <behbnd out> <log> |
names the state's selector and sets its two end-of-clip ids to -1 |
names |
<behbnd> <out tsv> |
every event, variable and state name |
events |
<behbnd> [from id] |
counts, then event names from that id |
clipcmp |
<behbnd> <anim regex A> <anim regex B> |
two clip generators side by side |
wcdump |
<behbnd> <state machine name> |
every wildcard transition of the state machine |
itemdump |
<behbnd> <item index> [depth] |
one object's members |
files |
<behbnd> |
id, size and name of every binder file |
names and selfix were not re-run in the import proof.
4. The rest
These steps are still per-stage scripts and are not shared tools yet (tools/README.md, "Waiting to be imported"):
- hks:
hks_patch.pyin the art stages wires the new art type. - Param rows:
rows_arts.pyandrows_lc.pygenerate the rowlist. Apply it with Polish, see edit-params-and-rowlists.md. - Effects: the DS3 FXRs are ported with
sfx_port.mjs,NpcPort sfxbnd-buildandFxModelsGen. - Sounds: add-ds3-sounds.md.
How to tell it worked
- Every tool prints a read-back summary and exits non-zero when the check fails.
SkillPort dumpof the output shows your sections and events.BehEdit showon the output lists the new clip generators under the CMSG.- Only a look in game proves the skill plays.
What goes wrong
- A relative path writes into the shared tool folder. Use absolute paths.
- A weapon buff shows nothing. Dated note (memory
ds3-nr-retarget, 0.0.87): a buff must go from TAE event 331 through a per-hand carrier SpEffect to the buff row, like the vanilla buffs. A buff row straight from 331 shows nothing. - A sound posts many times. A full-body effect row spawns its FXR many times on the player and each copy posts its sound.
Strip the sound from the FXR and post it once from TAE (
tools/README.md). - SpEffect ids above 16,777,216 in hks round. See the note on port-a-ds3-or-er-character.md.
- Never substitute a Nightreign look-alike effect for a DS3 one (team rule in the same memory note).