Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / The New ColossusThe New Colossus

Can a DLC Chunk overwrite an asset from the base game?

Partly solvedTeilweise gelöst Original page in the GitHub wikiOriginalseite im GitHub-Wiki

In plain wordsIn einfachen Worten

In Wolfenstein II: The New Colossus, the team checked whether a DLC chunk can overwrite an asset from the base game. If so, a mod could be a folder you add and delete, without touching the installation. The shipped DLCs never replace anything: 7,733 assets appear twice, but every copy is identical. A later game test showed a self-built DLC is found, but an extra dependency chunk does not win over the original map data. Using a DLC chunk as the primary source is still blocked by an unknown checksum in the file header.

  • 7,733 assets exist in both a DLC chunk and a base archive, and all 7,733 are bit-identical.
  • Game test: a self-built dlc_4 was detected (found 4 dlcs); the ownership check did not block it.
  • An added dependency chunk did not override the original map data; the primary chunk won.
  • One header check value (at +0x08) is still unknown, which blocks the remaining test.
  • The New Order has no comparable DLC map list, so this path is only realistic for The New Colossus.

Für Wolfenstein II: The New Colossus wurde geprüft, ob ein DLC-Chunk ein Asset aus dem Hauptspiel überschreiben kann. Dann wäre ein Mod ein Ordner, den du hinzufügst und wieder löschst, ohne die Installation anzufassen. Die offiziellen DLCs ersetzen nie etwas: 7.733 Assets liegen doppelt vor, aber jede Kopie ist identisch. Ein späterer Spieltest zeigte, dass ein selbst gebautes DLC erkannt wird, ein zusätzlicher Abhängigkeits-Chunk aber nicht gegen die Originaldaten gewinnt. Ein DLC-Chunk als Hauptquelle scheitert noch an einer unbekannten Prüfsumme im Header.

  • 7.733 Assets liegen sowohl in einem DLC-Chunk als auch im Hauptarchiv, und alle 7.733 sind bitgleich.
  • Spieltest: Ein selbst gebautes dlc_4 wurde erkannt (found 4 dlcs); die Besitzprüfung hat es nicht blockiert.
  • Ein zusätzlicher Abhängigkeits-Chunk hat die Originaldaten der Karte nicht überschrieben; der Haupt-Chunk hat gewonnen.
  • Ein Prüfwert im Header (bei +0x08) ist noch unbekannt und blockiert den letzten Test.
  • The New Order hat keine vergleichbare DLC-Kartenliste; der Weg ist nur für The New Colossus realistisch.

Technical detailsTechnische Details

Der technische Teil ist auf Englisch – so, wie er im Wiki steht. Unterstrichene Begriffe haben eine Erklärung auf Deutsch.

Wolfenstein II ships with three released DLCs. Each registers via its own packagemapspec.json and brings its own chunk_<id>.resources. If such a chunk can overwrite an existing asset, then the entire patch machinery for Part 2 becomes optional: a mod would then be a folder you drop in and take out — without backup, without journaling, without rollback, without a single write operation to the 40-GB installation.

All of this is lifted read-only from the unmodified Retail installation at <Steam library>\steamapps\common\Wolfenstein.II.The.New.Colossus and …\Wolfenstein.The.New.Order. Reproducible with:

python tools/dlcmod.py messung      # the decisive measurement
python tools/dlcmod.py felder       # packagemapspec fields, with prediction tests
python tools/dlcmod.py selbsttest   # the archiver builder
python tools/dlcmod.py bauen --ziel <folder outside the Steam library>

Grades like those in docs/patchvorrang.md: MEASURED (read from files), DEDUCED (inferred from structure), SPECULATED (plausible, not secured).


0. Answer with Evidence Grade

Short: Not determinable from the files — and for a reason that is itself an outcome: the three released DLCs do not overwrite a single asset. They double. In 7,733 instances, the same (Type,Name) pair exists in both a DLC chunk and a base archive, and in 7,733 instances the content is bit-identical. There is therefore no released precedent case from which to read the precedence rule.

