ACIDCAT . FILE FORMAT REFERENCE

Native Instruments Anatomy

NISound + kin.nmsv . .nabs . .ksd . .nksf
rev 2026.07
magic "hsin" @0x0C
families hsin . ksd . nksf
endian little (hsin)
sample 68 73 69 6E

Native Instruments presets are three containers wearing one badge. The NISound "hsin" tree carries Massive .nmsv, Absynth .nabs, FM8, Reaktor, and modern Kontakt .nki; the older .ksd document shell (old Absynth, KORE, FM8) wraps zlib chunks around plain XML; and .nksf is a RIFF form full of MessagePack. The formats are community-documented; everything below was re-derived from byte-level facts against real presets, nothing else. The metadata a producer wants is plain in all three: no decompression is needed to read an hsin preset name. Hover any field to light its exact bytes and read the decode; click a field with a + to open its table. Color marks kind (see the key).

family 1 . NISound "hsin" container

An EBML-like tree of size-prefixed frames, everything little-endian. Each frame opens with a 0x30-byte header (its own size, the "hsin" magic, a UUID), then a stack of DSIN-domain item records, then child frames. The item chain in every preset examined:

hsin root frame . frame_size = file size
  DSIN 118 RepositoryRoot
    DSIN 101 Authoring Application  .  appID 7 = Massive . version "1.3.1.0"
      DSIN 108 SoundInfoItem  .  "nicecombo" . author . vendor . description . "Massive"
        DSIN 121 ControllerAssignments
          DSIN 116 EncryptionItem
            DSIN 115 SubtreeItem  .  FastLZ payload, a whole hsin container inside
hsin frame header0x00 . 48 bytes . drawn below

The root frame of nicecombo.nmsv, byte for byte. The magic sits at 0x0C, not 0x00: sniff there. NI writes its four-char codes byte-reversed ("hsin" reads NIsh backwards), a habit that repeats through all three families.

extensions.nmsv (Massive) . .nabs (Absynth) . FM8 . Reaktor . modern Kontakt .nki/.nkm
worked examplenicecombo.nmsv: name "nicecombo", product Massive, version 1.3.1.0
second sampleJohn ananew.nabs: name "John ananew", product Absynth, version 5.0.0.0
DSIN item frames0x30 . 20 bytes each . stacked

At 0x30 the item stack begins: each record is a 4cc domain, a u32 item ID, a u32 version, and a u64 inner size. Records nest by size until the innermost item's own data begins. Drawn below: the RepositoryRoot record that opens nicecombo.nmsv.

UTF-16LE metadata stringsitems 101 + 108 . no decompression

The metadata is stored as pascal strings: a u32 character count, then that many UTF-16LE code units. Item 101 holds the app ID (7 = Massive, 6 = Absynth) and the writing version string; item 108 holds the preset name, author, vendor, description, and finally the app name. Drawn below: the name field of nicecombo.nmsv.

item 101 layoutu32 appID, u32, version pascal string, e.g. 7 . "1.3.1.0"
item 108 layoutname . author . vendor . description . app name, in that order; empty fields are a bare zero count
acidcat readsname, product, version, author, vendor; positional, bounds-checked, mostly-ASCII filter against binary noise
FastLZ subtreeitem 115 . synth state . opaque by default

The SubtreeItem holds the actual patch. Its payload header is drawn below (nicecombo.nmsv); after it comes a FastLZ level-1 stream that decompresses to a complete nested hsin container of raw synth parameter state. acidcat inspect --verbose runs the decompression and reports the inner container; by default it stays opaque, because the metadata never needed it.

worked examplenicecombo.nmsv: 1595 bytes of FastLZ open to a 6017-byte inner hsin; John ananew.nabs opens to 13442 bytes
hardeningdecompression output is capped, so a forged size field cannot balloon memory
family 2 . ksd document shell

Old Absynth, KORE, and FM8 saved through the "SoundShell" document layer: an ASCII "-in-" magic, a class path string, then a run of chunked zlib streams. The 4ccs are byte-reversed again: atad is data, ofni is info, bilz is zlib. Richest metadata of the three families.

header + chunk stream"-in-" @0x00 . atad/bilz chunks

After the magic and a u32 version comes the ASCII class path "#NI#CS#Document##NI#SoundShell#Sound#", then the atad chunks. Each names a stream and wraps it in a bilz envelope with both sizes up front. Drawn below: the info chunk of Trance Sequenz.ksd.

magic"-in-" at 0x00, then u32 version (2 here)
class path"#NI#CS#Document##NI#SoundShell#Sound#", plain ASCII
streams seenofni (the metadata XML) . dnss (sound data); each is one zlib inflate away
decoded info XMLNI_DOC_HEADER . inside the ofni chunk

The ofni stream inflates to a small blob tagged " LMX" (XML, reversed once more) holding a plain NI_DOC_HEADER document. Trance Sequenz.ksd, decoded:

doc_name"Trance Sequenz"; doc_type com.native-instruments.SoundShell.sound
commonAttrAuthor ("not set" here, filtered out) . Vendor . Bankname . Comment . Rating (2) . Color
TechnicalAttrDeviceType "Instrument" . Plugins/Plugin "FM8" . CPU/memory hints
MusicalAttrTempo (0 = unset) . Genre . Key . Scale . TimeSignature
acidcat readsname, author, vendor, bank, comment, plugin, device_type, tempo, genre, key; regex over the XML, no entity expansion possible
family 3 . nksf (NKS)

The modern cross-vendor preset wrapper is just RIFF with form type NIKS. The metadata chunk NISI is a u32 version followed by one MessagePack map; the plugin state rides along raw in PCHK.

RIFF form + NISI chunk0x00 . little-endian sizes . drawn below

The opening bytes of New_Init_2.nksf: standard RIFF header, then NISI lands first with the whole browser card in one msgpack map.

NISI map keysauthor . bankchain (array) . comment . deviceType . name . uuid . vendor
worked examplename "New Init 2" . vendor "Native Instruments" . deviceType INST . bankchain ["Massive X"] . uuid d4aeae07-af01-45a4-9b10-9e76fa3be911
one msgpack entryinside NISI . drawn below

MessagePack packs its type and length into the lead byte, so short strings cost one byte of framing. The name entry from New_Init_2.nksf:

NICA . PLID . PCHKthe rest of the form
NICAcontroller assignment stub; 5 bytes in this file
PLIDmsgpack again: VST.magic, VST3.uid, pluginName "Massive X"; pins the preset to its plugin
PCHKthe raw plugin state chunk; a zlib stream sits inside. acidcat leaves it opaque
acidcat readsname, author, vendor, comment, device_type, bankchain (joined as bank); the msgpack decoder is depth-capped and bounds-checked