SL2 Analyzer
Read a FromSoftware .sl2 save and report what is in it
Loading...
Searching...
No Matches
sl2.nr Namespace Reference

Functions

 nr_find_profiles (menu)
 Locate the profile table in a decrypted menu entry.
 
 nr_roster (menu)
 Read the Nightreign roster: one name per slot, empty where unused.
 
 nr_steam_id (menu)
 Read the account this save belongs to.
 
 nr_slot_used (slot)
 Is this decrypted slot occupied?
 
 nr_parse (name)
 Build the unified character dict for one Nightreign slot.
 

Variables

int NR_SLOT_COUNT = 10
 Number of character slots, matching the ten slot entries in the container.
 
str NR_FACE_MAGIC = b"FACE"
 The four-byte marker that opens each profile's appearance block.
 
 NR_PROFILE_STRIDE
 Bytes between one profile and the next, and where a name sits relative to the appearance marker that anchors it.
 
 NR_NAME_BACK_FROM_FACE
 Bytes between one profile and the next, and where a name sits relative to the appearance marker that anchors it.
 
 NR_NAME_CHARS
 Bytes between one profile and the next, and where a name sits relative to the appearance marker that anchors it.
 
 NR_STEAM_ENTRY
 Which entry carries the account, and where in it.
 
 NR_STEAM_OFF
 Which entry carries the account, and where in it.
 
int NR_EMPTY_SLOT_MAX_NONZERO = 4096
 A slot this empty holds no character.
 

Detailed Description

Elden Ring Nightreign.

What is read here is identity, and the module says so rather than implying more. The
save is fully decrypted (see @ref sl2.crypto.decrypt_nr) and every entry verifies its
own MD5, so the bytes are not in question — the layout past the roster is. Nightreign
does not persist a character level, attributes or souls the way the other games do:
progression lives in relics, unlocked Nightfarers and Nightlord kills, and none of
those has been pinned against a second save yet.

Function Documentation

◆ nr_find_profiles()

sl2.nr.nr_find_profiles (   menu)

Locate the profile table in a decrypted menu entry.

Finds the run of NR_SLOT_COUNT appearance markers spaced exactly NR_PROFILE_STRIDE apart and returns where the first profile's name starts. A single marker proves nothing — character data elsewhere in the file could spell the same four bytes — so the whole run has to hold before anything is returned.

Parameters
menuThe decrypted menu entry.
Returns
Offset of slot 0's name field, or None if no such run exists.

Definition at line 56 of file nr.py.

Referenced by sl2.nr.nr_roster().

Here is the caller graph for this function:

◆ nr_roster()

sl2.nr.nr_roster (   menu)

Read the Nightreign roster: one name per slot, empty where unused.

Parameters
menuThe decrypted menu entry.
Returns
A list of NR_SLOT_COUNT names, each possibly None.

Definition at line 74 of file nr.py.

References sl2.nr.nr_find_profiles().

Here is the call graph for this function:

◆ nr_steam_id()

sl2.nr.nr_steam_id (   menu)

Read the account this save belongs to.

Parameters
menuThe decrypted menu entry.
Returns
(low dword, high dword) of the SteamID64, or None.

Returned as two halves rather than one integer for the same reason every other game here does it: a SteamID64 does not survive a JavaScript double, and the two front ends have to agree byte for byte.

Definition at line 92 of file nr.py.

◆ nr_slot_used()

sl2.nr.nr_slot_used (   slot)

Is this decrypted slot occupied?

Parameters
slotThe decrypted slot entry.
Returns
True if the slot holds a character.

Definition at line 101 of file nr.py.

◆ nr_parse()

sl2.nr.nr_parse (   name)

Build the unified character dict for one Nightreign slot.

Identity only, and every other key is None on purpose so the writers can treat this like any other character without inventing fields. The tier is "roster", which is the whole claim: this save was opened, decrypted and its character named, and nothing further is asserted.

Parameters
nameThe roster name for this slot.
Returns
A unified character dict.

Definition at line 113 of file nr.py.

Variable Documentation

◆ NR_SLOT_COUNT

int sl2.nr.NR_SLOT_COUNT = 10

Number of character slots, matching the ten slot entries in the container.

Definition at line 14 of file nr.py.

◆ NR_FACE_MAGIC

str sl2.nr.NR_FACE_MAGIC = b"FACE"

The four-byte marker that opens each profile's appearance block.

The roster is found by this rather than by a fixed offset. Nightreign writes a variable-length block ahead of the profile table exactly as Elden Ring does, so an offset measured on one save is an offset measured on one save. Ten of these at a constant stride is a shape nothing else in the file reproduces.

Definition at line 22 of file nr.py.

◆ NR_PROFILE_STRIDE

sl2.nr.NR_PROFILE_STRIDE

Bytes between one profile and the next, and where a name sits relative to the appearance marker that anchors it.

Measured: the ten markers in the test save are 632 bytes apart, and the name ends 54 bytes before each one. The name is UTF-16 in a 32-byte field, so NR_NAME_CHARS is sixteen — sl2.reader.read_utf16 counts characters, not bytes, and reading it as 32 would run a name into the appearance block behind it.

Definition at line 31 of file nr.py.

◆ NR_NAME_BACK_FROM_FACE

sl2.nr.NR_NAME_BACK_FROM_FACE

Bytes between one profile and the next, and where a name sits relative to the appearance marker that anchors it.

Measured: the ten markers in the test save are 632 bytes apart, and the name ends 54 bytes before each one. The name is UTF-16 in a 32-byte field, so NR_NAME_CHARS is sixteen — sl2.reader.read_utf16 counts characters, not bytes, and reading it as 32 would run a name into the appearance block behind it.

Definition at line 31 of file nr.py.

◆ NR_NAME_CHARS

sl2.nr.NR_NAME_CHARS

Bytes between one profile and the next, and where a name sits relative to the appearance marker that anchors it.

Measured: the ten markers in the test save are 632 bytes apart, and the name ends 54 bytes before each one. The name is UTF-16 in a 32-byte field, so NR_NAME_CHARS is sixteen — sl2.reader.read_utf16 counts characters, not bytes, and reading it as 32 would run a name into the appearance block behind it.

Definition at line 31 of file nr.py.

◆ NR_STEAM_ENTRY

sl2.nr.NR_STEAM_ENTRY

Which entry carries the account, and where in it.

Entry 10 is the menu block; the SteamID64 is a little-endian uint64 eight bytes in. Checked against the folder the test save shipped in — the file reads 76561197960272671 and the folder is named 76561197960272671.

Definition at line 38 of file nr.py.

◆ NR_STEAM_OFF

sl2.nr.NR_STEAM_OFF

Which entry carries the account, and where in it.

Entry 10 is the menu block; the SteamID64 is a little-endian uint64 eight bytes in. Checked against the folder the test save shipped in — the file reads 76561197960272671 and the folder is named 76561197960272671.

Definition at line 38 of file nr.py.

◆ NR_EMPTY_SLOT_MAX_NONZERO

int sl2.nr.NR_EMPTY_SLOT_MAX_NONZERO = 4096

A slot this empty holds no character.

An unused Nightreign slot is not all zero — it carries about thirty nonzero bytes of scaffolding in a megabyte. A used one is half nonzero, so the threshold is nowhere near anything, and counting is cheap next to decrypting.

Definition at line 45 of file nr.py.