The DS3 Codex
Chapter X

The save file

What a DS30000.sl2 holds, as read from a real save (Steam build 10167187, a level 9 Knight 6 minutes in), with a read-only reader we wrote. Written in October 2026.

Twelve sealed entries under one key, each with its own seal of MD5. Inside, the world's every sword and helm, the Knight's stats to the last point, and a full copy of the rules the game was played by.Codex · DS30000.sl2

In short
  • The save is a BND4 binder of 12 entries, each AES-128-CBC encrypted with one fixed key and an MD5 over the rest of the entry. All 12 MD5s check, and every entry decrypts. data
  • Entries 0-9 are the ten character slots (768 KiB each), 10 is the account (the menu's slot summaries, the Steam id, options), and 11 is a copy of the regulation (all 103 params, identical to the game's). data
  • A slot starts with its map, play time and a section table. Then come the game's item-instance table (every weapon and armour piece in the world, NPCs' included), a copy of the in-memory PlayerGameData (stats, HP, souls, name) and the equipment and inventory. data
  • Not found yet: the event flags, the saved position, and most of the slot's back half. Finding them needs two saves that differ by one known action (method at the end). open

Tags, as on the other pages: data read from the save or game files and checked against something independent; guess an interpretation that fits but isn't proven; open not known. The character's name and the Steam id are left out of this page on purpose.

1Where it is, and how to read it safely

Under Proton: steamapps/compatdata/374320/pfx/drive_c/users/steamuser/AppData/Roaming/DarkSoulsIII/<SteamID64 in hex>/DS30000.sl2 (on Windows, %APPDATA%\DarkSoulsIII\<id>\). It is 9,307,472 bytes.

Work on a copy. Our reader only reads: it never writes the save. The game checks each entry's MD5, and characters edited online get soft-banned. The reader lists every entry and each used slot's summary, the inventory and item table with names, and can write each entry out decrypted for a hex editor.

2The container: a BND4 binder data

The same binder format as the game's archives (our archive reader reads it unchanged): a 0x40-byte header, then 12 entry headers of 0x20 bytes each, then UTF-16 names, then the data, packed end to end from 0x300.

EntryStored sizeData sizeWhat
USER_DATA000-0090xC0030 (786,480)0xC0000The ten character slots. An empty slot holds its size and 13 non-zero bytes.
USER_DATA0100x60030 (393,264)0x60000The account: the Steam id, each slot's menu summary, options.
USER_DATA0110x100040 (1,048,640)0x100010The regulation the game last ran with.

