Skip to content

NeFS Format 2.0

victorbush edited this page May 13, 2020 · 6 revisions

Overview

The NeFS archive is broken into multiple sections:

Part Description
Header Intro Archive overview; encryption and verification info
Header Intro Table of Contents Offsets to other header parts
Header Part 1 Item metadata: offsets to additional metadata and to the actual compressed data within the archive
Header Part 2 Item metadata: details about item's extracted state (e.g. a file's extracted size, filename, etc.)
Header Part 3 Table of filename and directory name strings
Header Part 4 Compressed sizes/offsets for the chunks of data for each item in the archive
Header Part 5 Archive information (size, name, data offset)
Header Part 6 Item metadata: Additional item attributes
Header Part 7 Item metadata: Navigation to next item in directory
Header Part 8 Unknown data
Compressed file data The chunks of compressed data for the files in the archive

An "item" is either a file or directory within the archive. Directories don't have any compressed data, but have metadata entries in the header just like files do.

An archive can be a single file that contains both the header and data, or the header and data can be split into different files (for example, game.dat files hold item data, but the headers are stored in the game executable).

Header Intro

The header intro contains size, encryption, and verification information.

Start* End* Length Description Notes
0x00 0x03 0x04 Magic # 4E 65 46 53 (ASCII: "NeFS"); 0x5346654E
0x04 0x23 0x20 Expected hash SHA-256 value
0x24 0x063 0x40 An 256-bit key for AES ECB encryption, stored as a hex ascii string
0x64 0x67 0x04 Header size
0x68 0x6B 0x04 NeFS version Major.Minor.Patch probably
0x6C 0x6F 0x04 Number of items in the archive
0x70 0x77 0x08 Unknown Potentially constant 7F 55 12 00 7A 6C 69 62; last four bytes are "zlib"
0x78 0x7F 0x08 Unknown

*Offsets are absolute.

For encrypted headers, this section is encrypted using an RSA-1024 scheme.

  • The first 0x80 bytes are "encrypted" with an RSA private key. Technically this is considered a decrypt operation:
    • valid data -> decrypt w/ private key -> scrambled data
  • To "decrypt", an RSA-1024 public key is used. Technically this is considered an encrypt operation:
    • scrambled data -> encrypt with public key -> valid data
  • No padding is used. Exponent is 65537.
  • For DiRT Rally 2, the public key is stored in the main game executable.
  • The rest of the header is encrypted using AES-256. The key is stored in the header intro.

0x0004 - Expected hash

The expected hash value is a SHA-256 hash of the following data:

  • Offset: 0x00; Length: 0x04 bytes
  • Offset: 0x24; Length: (Header Size - 0x24)

In other words, this is a hash of the header, without the expected hash.

0x0064 - Header Size

Size of the archive's header. Used in expected header hash calculation.

NOTE: The end of the header (computed using the Header Size value) seems to fall inside of Header Part 8, which is unknown data.

Header Intro Table of Contents

The table of contents contains offsets to other header parts.

Absolute Relative Length Description Notes
0x80 0x00 0x02 Number of volumes Usage unknown
0x82 0x02 0x02 Hash block size Usage unknown. This is the low 16-bits of a 32-bit value.
0x84 0x04 0x04 Offset to Header Part 1 Compressed item metadata
0x88 0x08 0x04 Offset to Header Part 6 Additional item metadata
0x8C 0x0C 0x04 Offset to Header Part 2 Extracted item metadata
0x90 0x10 0x04 Offset to Header Part 7 Sibling item links
0x94 0x14 0x04 Offset to Header Part 3 Filename strings table
0x98 0x18 0x04 Offset to Header Part 4 Chunk sizes/offsets
0x9C 0x1C 0x04 Offset to Header Part 5 Contains size of the archive file, plus other unknown data
0xA0 0x20 0x04 Offset to Header Part 8 Unknown
0xA4 0x24 0x5C Unknown Padding?

Header Part 1

  • Contains entries for each item (file or directory) in the archive.
  • Items are not guaranteed to be sorted by id.
  • The number of entries in part 1 is equal to the number of items specified in the header intro.
  • It is possible to have duplicates (multiple entries with the same id).

Each entry has the following layout:

Start* Length Description Notes
0x00 0x08 Offset to compressed data
0x08 0x04 Index Part 2 Used to index into parts 2 and 7
0x0c 0x04 Index Part 4 Used to index into part 4
0x10 0x04 Item id

*Offsets are relative to the start of the item's Part 1 entry.

Header Part 2

  • Entries are ordered by a depth-first traversal of the directory structure with children ordered alphabetically.
  • The number of entries are not guaranteed to be the same as part 1.

Each entry has the following layout:

Start* Length Description Notes
0x00 0x04 Directory id (id of the item that is the parent of this item)
0x04 0x04 Id of the first child of this item Only directories can have children. If the item has no children, the id is the same as the item's id. The first child id is based on the children being sorted by id, not file name.
0x08 0x04 Offset into Header Part 3 where this item's filename/directory name string starts
0x0c 0x04 Extracted size of file Will be 0 for directories
0x10 0x04 Item id

*Offsets are relative to the start of the item's Part 2 entry.

Header Part 3

Contains array of null-terminated strings. These are the filenames/directory names of the items in the archive. Each item's Header Part 2 entry has the appropriate offset into this array.

Header Part 4

Contains an array of cumulative chunk sizes for each file in the archive.

When a file is compressed to be put in the archive:

  • It is split into chunks of size 0x10000.
  • Each chunk is compressed using Deflate compression.
  • The compressed chunks are then put into the archive.

So an entry for an item in Header Part 4 looks like this (where n is the number of chunks):

Start* Length Description
0x00 0x04 Size of compressed chunk 1
0x04 0x04 Size of compressed chunk 1 + 2
((n - 1) * 0x04) 0x04 Size of compressed chunk 1 + 2 + ... + n

*Offsets are relative to the start of the item's Part 4 entry.

The last value in the entry will be the total size of the compressed data in the archive for that item.

Header Part 5

Part 5 has the following layout:

Start* Length Description
0x00 0x08 Total size of NeFS archive
0x08 0x04 Offset into part 3 for name of archive
0x0C 0x04 Offset to first item data

*Offsets are relative to the start of Header Part 5.

Header Part 6

  • Contains the same number of entries as part 1.
  • Entries are ordered the same as part 1.

Each entry has the following layout:

Start* Length Description
0x00 0x02 Volume (?)
0x02 0x01 Flags (bitfield)
0x03 0x01 Unknown

*Offsets are relative to the start of the item's Part 6 entry.

Flags

Value Description
0x1 IsZlib
0x2 IsAes
0x4 IsDirectory
0x8 IsDuplicated
0x10 Unknown
0x20 Unknown
0x40 Unknown
0x80 Unknown

Header Part 7

  • Contains the same number of entries as part 2.
  • Entries are ordered the same as part 2.

Each entry has the following layout:

Start* Length Description Notes
0x00 0x04 Sibling item id Id of the next item in the same directory. If this is the last item in the directory, the sibling id is equal to this item id. The sibling id is based on the children being ordered by id, not file name.
0x04 0x04 Item id

*Offsets are relative to the start of the item's Part 7 entry.

Header Part 8

Unknown data. Seems to be ignored by the game (at least for car nefs). NeFS Edit is currently writing this data as 0. It falls between Header Part 7 and the start of the first item's compressed data. It straddles the "Header Size" barrier that is used when performing a hash check of the header. So I'm not sure how much of this is actually part of the header or not. Needs investigated.

Compressed File Data

Contains the compressed chunks of data for each file in the archive.