.NOS Archive Format

Overview

A .NOS file is a container archive used by the game NosTale to store game assets. The file begins with a 16-byte header whose first bytes identify the internal archive format. Currently three format variants are recognised:

SignatureFormatDescription
NT DataZlibArchiveFormatMost game archives (items, textures, icons, etc.)
32GBS V1.0ZlibArchiveFormatLarge image archives (NS4BbData)
ITEMS V1.0ZlibArchiveFormatOld item data (NSipData2006)
CCINF V1.20CcinfArchiveFormatMonster/sprite info archives
(no magic)TextArchiveFormatLanguage data (NSlangData, etc.)

File Header (16 bytes)

Every .NOS archive starts with a fixed 16-byte header:

Offset  Size  Description
─────────────────────────────────────
0x00    4     Magic bytes (e.g. "NT D" for "NT Data")
0x04    4     Usually more magic or flags
0x08    4     Typically the creation timestamp (uint32 LE)
0x0C    4     Entry table offset or flags

The header is passed to ArchiveFormat::detect() which selects the correct parser based on the magic bytes.

ZlibArchiveFormat ("NT Data" / "32GBS V1.0" / "ITEMS V1.0")

This is the most common format. After the 16-byte header the entry table begins:

Offset  Size  Description
─────────────────────────────────────────────
0x00    4     Entry count (uint32 LE)
0x04    1     Separator byte (usually 0x00 or 0x20)
0x05         Entry records (24 bytes each)

Each entry record in the table:

Offset  Size  Description
─────────────────────────────────────────────
0x00    4     Entry ID (uint32 LE)
0x04    4     Absolute offset to entry data (uint32 LE)

At the offset above, the entry data header:

Offset  Size  Description
─────────────────────────────────────────────
0x00    4     Creation date (uint32 LE)
0x04    4     Uncompressed data size (uint32 LE)
0x08    4     Compressed data size (uint32 LE)
0x0C    1     Compression flag (0 = uncompressed, 1 = zlib)
0x0D         Entry body (compressed_size bytes)

The entry body is either raw data or zlib-compressed data depending on the compression flag.

Sub-types are identified by the third byte of the header:

Sub-typeArchiveContent type
5NStcDataTileGrid
7NStpDataTexture
11NStpeDataTexture
12NStpuDataTexture
14NSppDataSprite
17NSmpDataSprite
23NSeffDataEffectDef
24NSipDataIcon
101NS4BbDataImage4B
103NSipData2006Icon

TextArchiveFormat (no magic)

Text archives store text-based game data such as language strings. They have no unique header magic; detection is a fallback when no other format matches.

Offset  Size  Description
─────────────────────────────────────────────
0x00    4     Entry count (uint32 LE)
0x04         Entry records (variable size)

Each entry record:

Offset  Size  Description
─────────────────────────────────────────────
0x00    4     File number (uint32 LE)
0x04    4     Name length (uint32 LE)
0x08    N     Name string (N bytes, not null-terminated)
0x08+N  4     Flag: 0 = .lst file, non-zero = .dat file (uint32 LE)
0x0C+N  4     Data size (uint32 LE)
0x10+N  M     Data body (M bytes)

.dat files are decoded with the TextDat codec (XOR-based obfuscation). .lst files are decoded with the TextLst codec (line-based encoding).

CCINF V1.20 (NSmnData / NSpnData)

Used for sprite information archives: NSmnData (mobs) and NSpnData (players).

Offset  Size  Description
─────────────────────────────────────────────
0x00    12    Magic: "CCINF V1.20\x1a"
0x0C    4     Creation date (uint32 LE)
0x10    4     Total content file size (uint32 LE)
0x14    4     Same file size repeated (uint32 LE)
0x18    1     Separator (0x00)
0x19    4     Entry count (uint32 LE)
0x1D         Entry records (variable size, uncompressed)

