|
SL2 Analyzer
Read a FromSoftware .sl2 save and report what is in it
|
Functions | |
| read_names (paramdex, game, stem) | |
| ds1_spells (i, _n) | |
| ds1_goods (i, _n) | |
| ds3_goods (i, _n) | |
| ds3_weapons (i, _n) | |
| ds3_armors (i, _n) | |
| ds3_rings (i, _n) | |
Variables | |
| W | |
| P | |
| A | |
| G | |
| GEM | |
| JUNK | |
| DEVNAME = re.compile(r" -- ") | |
| dict | NAME_FIXUPS |
| Hand corrections applied last, after the merge. | |
| tuple | DS3_ESTUS = ((150, 171), (190, 211)) |
| DS3 flask ids, in level pairs (full, drained). | |
| dict | STRICT = {"db_ds3/rings"} |
| dict | PRUNE_EXISTING = {"db_ds1/Consumables", "db_ds1/Spells", "db_ds3/rings"} |
| Tables whose gate also applies to what is ALREADY shipped. | |
Regenerate the DS1 and DS3 id->name tables in db_*/ from soulsmods/Paramdex.
Elden Ring and Sekiro are NOT here: both are read out of the installed game instead,
which beat this source measurably in both cases. See the notes in PLAN below.
Pinned source: soulsmods/Paramdex @ ff7245e524329bc3eab00036723d2bd53384cedf
(2026-03-06) -- the commit that carries Elden Ring through Shadow of the Erdtree.
It is idempotent and non-destructive: an existing hand-disambiguated name always wins
on an id collision, so running it can add rows and can never silently rewrite a
name a human decided on.
git clone --depth 1 --filter=blob:none --sparse \
https://github.com/soulsmods/Paramdex.git /tmp/Paramdex
cd /tmp/Paramdex && git sparse-checkout set DS1 DS1R DS3
python3 tools/gen_from_paramdex.py --paramdex /tmp/Paramdex
--dry-run report the delta, write nothing
--only ds1 restrict to one game key (ds1, ds3)
DS2 is deliberately absent: its ids are little-endian save bytes, not param ids,
and its tables are already complete from the SOTFS Hex Code Compendium. Paramdex
DS2S cannot be mapped onto them and must not be imported.
| gen_from_paramdex.read_names | ( | paramdex, | |
| game, | |||
| stem | |||
| ) |
Paramdex Names/*.txt -> {int id: name}. Files are '<id> <name>' per line.
Definition at line 60 of file gen_from_paramdex.py.
| gen_from_paramdex.ds1_spells | ( | i, | |
| _n | |||
| ) |
DS1 stores sorceries/miracles/pyromancies as ordinary goods, 3000-8999. 9000-9014 is the gesture block, which is a good and stays one — filing a gesture under a "Spells" heading would be a worse lie than no heading.
Definition at line 99 of file gen_from_paramdex.py.
Referenced by ds1_goods().
| gen_from_paramdex.ds1_goods | ( | i, | |
| _n | |||
| ) |
Definition at line 106 of file gen_from_paramdex.py.
References ds1_spells().
| gen_from_paramdex.ds3_goods | ( | i, | |
| _n | |||
| ) |
Goods and spells both sit behind 0x40000000 in a DS3 save, so anything at or above the Magic id floor belongs to db_ds3/spells.json, not here. 9000- 9099 is the gesture block: gestures are not reported (they scatter off-grid flag hits and cost real items to acquire).
Definition at line 116 of file gen_from_paramdex.py.
| gen_from_paramdex.ds3_weapons | ( | i, | |
| _n | |||
| ) |
DS3's inventory is found by scanning every byte of the slot for a known id, so a junk row is not free: a dev id matching off the 16-byte record grid splits a run and takes real items with it (a mule lost four items to exactly this). Ammunition sits at 400000-409999 and real armaments at 1000000-29999999; everything Paramdex lists outside those is (Debug)/Test-/Ghost scaffolding, or the sub-10000 thrown-item rows whose small ids match constantly.
Definition at line 126 of file gen_from_paramdex.py.
| gen_from_paramdex.ds3_armors | ( | i, | |
| _n | |||
| ) |
Real DS3 protector ids start at 19000000. Everything below 10^7 in Paramdex is the cut DS1-legacy block (Armor of Favor, Stone Armor) that no DS3 save can hold.
Definition at line 136 of file gen_from_paramdex.py.
| gen_from_paramdex.ds3_rings | ( | i, | |
| _n | |||
| ) |
EquipParamAccessory 10000-19999 is the covenant-badge block, which is rendered from db_ds3/covenants.json. Real rings start at 20000.
Definition at line 143 of file gen_from_paramdex.py.
| gen_from_paramdex.W |
Definition at line 36 of file gen_from_paramdex.py.
| gen_from_paramdex.P |
Definition at line 36 of file gen_from_paramdex.py.
| gen_from_paramdex.A |
Definition at line 36 of file gen_from_paramdex.py.
| gen_from_paramdex.G |
Definition at line 36 of file gen_from_paramdex.py.
| gen_from_paramdex.GEM |
Definition at line 36 of file gen_from_paramdex.py.
| gen_from_paramdex.JUNK |
Definition at line 41 of file gen_from_paramdex.py.
| gen_from_paramdex.DEVNAME = re.compile(r" -- ") |
Definition at line 50 of file gen_from_paramdex.py.
| dict gen_from_paramdex.NAME_FIXUPS |
Hand corrections applied last, after the merge.
Small by design: if this map grows past a dozen entries the upstream table is the thing to fix.
Definition at line 55 of file gen_from_paramdex.py.
| tuple gen_from_paramdex.DS3_ESTUS = ((150, 171), (190, 211)) |
DS3 flask ids, in level pairs (full, drained).
ds3_resolve_estus computes the level from the id, so a table row would only ever disagree with it about the spelling — and would shadow it, since a direct hit wins.
Definition at line 113 of file gen_from_paramdex.py.
| dict gen_from_paramdex.STRICT = {"db_ds3/rings"} |
Definition at line 149 of file gen_from_paramdex.py.
| dict gen_from_paramdex.PRUNE_EXISTING = {"db_ds1/Consumables", "db_ds1/Spells", "db_ds3/rings"} |
Tables whose gate also applies to what is ALREADY shipped.
Everywhere else a gate filters Paramdex only, because a shipped id is one a real save was seen to hold — db_ds3/weapons carries Torch (90000) and Fists (110000), which the weapon gate rejects and which the equipped-weapon read depends on. Pruning is for the DS1 goods/spells split (rows must MOVE, not duplicate) and the ring eviction.
Definition at line 156 of file gen_from_paramdex.py.