Dated source reference. Current release status: handoff state. Full original relative paths are in the developer archive.
NBA 2K14 IFF container research
Current scope (October13,2026): Windows-first deep editing and a public extension API; Mac/Linux compatibility follows later. Earlier native-port goals and next-step proposals in this evidence history are superseded. Technical observations and verification limits remain as recorded.
OpenHoops now has a read-only native C++17 reader for the 94 ef 3b ff family. It was checked against all 4,323 original files in that family, covering 7,875 segments and 78,330 resource descriptors. Both raw and ZLIB-wrapped segments are supported. This is an asset tool, not recovered game-engine code or a playable native game.
Evidence and provenance
The starting reference is the already archived NLSC English IFF Explorer package. Its Nba2k14Explorer.exe is a .NET assembly identifying itself as NBA2K12TEXTUREEDITOR, version 0.4.83.0, company Lotsbiss, copyright 2011. ILSpyCmd 11.1.0.9782 produced the local decompiled IFFLOADER. Relevant methods are LoadIFFFile, FindFImgadr and ExpRawData; BigEndianReader establishes which reads swap byte order. The assembly was read as data and never executed.
The bundled file named NBA2K12TEXTUREEDITOR (0.4.83.0).cs contains only assembly attributes, not the parser implementation. Preserve that distinction when searching the archive. Extraction, attribution and per-file hashes, decompiler/runtime identities.
The reconstructed container reader is newly written, bounds-checked C++; it does not build or run the old editor. An independent Python survey checks the field interpretations against original files. Both implementations use zlib for the standard compression stream, so they are independent container parsers, not independent DEFLATE implementations.
Container layout verified in this corpus
All offsets in this section are byte offsets from the start of the file. LE32 and BE32 identify endianness. Do not cast a C++ struct onto the file: this format mixes byte orders.
| Offset | Encoding | Observed meaning |
|---|---|---|
0x00 |
Four bytes | Family signature 94 ef 3b ff |
0x04 |
LE32 | End of header / lower bound for segment payloads |
0x08 |
BE32 | End of segment body; bytes after this are retained as an unexplained trailer |
0x0C |
Four bytes | Uninterpreted |
0x10 |
LE32 | Number of 32-byte segment descriptors |
0x14 |
Four bytes | Uninterpreted by the native reader |
0x18 |
LE32 | Number of resource descriptors |
0x1C |
LE32 | Relative offset used to locate the resource index: value + 27 |
0x20 |
Repeated 32 bytes | Segment descriptor array |
The old editor calls the value at 0x1C a file type, and uses values 69/101 to select a texture layout. Across the corpus, value + 27 consistently locates a resource-offset index. Treating that value only as a type enum loses an important structural relationship. This observation does not explain every preceding header field.
Each 32-byte segment descriptor contains eight LE32 words. Word 3 is an extent value; word 5 is the absolute file offset; word 6 is stored byte length. All words are retained in the Python survey. The meaning of the other words is not established. Segments must lie inside the declared body and must not overlap.
ZLIB wrapper and the failed length assumption
If the segment begins with ASCII ZLIB, its first 16 bytes are:
| Offset within segment | Encoding | Meaning |
|---|---|---|
0x00 |
Four bytes | ZLIB marker |
0x04 |
BE32 | Exact decoded stream length |
0x08 |
BE32 | Total stored length, including this 16-byte wrapper |
0x0C |
BE32 | Preserved mode-like value; semantics unconfirmed |
0x10 |
Bytes | Standard zlib stream, including its checksum |
There are 7,854 compressed segments and 21 raw segments. Raw segment length equals descriptor extent in the tested corpus.
The first survey incorrectly required the descriptor extent to equal ZLIB decoded length. That rejected 1,417 valid original files. Examination showed that the wrapper gives the actual decompressed length, while the descriptor can be larger. The observed differences are:
| Descriptor extent minus decoded bytes | Segments |
|---|---|
| 0 | 6,458 |
| 8 | 811 |
| 16 | 583 |
| 48 | 15 |
| 56 | 6 |
| 64 | 2 |
For example, png0005.iff segment 1 declares extent 1,576,788 but its wrapper and actual stream both give 1,576,772 bytes. We preserve both values and do not invent padding bytes. Whether the difference represents runtime allocation, alignment or another layout adjustment remains unknown. Initial failed assumption
The reader requires a complete zlib stream, valid checksum, exact declared output length and no leftover compressed input. It rejects unbounded output sizes. Current policy limits are 256 MiB per decoded segment and 512 MiB combined decoded data; the largest observed segment is 147,887,424 bytes.
Resource index and descriptors
The resource index begins at LE32(0x1C) + 27 and contains one LE32 relative pointer per resource. For an entry at file offset p, p - 1 + LE32(p) points to its resource descriptor. This relationship was checked for every resource in the supported corpus.
Descriptors follow the index. Each has an LE32 identifier, LE32 type identifier, LE32 address count, and that many LE32 address values. Their meanings depend on resource type and segment layout. The generic reader preserves address lists without labeling all of them as file offsets: texture metadata, for example, uses addresses into decoded segments.
The complete header gap data, resource payload semantics, relocation mechanisms, and bytes after the body are not decoded. The tool does not rewrite containers or claim round-trip fidelity.
Native validation
The native validation report records the compiled binary, source hashes and commands. Tests compare every original supported file's header bounds, resource table, segment counts, decoded byte counts and CRC32 values to the Python survey. Input SHA-256 values must match the original baseline. Selected compressed and raw extractions additionally match SHA-256 byte-for-byte.
AddressSanitizer and UndefinedBehaviorSanitizer covered 551 truncated, corrupt, overlapping and bit-mutated files, plus representative valid compressed/raw inputs. There were 503 rejections and 48 accepted mutations with no sanitizer diagnostics. Accepted mutations include changes outside the fields currently interpreted; acceptance does not certify every byte's semantics. Existing extraction outputs are refused and their contents preserved.
The native reader and exporter have run only on macOS ARM64. Windows/Linux and macOS x64 remain untested. CMake Release builds are now verified through the native viewer milestone. Native code links the platform zlib; no DirectX, Wine or x86 emulation is involved in asset inspection.
Texture milestone and limitations
A narrow native exporter handles standalone, two-address texture resources of type 1547079785 in direct layouts whose 0x1C values are 69 or 101. For this subset, metadata is in decoded segment 0, pixel data in decoded segment 1. DXT1/DXT5 FourCC is at metadata +8; LE16 width/height are at +104/+106; the LE32 value at +116 gives a relative pixel adjustment of value - 1, added to the resource's second address.
The exporter creates a DDS header and copies the compressed top mip without recompressing pixels. DDS layout follows Microsoft's header and pixel-format definitions. Top-mip size uses complete 4-by-4 blocks: 8 bytes for DXT1 or 16 bytes for DXT5. Block compression reference
There are 4,393 candidate resources in that subset; 77 were exported and independently decoded, spanning every observed format/dimension combination plus a seeded spread. The sample includes 20 DXT1 and 57 DXT5 images. DDS payload bytes exactly match the original decoded segment slices. Pillow 11.3.0 reads the DDS files and supplies independent PNG previews. Texture validation
The original logo000.iff renders as the Philadelphia 76ers logo. The PNG preview was visually inspected. This is real extracted game art, not a mockup or replacement texture.
No palette textures, grouped/model textures, alternate layouts, swizzle conversion or full mip chains are claimed. This is a native asset-reader/exporter foundation with separate image previews, not a native game renderer. The 306 files belonging to the other three IFF signatures remain unsupported by this reader. The archived editor's DORNALOADER provides an additional IFF/CDF lead for the 305098f0 family; that has not yet been implemented.
Reproduce
.venv/bin/python tools/survey_iff.py
.venv/bin/python tools/verify_iff.py
.venv/bin/python tools/survey_iff_textures.py
.venv/bin/python tools/verify_iff_textures.py
See native tool usage, full container survey, survey summary, and texture candidates. Generated outputs go into build/ or analysis/; original IFF files remain unchanged.