Wolfenstein Modloader Buy me a coffeeKaffee spendieren

Wiki / Game systemsSpielsysteme

streamed.resources and english.streamed

SolvedGelöst Original page in the GitHub wikiOriginalseite im GitHub-Wiki

In plain wordsIn einfachen Worten

This page explains the two large audio files of Wolfenstein: The New Order, streamed.resources and english.streamed. They turn out to be simple containers of standard Ogg Vorbis sound streams, and every single byte is accounted for. All 23,565 sounds, about 24.6 hours of audio, can now be exported and played in any normal player. Replacing sounds directly is technically possible but risky, because these files are not included in the mod tool's backup. The New Colossus has no such files; it uses a different audio system (Wwise).

  • 23,565 Ogg Vorbis streams, 24.6 hours of audio, 0 unexplained bytes.
  • All 362,152 Ogg pages pass their CRC check with 0 errors.
  • Sample counts decoded with ffmpeg match the game's own index exactly (e.g. 7,938,432 = 7,938,432).
  • 4,020 layered weapon sounds sit inside 149 'samplepacks' and have no index entry of their own.
  • Export is safe; direct replacement is not done because the 1.5 GB files are not backed up.

Diese Seite erklärt die zwei großen Audiodateien von Wolfenstein: The New Order, streamed.resources und english.streamed. Es sind einfache Container mit Standard-Ogg-Vorbis-Tonspuren, und jedes einzelne Byte ist erklärt. Alle 23.565 Sounds, etwa 24,6 Stunden Audio, lassen sich jetzt exportieren und mit jedem normalen Player abspielen. Sounds direkt zu ersetzen ist technisch möglich, aber riskant, weil diese Dateien nicht im Backup des Mod-Tools liegen. The New Colossus hat solche Dateien nicht; es nutzt ein anderes Audiosystem (Wwise).

  • 23.565 Ogg-Vorbis-Spuren, 24,6 Stunden Audio, 0 unerklärte Bytes.
  • Alle 362.152 Ogg-Seiten bestehen ihre CRC-Prüfung ohne Fehler.
  • Mit ffmpeg dekodierte Sample-Zahlen stimmen exakt mit dem Index des Spiels überein (z. B. 7.938.432 = 7.938.432).
  • 4.020 geschichtete Waffensounds stecken in 149 'Samplepacks' und haben keinen eigenen Indexeintrag.
  • Export ist sicher; direktes Ersetzen wird nicht gemacht, weil die 1,5-GB-Dateien nicht gesichert sind.

Technical detailsTechnische Details

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

These are the two files that have been on the blacklist since the project began. They are now read — exclusively with "rb", without a single write operation in the game folder. Everything here is measured, not explored; where something remains uncertain, it is marked as conjecture.

All numbers come from tools/streamed.py (Standard Library, mmap). Verifiable via:

py -3.11 tools/streamed.py inventory
py -3.11 tools/streamed.py walk <file> --crc all
py -3.11 tools/streamed.py verify
py -3.11 tools/streamed.py prove "music/" <output_folder>
py -3.11 tools/streamed.py control --scratch <output_folder>
py -3.11 tools/streamed.py --selftest

0. Summary

They are Ogg Vorbis containers. Both, complete, with no remainder.

Part 1 (TNO) Part 2 (TNC)
*.streamed / streamed.resources 2 files, 1,572,314,960 B none
Content 23,565 Ogg Vorbis streams — (Wwise .pack, 2.88 GB)
Playtime 24.6 hours of audio —
Coverage by the format described here 100.000% of streams, 100.000% of page bytes —
Unexplained bytes 0 —

For mods, this means: the audio bytes of Part 1 are addressable, exactly length-defined, and exportable. Replacement is possible but dangerous — §7.


1. Inventory: where they lie, how many, how big

A recursive search was performed across both installations for *.streamed, streamed.resources, and anything carrying the name streamed.

Game File Bytes GiB
TNO base\english.streamed 415,137,488 0.387
TNO base\streamed.resources 1,157,177,472 1.078
TNC — 0 —

Part 2 has not a single streamed file. The audio lies there in base\sound\soundbanks\pc\ as Wwise packages:

