.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:
| Signature | Format | Description |
|---|---|---|
NT Data | ZlibArchiveFormat | Most game archives (items, textures, icons, etc.) |
32GBS V1.0 | ZlibArchiveFormat | Large image archives (NS4BbData) |
ITEMS V1.0 | ZlibArchiveFormat | Old item data (NSipData2006) |
CCINF V1.20 | CcinfArchiveFormat | Monster/sprite info archives |
| (no magic) | TextArchiveFormat | Language 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-type | Archive | Content type |
|---|---|---|
| 5 | NStcData | TileGrid |
| 7 | NStpData | Texture |
| 11 | NStpeData | Texture |
| 12 | NStpuData | Texture |
| 14 | NSppData | Sprite |
| 17 | NSmpData | Sprite |
| 23 | NSeffData | EffectDef |
| 24 | NSipData | Icon |
| 101 | NS4BbData | Image4B |
| 103 | NSipData2006 | Icon |
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
| Codec | Applied to | Method |
|---|---|---|
| ZlibCodec | ZlibArchiveFormat entries | zlib decompress |
| TextDatCodec | TextArchive .dat entries | XOR obfuscation |
| TextLstCodec | TextArchive .lst entries | Line 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.