What the 7,733 instances do show: a name collision between a DLC chunk and the base archive is in this engine not an error case, but everyday occurrence. The engine has a resolution rule — otherwise it could not load the three DLCs. Only open is how that rule reads.

# Finding Grade
1 7,733 (Type,Name) overlaps between DLC chunk ↔ base archive, 0 with differing data_hash MEASURED
2 The engine keeps these 7,733 collisions out of the Retail game. A collision is not an error. MEASURED
3 Exactly one pair in the entire installation where a DLC chunk and a Patch container carry different content: perks:perk/player/sp/grenadier_fc_base MEASURED
4 refchunks literally means "dependency chunks" in the Exe: an registered refchunk is not doubled in the DLC chunk (15 remaining cases), a non-registered one is (1,495 cases) MEASURED
5 gameresources and chunkbase_6 are not meaningfully doubled by any DLC (11 vs. 0 cases respectively) — they are always mounted MEASURED
6 layer is not f(basechunkid): basechunkid 6 carries layer 1 in the base game, layer −1 in the DLC. The 1:1 coverage from Patch-Precedence.md applies only within base/. MEASURED, correcting a previous statement
7 The Exe knows an entitlement check for DLCs (idEntitlements::Get DLC %d not installed) with exactly three named keys ENTITLEMENT_DLC1..3 — contrary to docs/ladepfade.md, which notes "no visible ownership check" for Part 2 MEASURED, correcting a previous statement
8 The DLC virtual textures overwrite nothing: they land in their own quadrant (vt_useDLCVmtrPageFiles) MEASURED
9 There can always be only one DLC sound container mounted simultaneously (MountDlc: another dlc already mounted) MEASURED
10 Part 1 has no packagemapspec.json equivalent. Instead, master.index — a pure file list without map names, layers, or refchunks. MEASURED

Runtime Addendum, September 22, 2026

The controlled gameplay test now yields a narrower, more reliable answer than the pure file analysis:

  • A newly built dlc_4 is detected: found dlc id 4 at ../dlc//dlc_4, SearchDLC: found 4 dlcs. The ownership check does not block Discovery.
  • resource_dlcDbgSearchPath cannot be set in this Retail build; the log reports Unknown command 'resource_dlcDbgSearchPath'.
  • A refchunks: [26] for c02p0 actually requests chunk_26. However, a deliberately different but valid card Canary (c02p0.entities, ...100000 → ...900000) was not used: the log still shows ...100000. The primary chunk_1 therefore wins against an additionally mounted Refchunk. MEASURED.
  • A freshly built IDCL container is strictly validated before loading. It requires the second IDCL directly following the metadata pools and a correct FarmHash64 at Header +0x20. Both are now present in the builder.
  • Header +0x08 is also actively checked; its algorithm remains undisclosed. The engine reports the calculated 64-bit value in the log upon deviation, thereby creating a targeted oracle, but no general formula exists yet.

Current Conclusion: An additional Refchunk is not an override path. The potential path via a DLC chunk selected as primary remains verifiable only when Header +0x08 can be calculated universally. All test files have been removed from the game folder and the original packagemapspec.json has been byte-for-byte restored.


1. The Decisive Measurement

python tools/dlcmod.py messung. All 24 base archives (178,077 entries, 167,694 unique pairs) against the three DLC chunks.

DLC / Chunk Entries Also in Base Content Same Content Different
dlc_1/chunk_20.resources 13,366 2,422 2,422 0
dlc_2/chunk_22.resources 6,985 1,326 1,326 0
dlc_3/chunk_24.resources 12,754 3,985 3,985 0
Sum 33,105 7,733 7,733 0

MEASURED. The statement is complete; no sample: every single one of the 33,105 DLC entries was checked against the base index.

data_hash is content-dependent — the cross-check in tools/dlcmod.py unpacks 60 overlapping pairs from dlc_1/chunk_20 and the respective base archive and compares them byte-by-byte: 60 of 60 bit-identical. Thus, "same data_hash" here truly means "same content" rather than just "same length". MEASURED.