Each entry record is a 16-byte header followed by 7 counted texture-part lists. The header layout follows the OnexExplorer reference interpretation (taletool's id/base/remap/animation reading is a documented discrepancy, see research/ccinf-entry-fields.md):

Offset  Size  Description
─────────────────────────────────────────────
0x00    1     Direction (0, 1, 2, 5, 6)
0x01    1     Animation
0x02    2     Monster (u16 LE)  NSmnData
              or sex/class (1) + morph (1)  NSpnData
0x04    4     Base sprite resource key (u32 LE)
0x08    4     NSPM (u32 LE; 0xFFFFFFFF = none)
0x0C    4     Kit (u32 LE)
0x10         7 texture-part lists

Each texture-part list is count(1) followed by count × 6-byte cells {index(u16 LE), id(u32 LE)}. In NSmnData the parts are keyed "0".."6"; in NSpnData they are named armor, hair, hat, mask, secondary-Weapon, first-Weapon, weapon-part. The player variant packs sex (bit 7) and class (low nibble) into byte 0x02, with the morph index in byte 0x03.

Sprite payloads (NSmpData / NSppData)

Entries are decompressed with ZlibCodec. The body starts with a frame-count byte followed by one 12-byte descriptor per frame:

Offset  Size  Description
─────────────────────────────────────────────
0x00    1     Frame count
0x01    12    Frame descriptor[0]
0x0D    12    Frame descriptor[1]
            

Each descriptor:

Offset  Size  Description
─────────────────────────────────────────────
0x00    2     Width (u16 LE)
0x02    2     Height (u16 LE)
0x04    2     X origin (i16 LE)
0x06    2     Y origin (i16 LE)
0x08    4     Absolute pixel offset (u32 LE)

At offset the frame stores width*2*height bytes of GBAR4444 pixels (2 bytes per pixel, little-endian ARGB nibbles). Descriptors with a zero width or height are unused frame slots (e.g. trailing terminators); they store no pixels and are skipped during extraction. During extraction the pixels are converted to RGBA8 and encoded to PNG (one file per frame), with a sidecar <name>.json holding the frame metadata (width, height, xOrigin, yOrigin).

Effect definitions (NSeffData)

Entries are decompressed with ZlibCodec. The body starts with a 24-byte root header followed by component_count 192-byte component records, then the variable track data:

Offset  Size  Description
─────────────────────────────────────────────
0x00    4     Resource key (u32 LE)
0x04    4     Loaded tick slot (i32 LE)
0x08    4     Source record slot (i32 LE)
0x0C    2     Component count (u16 LE)
0x0E    4     Child records slot (i32 LE)
0x12    4     Reference count slot (i32 LE)
0x16    2     Flags slot (u16 LE)

Each 192-byte component record stores the effect properties and one 12-byte track descriptor per track (8 tracks: rotation, scale, translation, color, texture, and the same rotation/scale/translation for the texture transform); the first byte of each descriptor is the key count. The variant union (kind at 0x04: 0 = positioned, 1 = flying, 2 = particle) defines the effect behaviour; the remaining variant fields start at 0x18.

Variable track data follows all component records. Per track with a non-zero key count the data is laid out as all u16 key times first, followed by the parallel value array — quaternion/vector values as f32, colors as 4 bytes (blue, green, red, alpha), textures as resource keys (i32 LE).

Codecs

CodecApplied toMethod
ZlibCodecZlibArchiveFormat entrieszlib decompress
TextDatCodecTextArchive .dat entriesXOR obfuscation
TextLstCodecTextArchive .lst entriesLine encoding

Image entries (Texture, Icon, Image4B, TileGrid) are additionally converted from their raw pixel format to PNG during extraction. Sprite payloads (NSmpData/NSppData) are split into one PNG per frame plus a metadata sidecar, and sprite info (NSmnData/NSpnData) / effect definitions (NSeffData) are decoded to JSON. See the project README for the supported pixel formats.