Repository navigation
Research Report Dlc4 Cfile
Route cfileindlc4, 2026-09-23. New: tools/build_dlc4_with_cfile.py, re_probes/dlc4_cfile/chunk_22.resources. Nothing installed, the game was not started, nothing written under <Steam library> or Saved Games (only read there).
The container is built and passes 21/21 checks. However, the idea behind it does not hold: The Exe selects the DLC directory via the map name, not via the chunk ID. When loading the retail game state game/dlc/c02/c2v1, it mounts ../dlc//dlc_2/base/chunk_22.resources. This also applies with today's dlc_4 spec (18:35), which lists c2v1 as well, because dlc_2 appears before dlc_4 in the DLC list. Our chunk_22 is only mounted when loading c2v1_custom. This finding stems from static analysis of the running Exe and was not measured within the game itself.
| Source |
re_probes/entitypipe/base/chunk_22.resources, 7128 entries, sha256 a8da9e4e… (= installed dlc_4) |
| Target |
re_probes/dlc4_cfile/chunk_22.resources, 7129 entries, 151 703 648 bytes, sha256 cb3d8bac6fa574173212cc681f75c3043d1a8bc7d925497c4db93f493c807f03
|
| New Entry | Last table position, cfile:wolfsdk_test.cfg, comp 0, csize = usize = 96, 0 dependencies, payload byte-identical to re_probes/cfilekey/wolfsdk_test.cfg.cfile
|
Nothing new was written for the build. It is appended using tools/build_tnc_map_alias_v3.write_archive, the function that previously attached 143 alias entries to this container. The line template is the entry from the exe carrier. Afterwards, wolfsdk/idclwrite.finalize runs. Nothing needed recalculation (data_hash 0, hash_0x60 0 changed), so the hashes of write_archive were already correct. The string table grows at the back by cfile and wolfsdk_test.cfg, and the string index pool by 2 entries. All old indices remain valid; therefore, nothing changes in any old entry except the offset.
Running python tools/build_dlc4_with_cfile.py --pruefen yields 21/21:
- 7129 entries on re-read with
wolfsdk/idcl.py. - 7128 old table lines are byte-identical except for
+0x38(offset). The check compared all 144 bytes, including+0x50/+0x60, versions, flags, and run indices. - 7128 payloads are byte-identical. Dependency records (1943) and Dep-index pool are byte-identical. The old string index pool, the old 8821 strings, and their raw bytes are each prefixes of the new ones.
- No payload overlaps another, and the last one ends at the file end. The 5 empty
cmentries share an offset like in Retail. My first, stricter check for unique offsets therefore failed and was corrected. - The new cfile decrypts from the built container to
b'map game/dlc/c02/c2v1_custom\n'. Withbase/names or 16 appended filler bytes, the tag fails. -
probe_datahash:+0x50 7128/7128(1 entry with compression mode 4 skipped, same as in source), rc 0.probe_idclhash:+0x08 1/1 +0x20 1/1, rc 0.
The control runs with --negativ on a disposable copy in the Temp directory. There, a ciphertext byte and the header byte 0x18 are flipped. Afterwards, probe_datahash, probe_idclhash, and pruefe() must fail. Additionally, zeilen_diff must see changes at +0x50, string_index, and compression, and report nothing on the offset. See §4 for the result.
The check runs with python tools/build_dlc4_with_cfile.py --vorrang (35/35). It checks 29 instructions and 4 strings at fixed RVAs in re_probes/runtime_87400/module.bin. The control run with addresses shifted by about 1 byte hits none of them.
-
LoadNamedResourceBlock (
0xac1730):r13holds the map name, populated by the format argument ofchunk missing in LoadNamedResourceBlock for map %s. The execution flow is as follows:-
0xabf440(MapName)retrieves the Spec entry; the chunk ID resides at[r15+0x30]. -
0xabf500(MapName)retrieves the directory prefix. -
0xabd940(chunkID)constructschunk_22.resourceswithout a directory path. -
0x1211e70combines the prefix and name, then mounts at0xacc6b0.
The same logic applies to
.texdb. The RefChunks (chunk_9,chunk_13) andchunkbase_6are mounted without a prefix frombase/. -
-
0xabf500and0xabf440: Both first search inbase/packagemapspec.json(this+8), then in the DLC list (this+0x30, 160 bytes per entry, MapSpec at+0x138). The first match wins (jneexits the loop). The prefix is"%s/%s" % (DLC-Path, "base/"). The chunk ID is never compared across DLCs. Therefore, the question "dlc_2 or dlc_4 with the same chunk ID 22" is not posed to the engine at all. -
Order of the DLC List: SearchDLC (
0xabdcf0) skips an already existing dlcid (the first wins), otherwise it hangs (0xabe2c6) and logs immediately afterfound dlc id %d at %s(0xabe503). Thus, the log order corresponds to the list order. Measured inqconsole.loadsave.log:dlc_1, dlc_2, dlc_3, dlc_4. -
Simulated against installed Specs (read-only):
c2v1 -> ../dlc//dlc_2/base/chunk_22.resources,c2v1_custom -> ../dlc//dlc_4/base/chunk_22.resources. -
Consistent with today's measurement at 18:27: The startup routine
0xabdaa0mounts onlygameresourcesand Patches. DLC Chunks come exclusively via LoadNamedResourceBlock. Therefore,resourceExecfinds nothing in the main menu. -
packagechunklist.jsonis read at0xb76330(chunks/chunkid/packagechunks). This lies in the neighborhood of idStreamingInstallManager, so the file is UNLOCKED as a Streaming-Install Manifest and not a Mount List. The warning is irrelevant for this question.
Summary: dlc_primary.md and map_selection.md held precedence for equal names as "statically unproven". For the directory path, it is now statically proven: first match in order base, then DLC list. Only what a runtime measurement would show remains open. A contradiction in the game would imply this interpretation is false.
-
The planned path (Loading Retail-Save c2v1, then
resourceExec wolfsdk_test.cfg) again predictscan't generate in productionbecausedlc_2/chunk_22is mounted. This is a cheap falsification test of this analysis. -
Our container is mounted when
c2v1_customloads. Then the cfile is superfluous because one is already in the map. It only proves that a custom cfile in the game is resolvable. -
Leverage without Retail write access (not tested): Rename the folder
dlc/dlc_4todlc/dlc_0. The dlcid 4 remains, and the Spec already listsc2v1. If the directory enumeration is alphabetical (the measured order 1–4 fits this, but it has not been proven), thendlc_0precedesdlc_2. Then for the Retail-Save, ourchunk_22will be mounted. It contains all 6985 c2v1 retail payloads byte-identically. Risks:- The same prefix goes to a virtual call at
0xac1a47..0xac1a8d. It is SUSPECTED that this is the DLC Sound Mount, anddlc_4has nosound/folder. - An entitlement check for DLC 2 for the Save cannot be ruled out.
Log evidence that the lever works:
found dlc id 4 at ../dlc//dlc_0appears beforefound dlc id 1. - The same prefix goes to a virtual call at
7/7, rc 0:
| Manipulation | Result |
|---|---|
zeilen_diff: one bit in +0x50, string_index, and compression
|
all three detected |
zeilen_diff: a bit in the offset |
ignored, as intended |
Ciphertext byte and header byte 0x18 flipped (copy) |
probe_datahash 7127/7128 rc 1, probe_idclhash +0x08 0/1 rc 1 |
pruefe() on the same copy |
17/21: Payload, Decryption (HMAC-Tag incorrect), and both checks fail |
Limitation: The equality checks of the old entries were only tested against manipulation (zeilen_diff) in memory, not with a second damaged 150-MB copy.
python tools/build_dlc4_with_cfile.py # build + 21 checks (~2.5 min)
python tools/build_dlc4_with_cfile.py --pruefen # check only
python tools/build_dlc4_with_cfile.py --negativ # negative controls, copy in temp directory
python tools/build_dlc4_with_cfile.py --vorrang # Mount rule on module.bin, 35 checks
English Wiki generated from the measured research notes in docs/. Evidence grades and uncertainty are preserved intentionally.
- WolfSDK Loader · DE
- WolfSDK Studio · DE
- FAQ · DE
- Mod format · DE
- Modding Guide
- Modding Surface
- CVar Catalog
- CVar Validation (old scrape)
- Texture Database
- Developer Mode
- Dev Menu
- Custom-Map Status