sound.pack                 1,722,766,110 B
english(us).pack             758,900,091 B
patch_3_sound.pack           193,042,687 B
patch_1_sound.pack           110,345,075 B
patch_2_sound.pack            47,419,238 B
patch_1/2/3_english(us).pack  47,619,261 B combined
patch_4_sound.pack / patch_4_english(us).pack   each 132 B
wwise_project.bin              4,493,695 B
prodspec.json                    163,237 B
                          ------------------
Sum                        2,884,749,658 B (2.69 GiB)

This is a different world (id Tech 6 + Wwise) and was not the subject of this investigation; docs/audio.md §0 had already correctly placed it.

Per language: in this installation there is exactly one language file, english.streamed. The bsnf descriptors in the archive lead to four languages (english, french, italian, spanish), but no french.streamed or similar exists. Conjecture: the other languages are separate Steam Depots and simply not installed. Not verified — this would require changing the game language in Steam, which was outside the scope of this task.


2. The Format, Layer by Layer

2.1 It is not a project format

Neither wolfsdk/resources.py (Magic 03 53 45 52) nor wolfsdk/idcl.py (Magic IDCL) applies. The files have no file header at all — Byte 0 is already the start of the first Ogg page (4F 67 67 53 = OggS). There is no table, no directory, no count, no checksum at the container level. The directory lies outside, in the chunkN.index.

2.2 Blocks on a 16384-Byte Grid

The container is a sequence of blocks. Each block begins on a multiple of 16384 (16 KiB) and is padded with null bytes up to the next 16-KiB boundary.

Blocks free space per block: min median max
streamed.resources 14,603 0 8,544 16,382
english.streamed 5,091 1 8,041 16,380

A block contains either one Ogg stream or a continuous chain of two to 140 Ogg streams (this is the samplepack, §2.4). The free space never exceeds a grid step — this serves as proof that the grid is truly 16384 and not a multiple thereof.

docs/audio.md §3 states "max 3,144,537 B of free space per slot". This was an artifact of the slot-based calculation: it measured the distance to the next referenced sample offset and skipped over the pack blocks in between. Measured block-wise, it is never more than 16,382 B.

2.3 The Ogg Layer

Regular Ogg per specification, nothing changed, nothing truncated:

streamed.resources english.streamed
Ogg Pages 267,219 94,933
Ogg Packages 4,213,830 1,780,066
Logical Streams 18,474 5,091
Page Bytes 1,034,059,500 (89.360 %) 373,618,721 (89.999 %)
Padding 123,117,972 (10.640 %) 41,518,767 (10.001 %)
Padding Pieces 14,599 5,090
Padding that is not pure zeros 0 0
Page Sequence Number Breaks 0 0
Balance Pages + Padding − File Size 0 0

The Vorbis identification header of each stream, read:

Channels Rate br_nominal blocksize0/1 Streams
2 48000 128000 256 / 2048 13,721
1 48000 128000 256 / 2048 3,948
1 48000 96000 256 / 2048 4,692 + 704
2 48000 96000 256 / 2048 399 + 101

Comment header, identical in all 23,565 streams:

Xiph.Org libVorbis I 20120203 (Omnipresent)

This is libVorbis 1.3.3 (February 2012) — fitting for a game from 2014 and an indication that the streams were generated with the reference encoder, not with middleware. User comment fields (Title, Artist) do not exist; names are stored exclusively in the archive.

Measured bitrate over all — page bytes divided by play duration:

streamed.resources   139,732 bit/s   (16.45 h audio)
english.streamed     102,422 bit/s   (8.11 h audio)

This aligns with br_nominal 128000 and 96000 plus Ogg frame overhead. The bitrate is thus measured and explained, not estimated.

2.4 samplepack — what the chains explain

During the pass it became apparent: of the 18,474 streams in streamed.resources, only 14,603 lie on the 16-KiB grid. The remaining 3,871 begin mid-block. This is not stated in docs/audio.md because there only the sample descriptors were followed, and they all point to grid addresses.

The explanation is the asset type sound with path extension .samplepack, Magic 1KPS (= SPK1 byte-swapped). Layout, derived and then fully verified against all packs:

