Skip to content

BHD5 and BDT archives

FromSoftware engineArchives and filesApplies toSekiroElden Ring

Almost every file the games load lives in an archive pair: a .bhd index and a .bdt data file of the same name in the game folder. The index lists each file by the hash of its path, with where its bytes sit in the data file, how long it is, and which of its byte ranges are encrypted. File names themselves are not stored anywhere: a path is found by hashing it and looking the hash up. Proven

Reading a file is four steps: unwrap the index with the pair’s RSA public key, find the path’s hash in it, read the listed bytes from the .bdt, and decrypt the listed ranges with the file’s AES key. What comes out is usually a DCX envelope around the real file. Proven

Every entry of both installs reads to the same bytes as the reference readers produce (SoulsFormats for Sekiro, SoulsFormatsNEXT for Elden Ring, both with UXM’s keys and dictionaries), and every index is accounted for byte by byte. Proven · Sekiro, Elden Ring · every entry

Game Pairs Confidence
Sekiro Data1 to Data5 (there is no Data0) Proven · Sekiro
Elden Ring Data0 to Data3; DLC when the expansion is installed; the sound archives sd/sd and sd/sd_dlc02 Proven · Elden Ring

Elden Ring’s list, and which of its pairs may be absent, is the one UXM’s EldenRingGameInfo.xml gives. Sourced · Elden Ring · UXM No path hash appears in two of Sekiro’s pairs, nor in two of Elden Ring’s pairs without the expansion. Proven · Sekiro, Elden Ring · every index

With the expansion installed, the DLC index lists five of Data3’s hashes again, and no other hash appears in two pairs: /chr/c5550.anibnd.dcx, c5550.behbnd.dcx, c5550.chrbnd.dcx, c5550_h.texbnd.dcx and c5550_l.texbnd.dcx. Proven · Elden Ring · every index, the expansion installed The two copies of each hold the same bytes. Each is the same DCX envelope byte for byte up to its end (the 76-byte header and the payload), and each decodes to the same bytes. They differ only in padding: Data3 pads its copies with 6 to 14 zero bytes to a multiple of sixteen; the DLC copies are unpadded. The records of all ten state a logical size of 0. Proven · Elden Ring · all five So whichever pair the game reads them from, it gets the same file. Inferred · Elden Ring

Which pair does the game read a hash from when two pairs list it?

The five repeats on the installed expansion hold the same bytes, so they cannot tell. Whether a later pair overrides an earlier one, or the first listing wins, decides what the game reads only if two listings ever hold different bytes. UXM’s unpacker and Smithbox’s archive file system both walk the pairs in the order Data0 to Data3, DLC, sd/sd, sd/sd_dlc02 and keep the first copy of a path, skipping a later one; that is their choice, not a statement of the game’s.