The Types of Overlap

The 7,733 are not an anomaly but precisely what a level needs:

cm 3119   image 1837   model 1395   material 591   anim 215   sound 118
entityDef 56   decalatlas 56   perks 74   particle 50   md6Def 30   …

Collision models, images, models, materials — geometry and surfaces appearing in multiple chapters. A DLC chunk is a self-sustaining copy of everything its maps need and what isn't mounted anyway.

Why This Does Not Answer the Question — And What It Is Still Worth

From 7,733 content-identical collisions it does not follow that the engine prefers a copy when content differs, nor that it does not. However, three things follow, and this is no comfort but substance:

  1. A collision is not an error case. Anyone fearing that a self-built DLC chunk with an already existing name could drive the engine into resource '%s' of type '%s' is orphaned or container entry defaulted: this happens in the Retail game 7,733 times per DLC map. MEASURED.
  2. The resolution rule exists and runs. Every time someone plays Gunslinger Joe, the engine decides between two containers 2,422 times. UNLOCKED.
  3. The attempt is cheap. It requires no rebuilding of a base archive and no backup — just a folder that can be deleted again.

2. The One Case Where Two Containers Conflict

Beyond perks 7 and 733, there is exactly one pair where a DLC chunk differs in content from another provided container — not from a base archive, but from a patch layer:

perks : perk/player/sp/grenadier_fc_base
   chunk_16              263 B   2017-11-03   hash 189cd9c94538365b
   dlc_1/chunk_20        263 B   2017-12-07   hash 189cd9c94538365b   <- identical to chunk_16
   patch_3_chunkbase_6   322 B   2018-02-24   hash ca35f0735b7b555b   <- divergent

The difference, unpacked and diffed (MEASURED):

        regionUnlockName = "#str_perk_grenadier_fc_tracking";
+       activateOnPerkItem = true;
+       item = "player/perkitem";

The timeline explains it completely. dlc_1 was built on 2017‑12‑07 — which is bit-exact the date of the most recent entry in patch_2. Thus, it took on the content valid at that time. patch_3 (2018‑02‑24) added two fields to the perk after that. And dlc_1 reports patchlevel 2.

RESOLVED, by backtracking across all three DLCs:

DLC patchlevel most recent entry in Chunk most recent entry in Patch Layer
dlc_1 2 2017‑12‑07 patch_2: 2017‑12‑07
dlc_2 3 2018-01-25 — (between patch_2 and patch_3)
dlc_3 3 2018-02-24 patch_3: 2018-02-24

Thus, patchlevel in a DLC spec is the search upper bound for patch layers when loading a map from this spec — the same meaning as in base/packagemapspec.json (docs/patchvorrang.md, Finding 7), but applied card-locally. Under this interpretation, the contradiction is none: patch_3_chunkbase_6 is not sought at all for a dlc_1 map, and thus dlc_1 brings the version fitting patchlevel 2.

The flip side is important for us: a self-built dlc_4, which declares a Base Map but reports an insufficiently low patchlevel, would strip its patch layers from that map. Therefore, both provided specs list patchlevel: 4 — the same value as in base/.

(CONJECTURED whether patchlevel applies per spec or globally takes the maximum. patchlevel: 4 is harmless under either interpretation.)


3. The packagemapspec Fields — with Prediction Tests

python tools/dlcmod.py fields. 41 map entries: 31 from base/, 10 from the three DLCs. basechunkid, chunkid, isbasechunk, layer, name appear in all 41; refchunks only in 7 (4 × dlc_1, 3 × dlc_2).

Field Meaning Status
name Map name, as loaded by the game MEASURED
chunkid In which chunk_<id>.resources/.texdb the map lies; 0 = gameresources MEASURED
basechunkid Common base archive: 0 = gameresources, 6 = chunkbase_6 MEASURED
isbasechunk Only common and init, both chunkid 0 MEASURED
refchunks Dependency Chunks: in addition to mounted chunks MEASURED, see 3.1
layer Load layer. See 3.2 — the previous interpretation was too narrow. PARTIALLY REFUTED
patchinfo.patchlevel Search upper bound for patch_N layers RESOLVED, see 2

