1"""Elden Ring Nightreign.
3What is read here is identity, and the module says so rather than implying more. The
4save is fully decrypted (see @ref sl2.crypto.decrypt_nr) and every entry verifies its
5own MD5, so the bytes are not in question — the layout past the roster is. Nightreign
6does not persist a character level, attributes or souls the way the other games do:
7progression lives in relics, unlocked Nightfarers and Nightlord kills, and none of
8those has been pinned against a second save yet.
11from .reader
import is_valid_name, read_utf16, u32
22NR_FACE_MAGIC = b
"FACE"
31NR_PROFILE_STRIDE, NR_NAME_BACK_FROM_FACE, NR_NAME_CHARS = 632, 54, 16
38NR_STEAM_ENTRY, NR_STEAM_OFF = 10, 0x08
45NR_EMPTY_SLOT_MAX_NONZERO = 4096
57 at = menu.find(NR_FACE_MAGIC)
60 menu[at + NR_PROFILE_STRIDE * i : at + NR_PROFILE_STRIDE * i + 4]
62 for i
in range(1, NR_SLOT_COUNT)
64 start = at - NR_NAME_BACK_FROM_FACE
65 return start
if start >= 0
else None
66 at = menu.find(NR_FACE_MAGIC, at + 1)
77 return [
None] * NR_SLOT_COUNT
79 for i
in range(NR_SLOT_COUNT):
80 name = read_utf16(menu, base + NR_PROFILE_STRIDE * i, NR_NAME_CHARS)
81 out.append(name
if name
and is_valid_name(name)
else None)
93 lo, hi = u32(menu, NR_STEAM_OFF), u32(menu, NR_STEAM_OFF + 4)
94 return None if lo
is None or hi
is None else (lo, hi)
101def nr_slot_used(slot):
102 return sum(1
for b
in slot[:0x20000]
if b) > NR_EMPTY_SLOT_MAX_NONZERO
117 "name": name
if (name
and is_valid_name(name))
else "(unnamed slot)",
nr_steam_id(menu)
Read the account this save belongs to.
nr_find_profiles(menu)
Locate the profile table in a decrypted menu entry.