Entry headers: flags byte 0x50, id -1 (no ids, unlike the game's archives), u64 size, u32 data offset, u32 name offset.

3Encryption data

entry = MD5[16]  IV[16]  AES-128-CBC(key, IV, plaintext)
        └ MD5 of IV + ciphertext (everything after the hash)

key   = FD 46 4D 69 5E 69 A3 9A 10 E3 19 A7 AC E8 B7 FA     (the same for every DS3 save on PC)

plaintext = u32 size  data[size]  0x0C × 12
            (PKCS7-style padding, but without a minimum block, so it can't be stripped blindly)

The key and the layout come from SoulsFormatsNEXT's Cryptography/SL2Decryptor.cs. That they're right is checked here: all 12 MD5s match, every entry decrypts to its own size field, and the padding is uniform.

4A character slot

Offsets below are into the decrypted entry, counting its leading u32 size.

hdr
item table
?
player
lists …
mostly zero
i32 arrays (?)
0x00x200000x400000x600000x800000xA00000xC0000
headeritem table player / equipment / inventorydense, not decoded sparse or not decoded
OffsetTypeWhat
0x00u32Size of the data: 0xC0000.data
0x04u3298 here. Not identified.open
0x08u32Map: 0x28000000 for m40_00_00_00, area in the top byte. The account summary holds the same value.data
0x0Cu32Play time in milliseconds (374,226): the account summary's 374 seconds × 1000.data
0x1010 × (u32, u32)A section table. Its pairs chain end to end (0x21A30 + 0x6C = 0x21A9C, the next one's start, and so on up to 0x76950), with two sections of 212 KiB and 128 KiB. The offsets don't line up with this file's own layout, so they probably index an unpacked or in-memory image.guess
0x6016 bytesLooks like a hash. It is not an MD5, SHA-1 or SHA-256 of the obvious ranges.open

4.2 The item table (GaItem), 0x70-0xE3E0 data

5120 records of variable size. Each holds a handle (the id the rest of the save refers to) and an item id. An empty record is (0, -1), 8 bytes. Weapons and armour carry per-instance state and take 0x3C bytes:

u32 handle     0x8080_0065   top nibble = kind (8 weapon, 9 armour); then a running counter
i32 item id    0x0141_0B80   = 21040000 Knight Shield; armour ids carry 0x1000_0000 (0x11298BE0 = Knight Helm 19500000)
u32 durability 65            = EquipParamWeapon/Protector durability, checked on every entry
u32 ?          3 / 8 / 0 / 4
u32 ?          1
5 × (u32 0x80000000, u32 0)  empty: the infusion and gem slots, at a guess

Other items have no record here. Goods, for example, are referred to by a handle of 0xB000_0000 | id. The table is the whole world's, not the player's: of the 332 records in use, 10 are the Knight's. The rest are the gear of the armed NPCs loaded in the map (weapons with their upgrade level in the id, e.g. 2230003 = +3, plus their armour sets). Another 1023 empty 8-byte records follow up to 0x103D0. Whether they belong to the same table is open.

4.3 PlayerGameData, at 0x103D0 data

A copy of the game's in-memory PlayerGameData, at the same offsets as in memory up to the name. The offsets come from The Grand Archives' Cheat Engine table for DS3 (the CT table below: the chain WorldChrMan → +0x40 → +0x38 → +0x1FA0). The values check out independently: the attributes are exactly the Knight's starting row, CharaInitParam 3000, and the account summary repeats the level and souls.

+OffsetFieldThis saveChecked against
0x18 / 0x1C / 0x20HP, max HP, base max HP390 / 454 / 454CT table
0x24 / 0x2CFP, max FP93 / 93CT table
0x34 / 0x3Cstamina, max stamina95 / 95CT table
0x44Vigor12CharaInitParam 3000 baseVit
0x48Attunement10baseWil
0x4CEndurance11baseEnd
0x50 / 0x54Strength / Dexterity13 / 12baseStr / baseDex
0x58 / 0x5CIntelligence / Faith9 / 9baseMag / baseFai
0x60Luck7baseLuc
0x6CVitality15baseDurability (the param's name for it)
0x70Soul level9soulLvl; the account summary
0x74 / 0x78Souls / soul memory204 / 204the account summary (souls)
0x88Name, UTF-16, 16 characters + 0(left out)the account summary
0xAAGender (u8)1CT table

The CT table's later fields (equipment at +0x32C onward) are not at the same offsets in the save. From about +0x270 on, the save's layout is its own.

4.4 Equipment, 0x105D0-0x106F7 data

OffsetWhat
0x105D0u32 per slot: 0x80, 0x81, …, the index of the inventory entry each slot holds. These match the low byte of the inventory's order word. guess
0x1064420 × i32 item ids: left 1, right 1, left 2, right 2, left 3, right 3; six more (ammo and two others, all −1 here) guess; head, body, arms, legs (armour ids without the 0x1000_0000 nibble); four rings (−1 = empty). This save: Knight Shield, Long Sword, fists in the other four hands, the Knight set.
0x1069C / 0x106C8The same slots as item-table handles (0x80800065 …, 0x9080006B …).

4.5 Inventory, from 0x106F8 data

u32 ?                       19 here; not the entry count (17)       open
then 16-byte entries until an empty one:
u32 handle                  item-table handle, or 0xB000_0000 | goods id
u32 item id                 with its category nibble: 0 weapon, 1 armour, 2 ring, 4 goods
u32 count
u32 order                   low byte 0x80 + n (n = the acquisition order); high half not identified

This Knight's: Knight Shield, Long Sword, four fists, the four armour pieces, Ashen Estus Flask, Estus Flask ×2, Darksign, Black Separation Crystal, Way of White Circlet, goods 94 (no English name in the item text), and Young White Branch. That's the Knight's starting kit plus what a new game gives at the start. Names come from the game's item text, msg/engus/item_dlc2.msgbnd.

Two more lists, near 0x19024 and 0x190A8, hold goods ids again, the same ones in different orders. They're probably the quick-item belt and the item slots of the radial menu. guess

4.6 The rest of the slot open

5The account entry, USER_DATA010 data

OffsetWhat
0x00u32 size (0x60000)
0x08u64 the Steam account id (the same as the save folder's hex name)
0x10A2Slot 0's menu summary: name (UTF-16), then at 0x10C4 level (9), 0x10C8 play time in seconds (374), 0x10CC souls (204), 0x10D0 map (0x28000000), 0x10D4 4000 (not identified), 0x10D8 −1
0x10DCA FACE block (version 3, 0xF4 bytes): the character's face, as the menu shows it
0x11F4The equipped weapon ids again, with 0x80 in the top byte (21040000 = 0x01410B80 shows as 0x80410B80)

With only one character, the stride between slot summaries isn't known. Further on are what look like option values (floats around 10-11 at 0xC540, perhaps camera or brightness curves). open

6The regulation copy, USER_DATA011 data

u32 size          0x100010
u32 magic         "REG " (a u32, so the bytes read " GER")
u32 2
u32 1350000       the regulation's version (1.35)
DCX               a DCX-compressed BND4 of the 103 params (.param and .stayparam)

All 103 params are byte-for-byte identical to the ones in the game's own Data0.bdt. The save carries the regulation the game last ran with. DS3 takes regulation updates from its servers, and the copy in the save is probably how a character keeps the rules it was played under when offline. guess, the purpose

7What's left, and how to find it

The fastest way to place the event flags and the position is a difference between two saves with one known change between them:

  1. Copy the save. In the game, do one thing: light a bonfire (flag 14000001), or walk ten metres and quit to the menu.
  2. Copy the save again. Then decrypt both and compare the slot byte by byte.
  3. A bonfire lit should flip one bit (the flag), plus the play time and the section table. Ten metres walked should change a float triple (the position).

The same works for souls (pick some up), the inventory order word (pick up an item), and the account's slot stride (make a second character). From the exe's side, DS3's save serializer would name every section. Its RTTI classes (the EventFlagMan family, PlayerGameData) are the way in.

Sources

Not done

Our engine doesn't save: this is research only. Our reader reads a save and never writes one.