|
SL2 Analyzer
Read a FromSoftware .sl2 save and report what is in it
|
Functions | |
| address (fid) | |
| Flag id -> (distance into the flag region, bit), or None outside groups 0..9. | |
| label_for (names) | |
| The best label for a flag, given every row that carries it. | |
| main () | |
Variables | |
| BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| SRC = os.path.join(BASE, "test", "flag_data", "files8", "dark_souls_3_flags.csv") | |
| OUT = os.path.join(BASE, "db_ds3", "npcs.json") | |
| int | GROUP_STRIDE = 128 |
| int | GROUP_ZERO = 111 |
| dict | TITLE |
Build `db_ds3/npcs.json` — Dark Souls III NPC deaths, hostility and quest states.
Sources, both in `test/flag_data/files8/dark_souls_3_flags.csv`: Smithbox's committed
`Documentation/DS3/Info - QWC Flags.txt`, which is FromSoft's own English descriptions,
and rows extracted from the decompiled `.emevd` (`20006002` awaits a `CharacterDead` then
sets a flag; `20006000` sets `HostileNPC` then a flag).
THE BASE IS THE INTERESTING PART, and it is derived rather than taken from anywhere. Three
DS3 common-group bases had been pinned one differential at a time; they are not
independent. A group holds `n < 1000`, so it occupies 128 bytes, and groups 0..9 are packed
128 apart inside the `k = 0` category:
base(g) = 111 + 128 * g # g < 10
`6 -> 879` and `9 -> 1263` fall straight out of that, which is two of the three already
known. It predicts `1 -> 239`, and group 1 is where most of these flags live. Checked three
ways: at 239 all 161 group-1 flags are MONOTONE across a 79-save ladder with 43 ever set,
the only rival candidate is non-monotone eleven times, and -- the one that settles it --
flag `1218` "character 3000700 killed" first reads set in exactly the 33:31:38 snapshot,
which is where Ringfinger Leonhard's own Red Eye Orb pickup flag turns on. 3000700 is
Leonhard. The whole family dates correctly besides: `4500701` (Ariandel) at 45:06,
`5100810` (Ringed City) at 56:59, `5000705` (Dreg Heap) at 62:49.
TWO WARNINGS THAT MUST SURVIVE INTO THE REPORT.
* The QWC descriptions are NOT reliable per row. The same ladder that confirmed the base
shows `1158` -- which the source calls "after killing Leonhard" -- never setting on a
run where Leonhard demonstrably died. The EMEVD-derived row is right and the English
label is on the wrong one. Where a flag has both, the English is kept because it is
what a reader can use, and the entity id is appended so a wrong label is checkable.
* `character 3000700 killed` is an entity id, not a name. Rows with no English label
print that, because the alternative is inventing a name.
Only flags in groups 0..9 are emitted. The `70000` and `73xxx`-`74xxx` shop and handover
groups are two orders of magnitude past the single-digit grid, so this shortcut cannot
reach them and each needs its own anchor -- a differential either side of handing something
to the Shrine Handmaid.
Run from repo root: python3 tools/gen_ds3_npcs.py
| gen_ds3_npcs.address | ( | fid | ) |
Flag id -> (distance into the flag region, bit), or None outside groups 0..9.
Definition at line 70 of file gen_ds3_npcs.py.
Referenced by main().
| gen_ds3_npcs.label_for | ( | names | ) |
The best label for a flag, given every row that carries it.
An English description beats an entity id, because it is the half a reader can act on. The entity ids are appended when there is a description AND ids, so a label the ladder later proves wrong – and one already is – can be checked against the entity that actually died rather than quietly believed.
Definition at line 85 of file gen_ds3_npcs.py.
Referenced by main().
| gen_ds3_npcs.main | ( | ) |
Definition at line 96 of file gen_ds3_npcs.py.
References address(), label_for(), and main().
Referenced by main().
| gen_ds3_npcs.BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
Definition at line 49 of file gen_ds3_npcs.py.
| gen_ds3_npcs.SRC = os.path.join(BASE, "test", "flag_data", "files8", "dark_souls_3_flags.csv") |
Definition at line 50 of file gen_ds3_npcs.py.
| gen_ds3_npcs.OUT = os.path.join(BASE, "db_ds3", "npcs.json") |
Definition at line 51 of file gen_ds3_npcs.py.
| int gen_ds3_npcs.GROUP_STRIDE = 128 |
Definition at line 55 of file gen_ds3_npcs.py.
| int gen_ds3_npcs.GROUP_ZERO = 111 |
Definition at line 56 of file gen_ds3_npcs.py.
| dict gen_ds3_npcs.TITLE |
Definition at line 58 of file gen_ds3_npcs.py.