OPENHOOPS / 2K14Final team archive · November 9, 2026Reference libraryChecksums

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.