u8[4]    "1KPS"
LE u16   Number of members
per member:
  LE u32   Path length
  char[]   Path of the sample asset, e.g.
           "generated/sound/weapons/ar60/ar60_ai_split/…_vorbis.bsnd"
  LE u32   Offset, relative to block start
  LE u32   Length in bytes

149 non-empty packs (plus 2 empty placeholders with count = 0), members min 5 / median 25 / max 140, totaling 4,020 streams and 45,113,737 bytes. 143 packs are under weapons/, 6 under enemies/.

Content-wise these are the layered weapon sounds: a pack like weapons/ar60/split_ai/ar60_ai_split_nearmid bundles 40 variants (fire_first_r001…r005, fire_split_r001…r030, fire_tail_r001…r005) which are merged together on firing. They lie one after another in one streaming block: a single read operation, not forty.

Important for Mods: the 4,020 pack members have no own index entry. They are addressable exclusively via the pack. Anyone looking only at sample assets will see 21.8 % of the streams in streamed.resources missing.


3. The Proof of Completeness

The requirement was: the structure must span the entire file, gap-free, non-overlapping, ending matching the file size. This is the finding.

3.1 Container → Archive

tools/streamed.py verify runs both containers without any index, collects every found stream start along with its measured length, and then asks for each individual one: is it claimed by any archive descriptor?

streamed.resources   Streams 18474   claimed 18474 (100,000 %)   remaining 0
   claimed Bytes 1.034.059.500 of 1.034.059.500 page bytes (100,000 %)
english.streamed     Streams  5091   claimed  5091 (100,000 %)   remaining 0
   claimed Bytes   373.618.721 of   373.618.721 page bytes (100,000 %)
total 23565 Streams, 23565 claimed (100,000 %)

3.2 Archive → Container

Conversely: no descriptor must point to nothing. The length in the descriptor must exactly match the length resulting from the Ogg page scan in the container.

unique sample-Assets      19545   hits exact  19545   failures 0
unique samplepacks          151   of which empty 2
   fully consumed         149   failures 0   members 4020

"Fully consumed" means for a pack: every one of its members lands on a real stream start and the length matches exactly. A single false member causes the entire pack to fail.

3.3 The Math Adds Up

19.545 sample-Assets            (= all 16-KiB block starts except the packs)
+  149 samplepack-block-starts
= 19.694 blocks                 = 14.603 + 5.091  ✓

  4.020 pack-members − 149 block-starts = 3.871 non-grid-aligned streams  ✓
19.545 + 4.020 = 23.565 streams total                                     ✓

Each of these three equations was measured independently. No byte and no entry remains unaccounted for.

3.4 Byte Balance

For both files, page_bytes + padding_bytes − file_size = 0. The scan assigns every single byte: either part of an Ogg page or null padding. Unexplained bytes: 0. No overlap case, no backward offset, no sequence number gap.


4. The Proof That Counts: An Entry That Is Really Something

The chain spans two separate files and closes on the individual PCM sample exactly. Executed with the real reference decoder (ffmpeg 8.1.2, libvorbis), not with a heuristic.

sample:sound/music/journal/house_of_the_rising_sun.wav

Archive (chunkN.index)      Offset 0x08638000  (grid-aligned)
Descriptor (bsnf in archive) Length 2.681.895 B   Granule 7.938.432 Samples
Container (measured)        Length 2.681.895 B   Granule 7.938.432
                            623 Ogg pages, CRC ok 623 / incorrect 0
Vorbis-ID                  2 channels, 48000 Hz, br_nom 128000
ffprobe                    codec_name=vorbis  duration=165.384000
ffmpeg -f s16le            31.753.728 B / 2 channels = 7.938.432 PCM-Frames

7.938.432 == 7.938.432. The number in the archive and the number calculated by a foreign decoder from the container bytes are identical. 165.384 s results with 2.681.895 bytes exactly 129.7 kbit/s — the nominal bitrate of the header.

Two further proofs in the same form:

Entry Container Length Granule ffmpeg-PCM-Frames
sound/music/journal/karla_master.wav streamed.resources 2.115.418 6.260.416 6.260.416
sound/vo/english/bj/efforts/hurt/eff_pl_hurtsoft_001.wav english.streamed 11.991 33.340 33.340