Sekiro’s five indexes list 8,765 files, of which UXM’s path dictionary names 8,752. Proven · Sekiro Elden Ring’s six installed indexes list 97,118 files (5,878 in Data0, 39,419 in Data1, 39,684 in Data2, 1,672 in Data3, 8,650 in sd/sd and 1,815 in sd/sd_dlc02); hashing every line of three community lists with the game’s rule and intersecting them with the six indexes shows that UXM’s Elden Ring dictionary (revision 9501be8) names 97,031 of them, the newer copy of that list bundled with Smithbox names 97,045, and Smithbox’s own file dictionary (130,951 paths, no two sharing a hash) names all 97,118. Proven · Elden Ring The 87 entries UXM 9501be8 leaves unnamed are 77 .dcx files (54 parts/*.partsbnd, 8 chr/*.texbnd, 4 chr/*.anibnd, 4 chr/*.chrbnd, 3 asset geometry binders, 2 .gparam and 2 .devpatch shader bundles), 5 .bnk and 5 .wem. Proven · Elden Ring The expansion’s DLC index lists 33,755 files, five of them the repeats above. Proven · Elden Ring Data5 holds the map files: its salt is NTC_map and its index lists 5,449 of them. Observed · Sekiro

A .bhd file on disk is a run of 256-byte blocks. Each block is the raw RSA public-key operation of a 255-byte plaintext block (not PKCS#1 encryption: a signature-style envelope opened with the public key), and the plaintexts joined in order are the index. Proven Each archive pair has its own 2048-bit public key. Proven Both games’ public keys are published by UXM (ArchiveKeys.cs). Sourced · UXM

After the index’s own length (the field at offset 12 below), the unwrapped bytes hold fewer than 255 zero bytes of padding from the last block. Proven

The unwrapped index is a header, a table of hash buckets, the file records the buckets point at, and per-file hash and key tables. Every byte of it belongs to one of these. Proven

BHD5 headervariable96% known

Offsets are from the start of the unwrapped index. Integers are little-endian when the flag at 4 is 0xFF.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04char[4] = BHD5magicProven
0x41u8endiannessProven0xFF little-endian, 0 big-endian
0x51u8unknownUnknown0 or 1
0x62u8[2]reservedProvenalways zero
0x84u32 = 1versionProven
0xC4u32index lengthProvenbytes of index; what follows is RSA padding
0x104u32bucket countProven
0x144u32bucket table offsetProven
0x184u32salt lengthProven
0x1Csalt lengthchar[]saltSourcedSoulsFormats' name; nothing in reading depends on it
Bucket8 bytes100% known

The bucket table holds bucket count of these. A path's bucket is its hash modulo the bucket count.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32file countProven
0x44u32file records offsetProvenfile count records of 40 bytes each

A path’s bucket is its hash modulo the bucket count: it holds for every record of every bucket, 8,765 in Sekiro’s five indexes and 97,118 in Elden Ring’s six, so a lookup can read one bucket. Proven[every entry of both installs, Elden Ring without the DLC pair] The bucket counts are 173, 23, 83, 199 and 787 in Sekiro’s Data1 to Data5, and 839, 5,639, 5,669, 239, 1,237 and 263 in Elden Ring’s Data0 to Data3, sd/sd and sd/sd_dlc02. Proven · every index

Both games use a 40-byte record with the same fields, laid out differently: Elden Ring widens the hash to 64 bits and narrows the unpadded size to 32. Proven

File record (Sekiro)40 bytesSekiro100% known

Dark Souls III uses the same layout.

ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x04u32path hashProvenbase 37, see below
0x44u32padded sizeProvenbytes stored in the .bdt
0x88u64data offsetProveninto the .bdt
0x108u64hash table offsetProven0 when the file has none
0x188u64key table offsetProven0 when the file is not encrypted
0x208u64unpadded sizeProvennever more than the padded size
File record (Elden Ring)40 bytesElden Ring100% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x08u64path hashProvenbase 133, see below
0x84u32padded sizeProvenbytes stored in the .bdt
0xC4u32unpadded sizeProvennever more than the padded size
0x108u64data offsetProveninto the .bdt
0x188u64hash table offsetProven0 when the file has none
0x208u64key table offsetProven0 when the file is not encrypted

Within one index no hash appears twice. Proven

The unpadded size is zero for most entries: for 7,851 of Sekiro’s 8,765 and 71,332 of Elden Ring’s 97,118 (every entry of Sekiro’s Data2 and Data4 and of Elden Ring’s Data1 and Data3, none of Elden Ring’s sd pairs). Where it is stored, the padded size is it rounded up to 16 bytes, except for 82 Sekiro entries and 6 Elden Ring ones. Proven[every entry of both installs, Elden Ring without the DLC pair] The padded size is therefore the size to read, and “never more than the padded size” is all the stored value guarantees. Proven

Within a pair the entries tile the .bdt file: ordered by data offset, each starts where the previous one ends, with no gap and no overlap, and the first starts at offset 0. Proven · all 11 pairs A .bdt has no header of its own: its first bytes are its first entry’s bytes (a DCX envelope in Sekiro’s Data1 and Elden Ring’s Data2, ciphertext in the pairs whose first entry is encrypted). Proven · all 11 pairs

A file’s key table gives the AES key and the byte ranges it applies to; its hash table gives a digest and the ranges it covers. Both list ranges the same way. Proven

Range16 bytes100% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x08i64startProven
0x88i64endProvenexclusive; (-1, -1) is a disabled range
Key tablevariable100% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x016u8[16]AES keyProvenAES-128
0x104u32range countProven
0x1416 × range countRange[]rangesProvenascending, not overlapping, multiples of 16
Hash tablevariable11% known
ProvenObservedSourcedInferredUnknownNot described
OffsetSizeTypeFieldConfidenceNotes
0x032u8[32]digestSourceda SHA-256 of the listed ranges per SoulsFormats; not checked
0x204u32range countProven
0x2416 × range countRange[]rangesProvenascending, not overlapping

A file is decrypted by running AES-128 in ECB mode over exactly the ranges of its key table, block by block, and leaving every other byte as stored. Proven Disabled ranges are skipped. Proven Bytes outside the listed ranges are stored in the clear, so decrypting a whole file corrupts it. Proven

A path is normalised before hashing: lower-cased, backslashes turned into forward slashes, and a leading / added if it has none. The hash runs over the path’s UTF-16 code units, starting from zero, with wrapping arithmetic. Proven

Game Hash Confidence
Sekiro h = h × 37 + unit, 32-bit Proven · Sekiro
Elden Ring h = h × 133 + unit, 64-bit Proven · Elden Ring

So /map/m11_01_00_00/m11_01_00_00.msb.dcx is found by hashing that string, never by a stored name. Names come from dictionaries the community has assembled (UXM ships one per game); a hash no dictionary names is a file whose path is not known, not a missing file. Proven Because the hash is lossy, a dictionary can map two paths to one hash; both are then candidates for that file. Inferred

Smithbox’s file dictionaries list, for each path, the archive it is said to be in and a flag that says whether the path was validated. Sekiro’s lists 8,949 paths (no archive named; 8,752 flagged valid and in the index, 197 flagged invalid and not) that name 8,752 of the 8,765 index entries. Proven · Sekiro Elden Ring’s lists 130,951 lines (130,946 distinct paths) and, held to the indexes without the expansion, names all 97,118 index entries; 97,123 of its lines hash into the index (five paths twice). The archive it states is the pair that holds the path for all but five paths (stated dlc, in Data3), but the flag is not exact: 10,467 paths flagged invalid are in the index, and 33,750 flagged valid are in none; 33,757 of the lines state the archive dlc, which those indexes do not list. Proven · Elden Ring The five paths stated dlc but held in Data3 are the five the expansion’s index lists again. Proven · Elden Ring

What is the byte at offset 5 of the index header?

It is 0 or 1. Neither reference reader gives it a meaning, and reading does not depend on it.