- 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.
| Entry | Stored size | Data size | What |
|---|---|---|---|
| USER_DATA000-009 | 0xC0030 (786,480) | 0xC0000 | The ten character slots. An empty slot holds its size and 13 non-zero bytes. |
| USER_DATA010 | 0x60030 (393,264) | 0x60000 | The account: the Steam id, each slot's menu summary, options. |
| USER_DATA011 | 0x100040 (1,048,640) | 0x100010 | The 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.
4.1 Header (0x00-0x6F)
| Offset | Type | What | |
|---|---|---|---|
| 0x00 | u32 | Size of the data: 0xC0000. | data |
| 0x04 | u32 | 98 here. Not identified. | open |
| 0x08 | u32 | Map: 0x28000000 for m40_00_00_00, area in the top byte. The account summary holds the same value. | data |
| 0x0C | u32 | Play time in milliseconds (374,226): the account summary's 374 seconds × 1000. | data |
| 0x10 | 10 × (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 |
| 0x60 | 16 bytes | Looks 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.
| +Offset | Field | This save | Checked against |
|---|---|---|---|
| 0x18 / 0x1C / 0x20 | HP, max HP, base max HP | 390 / 454 / 454 | CT table |
| 0x24 / 0x2C | FP, max FP | 93 / 93 | CT table |
| 0x34 / 0x3C | stamina, max stamina | 95 / 95 | CT table |
| 0x44 | Vigor | 12 | CharaInitParam 3000 baseVit |
| 0x48 | Attunement | 10 | baseWil |
| 0x4C | Endurance | 11 | baseEnd |
| 0x50 / 0x54 | Strength / Dexterity | 13 / 12 | baseStr / baseDex |
| 0x58 / 0x5C | Intelligence / Faith | 9 / 9 | baseMag / baseFai |
| 0x60 | Luck | 7 | baseLuc |
| 0x6C | Vitality | 15 | baseDurability (the param's name for it) |
| 0x70 | Soul level | 9 | soulLvl; the account summary |
| 0x74 / 0x78 | Souls / soul memory | 204 / 204 | the account summary (souls) |
| 0x88 | Name, UTF-16, 16 characters + 0 | (left out) | the account summary |
| 0xAA | Gender (u8) | 1 | CT 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
| Offset | What |
|---|---|
| 0x105D0 | u32 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 |
| 0x10644 | 20 × 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 / 0x106C8 | The 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
- 0x10900-0x23000: more lists. Some run in step: u16 pairs counting up (
02 00 00 00 04 00 01 00 06 00 02 00 …at 0x19000, again at 0x21A00), then long runs of(0, 0, −1, 0)records from 0x1A000. - 0x23000-0x7C000: almost all zero in this early save. It could be where flags and world state fill in as the game goes on.
- 0x7C000-0xC0000 (272 KiB): dense arrays of signed 32-bit values in the hundreds of thousands, smooth from one to the next. They might be recorded positions in millimetres (the player's path, kept for bloodstains and ghosts), but nothing confirms it.
- The saved position: no float triple anywhere in the slot (or the account) is a position in m40, and neither is any millimetre-integer triple. The game may keep it packed, relative to something, or in one of the undecoded regions.
- Event flags (the bonfires' lit flags, 14000001; bosses, 14000800; …): not located. DS3 keeps them as bit fields in blocks per area. Without a second save to compare, they can't be told apart from other bits.
5The account entry, USER_DATA010 data
| Offset | What |
|---|---|
| 0x00 | u32 size (0x60000) |
| 0x08 | u64 the Steam account id (the same as the save folder's hex name) |
| 0x10A2 | Slot 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 |
| 0x10DC | A FACE block (version 3, 0xF4 bytes): the character's face, as the menu shows it |
| 0x11F4 | The 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:
- Copy the save. In the game, do one thing: light a bonfire (flag 14000001), or walk ten metres and quit to the menu.
- Copy the save again. Then decrypt both and compare the slot byte by byte.
- 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
- SoulsFormatsNEXT
Utilities/Cryptography/SL2Decryptor.cs: the key and the entry layout (verified). - The Grand Archives' Dark Souls III Cheat Engine table (the CT table): the in-memory PlayerGameData offsets (verified against the params for the stats).
- The game's own params (
CharaInitParam,EquipParamWeapon,EquipParamProtector) and text (item_dlc2.msgbnd) as independent checks. - One save, a level 9 Knight in Cemetery of Ash (m40) with 6 minutes played.
Our engine doesn't save: this is research only. Our reader reads a save and never writes one.