And a Pack Member

The harder case, because this stream has no own index entry and is only findable via the 1KPS table — member 1 of weapons/ar60/split_ai/ar60_ai_split_nearmid, absolute 0x003F167A, thus in the middle of the block:

Pack says:   Offset relative 0x167A, Length 5.792 B
Container:   5.792 B, 3 pages, CRC ok 3 / incorrect 0, Granule 4.080
ffprobe:     vorbis, 48000 Hz, 2 channels, duration=0.085000
ffmpeg:      16.320 B / 4 = 4.080 PCM-Frames

4.080 == 4.080, and 4080 / 48000 = 0.085 s. The chain Pack table → container bytes → decoder closes exactly as well.


5. The Control Check

An inspection that accepts everything checks nothing. This one checks three things separately — Magic, page arithmetic, and CRC — and each must be able to fail individually.

The Ogg checksum is CRC-32 with polynomial 0x04C11DB7, MSB first, Init 0, no Final-XOR, no reflection. This is explicitly not zlib.crc32 (same polynomial, but reflected, Init 0xFFFFFFFF, Final-XOR). For the example b"OggS-probe-0123456789": zlib.crc32 = 0x291ADAC9, ogg_crc = 0x36C40DCD.

Input                                 Bytes  Pages  CRCgepr  CRCbad  CRCok  Cover%
Random bytes                          1048576       0        0       0      0    0,000
Zeros                                1048576       0        0       0      0    0,000
chunk0.resources @1MiB (TNO, real)    1048576       0        0       0      0    0,000
chunk_1.resources (TNC, real)         1048576       0        0       0      0    0,000
Random bytes with "OggS" Magic        1048576       0        0       0      0    0,000
Real Ogg start (unchanged)            1048576     226      226       0    226   65,862
Real Ogg start, 1 bit flipped         1048576     226      226       1    225   65,862

What this shows:

  1. Real game data that is not Ogg is rejected — both archive formats, not just random. 0 pages, 0% coverage.
  2. Magic alone is insufficient. Random bytes preceded by "OggS" yield 0 pages: the version byte check and segment table arithmetic catch them.
  3. The CRC is sharp. A single flipped bit in a real stream causes exactly one page to fail — and only that one.

Plus the full run: all 362,152 Ogg pages of both files were CRC-checked, 0 errors. There is no falsely structured entity that happens to hit a 32-bit checksum 362,152 times in a row.

tools/streamed.py --selftest additionally builds a valid Ogg page by hand, checks CRC and arithmetic, and ensures that a corrupted page and false Magic fail.


6. What's inside

After the upper scope of the sample name, across both containers combined (only the 19,545 indexed assets; the 4,020 Pack members count towards their Pack):

Scope Streams Bytes
vo (Voice output) 5,896 385.8 MB
music 461 382.3 MB
ambience 1,331 249.6 MB
anim_events 1,810 106.5 MB
weapons 4,078 65.5 MB
enemies 2,122 53.7 MB
ambient_events 265 35.1 MB
vehicles 180 21.6 MB
movements 941 13.2 MB
footsteps 1,279 12.3 MB
explosions 164 9.8 MB
env_vo 63 7.5 MB
impacts 295 4.2 MB
projectile 255 3.6 MB
Rest (≈ 20 more) 405 ~9 MB

