-
Notifications
You must be signed in to change notification settings - Fork 0
MK11 Encryption and VFS
MK11 uses DFP encryption for certain game files (inventory databases, config, coalesced). DFP-encrypted files are identified by the 32-byte magic suffix:
mcnxyxcmvmcxyxcmskdldkjshagsdhfj
[Encrypted Data] [DFP Info Block] [Info Size (u32)] [Magic (32B)]
Reading from the tail:
- Last 32 bytes = magic suffix
- 4 bytes before magic = DFP info block size
- Info block =
file_size - info_sizetofile_size - 36 - Encrypted data = bytes 0 to
data_size(from decrypted info)
The info block contains the encryption key and data size, but is itself encrypted with a custom stream cipher. The algorithm (reverse engineered from the game binary) uses:
- Stage 1: Accumulate a hash from bytes at offset 0x19E (count from u16 at 0x188)
- Stage 2: Compute a checksum (r12b) from specific byte positions across the info block using chained add+double+XOR operations
- Stage 3: Process 129 triplets from offset 4 with the same chain operation
-
Stage 4: Six cipher passes decrypt different regions using a CRC-like stream cipher seeded with
0xE3AFEC21:- Pass 1:
buf[0x19E..0x19E+count](key schedule region) - XOR
buf[0x187]with0x9F - Pass 2:
buf[0x18A..0x18E](4 bytes) - Pass 3:
buf[0x00..0x04](4 bytes) - Pass 4:
buf[0x192..0x19A](8 bytes — contains data_size) - Pass 5:
buf[0x19A..0x19E](4 bytes) - Pass 6:
buf[0x04..0x187](387 bytes — contains mode and key)
- Pass 1:
-
Final XOR: Mode-dependent XOR at
buf[5]using the r12b checksum
Each cipher pass uses a rolling state: hash 4 bytes of state → XOR with current byte → store result → rotate state by result & 0x1F → spread byte → add → rotate 1. Counter resets state doubling every 17 iterations (check BEFORE increment).
After info block decryption, mode (byte at offset 4) and key (byte at offset 5) are extracted. Mode 2 uses a rolling XOR cipher:
xor_val = key
for i in range(data_size):
decrypted[i] = encrypted[i] ^ xor_val
xor_val = (encrypted[i] ^ key) - (i & 0xFF) # all & 0xFF| File Type | DFP Output | Next Step |
|---|---|---|
| JSON files | Plaintext JSON | Direct use |
| Coalesced files | AES-encrypted binary | Feed to Coalesced AES decryptor |
| .xxx assets | NRS archive (magic 0x9E2A83C1) | Feed to game pipeline |
| .ini files | AES-encrypted binary | Feed to Coalesced AES decryptor |
mk_utils/formats/dfp.py — pure Python, no native code. Streaming file support via decrypt_dfp_file() which reads only the tail for header info, then decrypts data in 64KB chunks.
MK11 export paths include a "package group" prefix: /Package/, /Audio/, /FX/, /Mesh/, /Texture/, etc. These are bundle type identifiers, not real filesystem paths. The browser strips them.
Each .xxx file has an internal linker name (e.g., CHAR_SUB_SCRIPTASSETS). In UE3 games (IJ2, MKX), this becomes a directory in the browse tree. For MK11, linker paths are stripped — root Package exports attach directly under the game node.
When multiple .xxx files are mounted, MK11's tree merges directories with matching names. This matches IOStore behavior — shared assets (FX, Audio, Materials) that appear in multiple packages are deduplicated.
Clash analysis (4 character files): 1984 cross-file clashes, all identical shared assets. Zero conflicting data. Safe to merge.
For MK11 exports, the right pane shows:
-
Linker: The source
.xxxfile's internal linker name - Bundle: The package group prefix (first path component)
Mounting uses QThread workers. Each file mounts in the background, emitting a signal per completion. The browse tree syncs incrementally — new nodes are added without rebuilding the existing tree, preserving scroll position, selection, and expansion state.
NRS Asset Manager
Architecture
Game Formats
Game Documentation
- Injustice 2 (DCF2)
- Mortal Kombat X (MK10)
- Mortal Kombat 11 (MK11)
- Mortal Kombat 1 (MK12)
Export Handlers
Reference