3.1 refchunks — the prediction that holds

The exe names it. 0x1426CF933, MEASURED:

idResourceManagerLocal::GetIsChunkForMapInstalled: dlc maps are considdered
installed for now %s, but checking dependency chunks

From this, the verifiable prediction: a chunk registered as refchunks will be mounted — thus the DLC chunk does not need its own copy of its assets. A chunk not registered must be duplicated.

Tested on the three chunks from which all three DLCs draw (MEASURED):

chunk_9 chunk_12 chunk_13 registered
dlc_1 10 4 0 9, 12, 13
dlc_2 0 127 1 9, 13
dlc_3 692 459 217 none

Six cells, six hits. Logged in → 0–10 copies. Not logged in → 127 to 692. dlc_2 is the sharpest case: the same DLC doubles from chunk_12 twelve times and exactly once from the two logged-in chunk_9/chunk_13.

Overall picture across all three DLCs: 15 copies from logged-in refchunks, 1,495 from non-logged-in.

The 15 remaining cases are explained, not dismissed. All 14 from dlc_1 are image entries with $streamed in the name:

image models/food/textures/icecream_01.tga$mtlkind=albedo$streamed
image models/modules/neworleans/doors/textures/doorknob_01.tga$…$streamed

$streamed images do not carry their payload data in the archive, but in streamed.resources (docs/streamed.md); the archive entry is a stub. SPECULATED: such a stub must reside in the container from which the map loads, and is therefore excluded from refchunk savings.

3.2 layer — the previous interpretation was too narrow

docs/patchvorrang.md notes: "layer matches basechunkid 1:1, without exception across all 31 maps." Across all 41 entries this is no longer true (MEASURED):

layer=-1  basechunkid=6   dlc    10 entries
layer= 0  basechunkid=0   base    9 entries
layer= 1  basechunkid=6   base   22 entries

basechunkid 6 carries two different layer values. The 1:1 match applies only within base/; layer −1 is a separate value led exclusively by DLC maps.

What layer is remains open. The Exe has the Cvar resource_ignoreLayers with the description "ignore layer bits and load all layers during map loads" (docs/patchvorrang.md, Section 1) — Layer Bits, i.e., a bitmask. SPECULATED: −1 is the mask "all bits set" and means "no layer restriction", which fits a DLC that is not part of the base game's chapter streaming. Not proven.

3.3 What is mounted without being logged in

The doubling numbers say it directly (MEASURED):

Target Archive Total Duplications from all three DLCs combined
gameresources 11
chunkbase_6 0
logged-in refchunks 15
present only in patch_* 4
non-logged-in chunk_* 7,703
Sum 7,733