english.streamed contains exclusively sound/vo/english/* — 5,091 streams, 373.6 MB, 8.11 hours, a single namespace, no exceptions. streamed.resources contains everything else, distributed across 404 namespaces, including non-English voice output under vo/efforts (775 streams, neutral sounds like screams).

Largest individual entries: music with 461 streams and 382 MB — so the music is almost as large as the entire English voice output. The longest stream in the container runs 357.4 s, the shortest 0.001 s.

Stream sizes: streamed.resources min 4,426 / median 12,146 / max 5,789,560 bytes; english.streamed min 6,897 / median 37,199 / max 2,273,748 bytes.


7. What this means for Mods

What is now safe:

  • Export. Each of the 23,565 Sounds can be individually extracted and played back with any standard player — tools/streamed.py prove <pattern> <directory> generates ready-made .ogg files. For modding, this is the foundational material: you hear what you will bend before you bend it.
  • Bending without touching the container. The method described in docs/audio.md §4 — making edit.soundFiles.item[i] of a sound declaration point to another existing sample — remains correct. It is now easier to use because candidates can be heard beforehand.

What works, but is dangerous:

A sample replaces itself by writing new Ogg bytes into its block and adjusting the length in the bsnf. Technically, there is no objection: the block has an average of 8.6 KiB slack (english.streamed: 8.0 KiB), there is no checksum at the container level, and the blocks are non-overlapping. It has not been done, and it should not be done, for a reason unrelated to the format:

streamed.resources and english.streamed lie in no backup. base/_wolfsdk_backup/ contains only chunkN.index and chunkN.resources. A corrupted byte costs 1.5 GB of re-download via Steam's file verification.

Anyone who intends to do this must first: make a full copy of both files outside the game directory, and extend wolfsdk/patch.py to treat this copy like the archive backup. Without both, it is not modding, but a risk.

Three limitations apply, all measured:

  1. Block space is tight. An average of 8,544 B slack (english.streamed: 8,041 B) means a replacement must be roughly 8 KiB larger than the original on average, or else it will overwrite the neighboring block. There is no more than 16,382 B slack anywhere, and in at least one block it is 0.
  2. Packs are rigid. Changing a member of a samplepack shifts all subsequent members — and the offsets are recorded in the 1KPS table within the archive. These are two files that must match. This affects 4,020 streams, or 21.8% of streamed.resources.
  3. The descriptor contains more than just length. The bsnf includes Granule, Channels, Rate, and a WAVEFORMATEX. A replacement with a different channel count or sample rate requires including all of this. Hypothesis, how the engine reacts to contradictions: untested and unverifiable without starting the game.

What does not work: Part 2. There, such containers do not exist; the audio lies in Wwise Banks, which is a separate, unresolved topic.


8. What remains open

  • The other three languages. The descriptors know french, italian, and spanish; no installed files exist for them. Whether they lie alongside as french.streamed or similar is speculation.
  • voicetrack (30,627 entries, 5,115 unique, .bmvtr). These demonstrably do not belong in these containers. Two independent reasons: first, after matching, every single stream is already claimed by a sample or a samplepack, leaving none for them. Second, their field at the same location where sample leads to the container offset (+12+20) contains the value 0x706C653F for all 5,115 — these are the ASCII characters ple?, i.e., remnants of a string, not an offset; 0 out of 5,115 hit a stream start. The entry remainder in voicetrack is simply structured differently. What resides in their 310-byte blocks per language (docs/audio.md §2 speculates on lip-sync data) remains open — but it is not audio. This is an exclusion, not a finding.
  • The two empty samplepack placeholders (1KPS + count = 0, inheritences/weapons/weapons_ai_dry_looping, …/weapons_ai_env_looping). Their offset field contains ASCII (0x706C653F), pointing nowhere. Speculation: inheritance nodes without their own data, analogous to the inheritences/* sound decls.
  • Why exactly 16384. The grid is measured and applies universally, but the number itself appears nowhere in the EXE search this task covered. It is likely the sector size of the streaming reader. Speculation.
  • The EXE section. .text is SteamStub-encrypted; the task did not allow unpacking. Therefore, an idStreamedFile class could not be reconciled with the finding. It was not necessary: the structure is fully populated from the data alone.

9. Relation to docs/audio.md

docs/audio.md §3 was correct in every point and forms the basis of this work. What is added here:

Audio.md §3 here
Access via sample descriptors additionally without any index, directly through the file
Streams 14,454 + 5,091 slots 18,474 + 5,091 streams
Coverage not quantified 100.00 %, balance 0
Padding "free space per slot" proven pure zeros, 100%
samplepack mentioned as type, content unknown layout solved, 149 packs, 4,020 members
Checksum — all 362,152 Ogg pages CRC-checked, 0 errors
Decoder — ffmpeg, PCM frames == Granule
Cross-check — present, separates Magic / Arithmetic / CRC

The one number that shifts: "14,454 occupied slots" were the grid-aligned blocks without packs. With the packs included, there are 14,603 blocks and 18,474 streams. Both numbers are correct; they count different things.