ELUCIDATED: gameresources and chunkbase_6 are mounted for every map, so no one duplicates them. This is the decisive number for a mod: the seven table:playerdamage/* entries lie exclusively in gameresources — i.e., in exactly the container every map brings along that no DLC has ever duplicated. Whether a DLC chunk can override it is thus the ultimate untested case.


4. What the Exe Says About the DLC Path

NewColossus_x64vk.exe, 58,062,032 bytes, .text SteamStub encrypted. Readable is .rdata. All addresses MEASURED, Virtual Addresses:

The SearchDLC Block

0x1426CF690  resource_dlcDbgSearchPath
0x1426CF6E0  %s/dlcinfo.json
0x1426CF730  %s/base
0x1426CF738  SearchDLC: Could not load mapspec for dlc id %d from %s
0x1426CF770  SearchDLC: could not read existing dlc info file: %s
0x1426CF7A8  dlcid
0x1426CFAF0  SearchDLC: found %d dlcs
0x1426CFB30  found dlc id %d at %s          <- new, the success line per DLC
0x1426CFB48  ../dlc/

found dlc id %d at %s has previously appeared in no document. This is the line where one sees in the log per DLC that it was recognized, with ID and path — more precisely than the summary line found %d dlcs.

No ownership check exists in this literal block. MEASURED.

The Installation Check

0x1426CF8E3  GetIsChunkForMapInstalled: can't find chunk for map %s
0x1426CF933  GetIsChunkForMapInstalled: dlc maps are considdered installed
             for now %s, but checking dependency chunks
0x1426CF9C3  GetIsChunkForMapInstalled: chunk %d for map %s is not installed
0x1426CFA43  GetIsChunkForMapInstalled: chunk %d is a dlc and base chunk %d
             is not in fast storage for map %s

MEASURED. The engine explicitly tracks that a chunk is "a dlc" and checks the associated basechunkid. The message can't find chunk for map %s appears when a map name lacks a Spec entry — this is the first error that would trigger an incorrectly constructed dlc_4.

The Ownership Check That Exists for Part 2

docs/ladepfade.md states that the TNC path contains "no visible ownership check." This holds true for the SearchDLC block. However, elsewhere in the executable, it is clearly present (MEASURED):

0x1426DBD10  idEntitlements::Get DLC %d not installed
             idEntitlements::Get DLC %d installed
             idEntitlements::Get entitlements are empty
             New DLC installed
             .?AUCCallbackInternal_OnDLCInstalled@idEntitlements@@

and, with exactly three named identifiers:

GDM_CONNECTION_TO_HOST_DENIED_MISSING_ENTITLEMENT_DLC1 / _DLC2 / _DLC3
GDM_KICKED_MISSING_ENTITLEMENT_DLC1 / _DLC2 / _DLC3
GDM_DLC_ERROR_REMOVED / _MISSING_GENERIC / _CORRUPT
SAVEGAME_E_DLC_NOT_FOUND
idDeclChapter::DLC_CHAPTER_PRESENCE_00 / _01 / _02 / _03

ENTITLEMENT_DLC exists only with the digits 1, 2, and 3 — MEASURED, across the entire executable. DLC_CHAPTER_PRESENCE has four slots, from 00 to 03.

UNCOVERED: There is an entitlement layer, and it is hardwired for three entitlements. Whether SearchDLC queries it during mount is recorded in .text but is not measurable. This is the one risk that cannot be seen with a dlcid: 4 — and the reason why the guide below searches the log precisely for this.

Sound and Virtual Textures

0x14268C377  MountDlc: Could not mount dlc sound containers for %s
0x14268C587  MountDlc: another dlc already mounted (%s) while mounting %s
             MountDlc: dlc %s already mounted
             MountDlc: mounting %s
             UnmountDlc: unmounting %s

MEASURED. There can only ever be one DLC sound container mounted simultaneously. This matches the file layout: each DLC has a complete, independent base/sound/soundbanks/pc/ with the same filenames as the base game (sound.pack, english(us).pack, prodspec.json, wwise_project.bin) and its own prodspec.json with patchlevel: 0 containing only the banks for its own maps. UNCOVERED: The DLC sound path replaces the base game's path for the duration of the DLC map — this is the only measured location where a DLC actually displaces a base game resource, and it does so via the file path, not the resource container.

For virtual textures, it is reversed. 0x142679478/0x1426794B8, two strings immediately adjacent, MEASURED:

"Loads the dlc vmtr list for storing pages in the dlc quadrant."
vt_useDLCVmtrPageFiles

(The mapping of Description↔Name here is unusually secure: in the same block directly before stands "Loads the intro vmtr list for storing pages in the first quadrant." / vt_useIntroVmtrPageFiles, and directly after are two further pairs in the same order, description then name.)

UNCOVERED: dlc_1/virtualtextures/maps/game/dlc/c01/*.pages is not a replacement for the base game's virtualtextures/, but rather an additional page source in its own quadrant of the physical page buffer. A DLC does not overwrite anything here either. For a dlc_4 that brings no new maps, the folder is simply superfluous — which is why the built package omits it.


5. Part 1 — no packagemapspec.json, but master.index

MEASURED, WolfNewOrder_x64.exe and …\Wolfenstein.The.New.Order\base\:

  • The strings packagemapspec and mapspec appear 0 times in the executable.
  • There is no packagemapspec.json on disk.
  • Instead: master.index, 520 bytes, Magic \x03SER, followed by a u32 (big endian) 14 and 28 length-prefixed names:
chunk0.index  chunk0.resources  chunk1.index  chunk1.resources  …
chunk13.index chunk13.resources

This is a pure file list — no card names, no layer, no refchunks, no patchlevel. It includes gamechunktoinstallchunk.gic (678 bytes, Magic \x02CIG), which maps game chunks to install chunks.

OPEN: Part 1 has no equivalent for packagemapspec.json. The map-to-chunk assignment is not in a plaintext file, but in the .index files and the .gic. Therefore, a Part 1 DLC cannot "register" anything — it can only carry packages that the engine opens via OpenDLCFileHandles.

The hurdle remains that from docs/ladepfade.md E7: info.txt with appID and dlcID, and the message User does not own this DLC. There, ownership verification is explicitly stated in the literal block of the Mount function — whereas in Part 2 it resides in a different translation unit (idEntitlements).

Conclusion for Part 1: The DLC path there is not cheaper than in Part 2, but rather more expensive — more guessing involved (the info.txt dialect, four versions in tools/dlcinfo.py), an additional verified ownership check, and no spec format through which something could be registered. Part 2 is the path that should be taken.


6. The built package dlc_4

python tools/dlcmod.py build --target <folder>. This tool refuses to write into the Steam library; placing it there is a manual step for the user.

dlc_4/dlcinfo.json                 19 Byte   { "dlcid" : 4 }
dlc_4/base/packagemapspec.json    233 Byte
dlc_4/base/chunk_26.resources   90 172 Byte   7 entries, all uncompressed
dlc_4/base/chunk_26.texdb          32 Byte   empty
stufe_b/packagemapspec.json                   Exchange file, see 7
stufe_c/packagemapspec.json                   Exchange file, see 7

Content: the seven table:playerdamage/* from mods/tnc_erste — the multiplier for incoming player damage, set to 0 in all seven difficulty tables. Nothing new invented; the tool reads mods/tnc_erste/mod.json and applies the same wolfsdk.decl.apply_patch rule as the regular patch path.

{ spline clamp min 0.5 max 0.5 … }   ->   { spline clamp min 0.0 max 0.0 … }

All seven replacements are byte-identical — csize and usize remain as they were. The tool aborts if a replacement changes the length.

The selected identifiers

dlcid: 4. Released are 1, 2, 3 (dlc_1/2/3), so 4 is the first free one. It collides with neither an existing folder nor any identifier provided by the game. The known risk: the entitlement layer knows only ENTITLEMENT_DLC1..3 (Section 4). If SearchDLC queries it, dlcid 4 fails — and exactly that appears in the log (idEntitlements::Get DLC 4 not installed). The attempt thus answers the question even when it fails.

The alternative would be to use an occupied identifier (1, 2, or 3) — but this would mean competing with a released DLC for the same identifier (found dlc id %d at %s suggests a mapping from identifier to path). Try only after dlcid 4 fails at entitlement, and then with the knowledge that the path is no longer interference-free.

chunkid: 26. Occupied are 0 (gameresources), 1, 3, 4, 5, 6 (chunkbase_6), 7–16, and the DLC chunks 20, 22, 24. 26 continues the two-step sequence of DLCs and leaves 21/23/25 free, should the engine expect partner files there.

What the tool does not compute

Three fields tools/dlcmod.py cannot calculate, and this is also stated in the source text:

  1. data_hash and hash_0x60 of the entries. The algorithm is unknown (wolfsdk/idcl.py: neither CRC32/64, Adler32, FNV-1a, Murmur, xxHash64, MD5, SHA-1, FarmHash Fingerprint64 nor CityHash64 over stored or unpacked bytes). tools/probe_tnc_hash.py additionally checked Type/Path combinations and both halves of CityHash128: 2,000 uncompressed Retail entries, 0 hits on both fields, including byte order. The EXE contains the CityHash constants k1/k0 at file offset 0x25C8330/0x25C8338; nevertheless, it is demonstrably not using the direct standard form. Therefore, the built entries carry the hashes of the originals — that is, hashes that do not match the new content. This is precisely the same state as with the regular In-Place path, which loaded flawlessly in Part 1 with 373 patched assets (verified). Unverified for Part 2. If resourceStorage_checkDataCheckSum triggers, the log shows container entry defaulted (0x1426CDDF8).
  2. Header hash at +0x08. This is actively checked upon mounting and causes container ... has corrupt header on deviation. The algorithm remains open; however, the Engine outputs the calculated value in the log. +0x20 is resolved: FarmHash64 from the entry table (0x78) up to and including the second IDCL marker.
  3. No dependencies. chunk_26 reports num_dependencies = 0. No released archive does this (MEASURED, all 24 have some), but all seven entries have num_deps = 0 in gameresources themselves, so an empty dependency table is structurally sound. Unrecorded.

The self-test (python tools/dlcmod.py selbsttest) checks what is verifiable: the builder writes an archive that wolfsdk.idcl.Archive — the same parser that reads the 24 Retail archives — fully re-reads, with correct types, names, payload data, and shared pool strings; and the replacement remains length-neutral.


7. The Guide

First, once: Enable log file. Part 2 otherwise writes no qconsole.log (docs/ladepfade.md). Start parameters:

+logFile 1 +logFilePersistent 1

The log lands under %USERPROFILE%\Saved Games\MachineGames\Wolfenstein II The New Colossus\base\qconsole.log.

Retrieve baseline value first: start without dlc_4 and search for SearchDLC: found 3 dlcs in the log. If a different number appears, the initial state is incorrect and everything else is uninterpretable.

Stage 0 — no file at all

+resource_dlcDbgSearchPath "C:/wolfdlc"

If the log subsequently shows SearchDLC: found 0 dlcs instead of 3, the Cvar has indeed moved the search path — and the entire attempt can occur on C:\wolfdlc\ without a single file in the Steam folder. If this works, use this path for everything that follows and read <SearchPath> below instead of …\Wolfenstein.II.The.New.Colossus\dlc.

Stage A — place it, do not register (safe)

Copy dlc_4/ unchanged to <SearchPath>. The included packagemapspec.json reports a custom, non-existent map (game/dlc/c04/c4v1) and collides with nothing.

What Stage A answers:

  1. Is a self-built DLC ever found and its Spec accepted?
  2. Are DLC chunks mounted globally? If yes, the invulnerability works immediately and everywhere — and then the entire question is answered without ever touching a base map.
In Log Meaning
SearchDLC: found 4 dlcs Found. The spec was accepted.
found dlc id 4 at <path> ID and path confirmed.
SearchDLC: found 3 dlcs (unchanged) The folder was not seen. Wrong location, or dlcinfo.json is unreadable.
SearchDLC: could not read existing dlc info file: <path> dlcinfo.json found, but unreadable/unparseable.
SearchDLC: Could not load mapspec for dlc id 4 from <path> Folder OK, packagemapspec.json rejected. Tinker the JSON, not the folder.
idEntitlements::Get DLC 4 not installed The entitlement layer is active. dlcid 4 is not possessed. This is a valid result: the interference-free path then leads nowhere.

Then start any mission and take a hit:

  • No damage → DLC chunks are globally mounted and override gameresources. Both open questions answered at once, and Mods for Part 2 are now just a folder.
  • Normal damage (with found 4 dlcs) → the DLC is found, but its Chunk is not mounted for foreign maps. Expected. Continue with Level B.

Level B — register an existing map

Copy stufe_b/packagemapspec.json over dlc_4/base/packagemapspec.json. It reports game/wolf/c01/c01p1 — the same map that base/ also reports, but with chunkid 26 and refchunks: [1], so that chunk_1 (where the map actually resides) is still mounted, and patchlevel 4 like in base/.

Then restart (do not load — a savegame can store values between runs), start Chapter 1 via the chapter selection, and take the first hit.

Observation Conclusion
No damage A DLC Chunk overrides. The DLC Spec entry displaces the Base one, and chunk_26 beats gameresources. The answer to the title question is Yes.
Normal damage, map loads cleanly The DLC entry is not prioritized, or gameresources wins. Answer: No.
Map does not load / missing Assets The DLC entry has displaced, but the refchunk contributes nothing. The mechanism works, the recipe is wrong. Try again with refchunks: [1, 0].
GetIsChunkForMapInstalled: can't find chunk for map game/wolf/c01/c01p1 Both Spec entries are mixed up. Level C.
SearchDLC: Could not load mapspec for dlc id 4 The spec is rejected in this form. Level C.
container entry defaulted: table:playerdamage/normal The entry was found and discarded — the integrity check works, so the uncomputable data_hash fields from 6 are out. Then the path failed not on priority, but on the hash.
resource 'playerdamage/normal' of type 'table' is orphaned No container knows the name anymore — the spec displaced gameresources instead of complementing it. Revert immediately.

Level C — same map in DLC form

stufe_c/packagemapspec.json differs from B in only two fields: layer: -1 and basechunkid: 6 instead of 0/0 — the form used by all three delivered DLCs. Try this if B fails with Could not load mapspec or can't find chunk for map.

Revert

Delete dlc_4/. That is all. No delivered file was touched, so there is nothing to restore — that is the whole point of this path.

How to recognize that it did not work

In summary, after ascending depth of failure:

  1. SearchDLC: found 3 dlcs — never observed.
  2. could not read existing dlc info file — dlcinfo.json corrupted.
  3. Could not load mapspec for dlc id 4 — spec rejected.
  4. idEntitlements::Get DLC 4 not installed — entitlement missing.
  5. can't find chunk for map … — spec accepted, mapping broken.
  6. container entry defaulted — entry found, content discarded (hash check).
  7. … is orphaned (no container contains it anymore) — the DLC was superseded rather than supplemented, and the target is now nowhere.
  8. All quiet, normal damage — the DLC chunk loses to gameresources. This is the clean no-answer.

8. What remains open, and which experiment decides it

In this order, each builds on the previous:

# Question Experiment Cost
1 Does relocating resource_dlcDbgSearchPath move the search path? Stage 0 1 start, 0 files
2 Is a custom-built DLC found? Stage A, log line found dlc id 4 1 start, 1 folder
3 Does the entitlement check access dlcid 4? Stage A, log line idEntitlements same
4 Are DLC chunks globally mounted? Stage A, tally hits same
5 Does a DLC chunk overwrite a base asset? Stage B 1 start, 1 file swap
6 Is the data_hash check active? falls into 5 together (container entry defaulted) —

Question 5 is the one everything hinges on. It cannot be decided without a game launch — the files yield 7,733 collisions and zero disagreements, and the place where the engine chooses lies in the SteamStub-encrypted .text. This is the same hard limit as in docs/patchvorrang.md, Section 4.

The difference from Patch-Precedence.md: there, the decisive experiment costs four changed bytes across two delivered archives and 129 MiB backup. Here it costs a folder that gets deleted again. If both attempts are scheduled anyway, this one should go first.


Sources

  • tools/dlcmod.py — all numbers in this document, plus the packer
  • wolfsdk/idcl.py — archive format, data_hash, the hash note
  • wolfsdk/decl.py — apply_patch, the replacement rule
  • mods/tnc_erste/mod.json — the seven tables and their target values
  • docs/patchvorrang.md — patchlevel, patch precedence, containerType_t::PATCH
  • docs/ladepfade.md — dlc folders of both games, E7/E8, partial-1 strings
  • docs/streamed.md — $streamed images
  • docs/virtualtextures.md, docs/vt-planes.md — .pages, quadrants