LabVIEW VI File Format

Table of Contents

Deciphering NI’s LabVIEW VI file format and proprietary data structures.

VI File Format

What’s in a VI? How does it work? Can I avoid VI Server to load VIs? Why is the VI “source code” proprietary?

Stay tuned…

Disclaimer: All content on this page is my own interpretation of the VI file format and is subject to change with each future LabVIEW release. Read with caution.

VI Read Chunks

Download VI (LV2021)

Overview

A LabVIEW Virtual Instrument (“VI”) is NI’s proprietary source code file format. Little information is publicly available. There are different types of VIs but they are essentially the same unix resource file formats.

VI File Types:

  • VI: *.vi
  • VI Templates: *.vit
  • VI Malleables: *.vim
  • VI Polymorphics: *.vi
  • VI Globals: *.vi
  • Controls: *.ctl
  • Control Templates: *.ctt

This document describes my best attempt to explain how LabVIEW VI’s are formatted and how to parse them.

File Structure

Before geting started, let me explain my terminology. A VI file is a binary file that contain “Sections” and “Chunks.” Sections are blocks of data within the file that hold position and metadata information about the Chunks. Chunks are named data pairs that define the functions & behaviours of the VI (Connector Pane, Front Panel, Block Diagram, Icons, etc.). Sections have a predefined order but Chunks can vary in position and order. Similar to PNG images, Chunks have a 32-bit human readable ASCII string that define that chunk (‘LVSR’, ‘vers’, ‘ICON’, ‘BDHb’, ‘FPHb’). Unlike PNG images, a Chunks identifier and data are located in different segments of the file and are referenced by relative byte offsets from the header definitions throughout the file.

LabVIEW VI File Structure

VI File (*.vi|*.vit|*.vim|*.ctl|*.ctt)

Bytes | Section       // Description
----------------------------------- 
   32 | RSRC_HEADER1  // First resource header
  x.. | CHUNK_DATA[]  // Data chunks (Positions determined by CHUNK_INFO)
   32 | RSRC_HEADER2  // Second resource header (duplicate of first)
   20 | RSRC_INFO     // Resource information packet
    4 | CHUNK_CNT     // Number of chunks (Add 1 to count)
 12*n | CHUNK_IDS[]   // Chunk identifiers (Array size: CHUNK_CNT + 1)
 20*n | CHUNK_INFO[]  // Chunk position information (Array size: CHUNK_CNT + 1)
  1*x | VI_NAME       // Qualified VI filename (1 byte length + string)
<End Of File>

Order of operation

  1. Read the RSRC_HEADER1 to determine the byte offset to RSRC_HEADER2
  2. Read RSRC_HEADER2 and verify that RSRC_HEADER1 and RSRC_HEADER2 match (if not the file is corrupt)
  3. Next read RSRC_INFO to determine the byte offset to CHUNK_CNT
  4. For each chunk
    • Read the CHUNK_ID to get the chunk name and CHUNK_INFO byte offset
    • Read the CHUNK_INFO to determine the byte offset to CHUNK_DATA
    • Return the chunk name + CHUNK_DATA flattened data string
  5. Map each chunk name to data
    Chunk Data
    ‘LVPS’ 0x FF..
    ‘ICON’ 0x FF..
    ‘FPHb’ 0x FF..
    ‘BDHb’ 0x FF..
  6. Unflatten each Chunk into LabVIEW Data Types

Sections

RSRC_HEADER

All VI files start with the resource header RSRC_HEADER which is a 32 byte packet with the VI Signature and position data of the chunk identifications vs data segments (Metadata vs. Data).

The first 8 bytes of the file start with the resource identifier 'RSRC\r\f' with a resource version in hex 0x0003 (Currently version 3 as of LabVIEW 2021).

The next 8 bytes identify the VI Signature type and subtype, for example 'LVINLBVW' is a standard VI (Refer to the VI Signature section below).

The last 16 bytes are 4 * I32’s that define Resource byte offset and byte size (offset to RSRC_HEADER2) and the Data byte offset and size (offset to CHUNK_DATA[]) sections respectively.

RSRC_HEADERx (32 bytes)

Bytes| Parameter    // Description
----------------------------------- 
   4 | 'RSRC'       // S32 Resource header (32-bit ASCII string: 'RSRC')
   2 | '\r\n'       // U16 Carriage return + line feed (0x0D0A)
   2 | '\xx\xx'     // U16 Resource version in hex (0x0003)
   4 | 'LVIN'       // S32 VI type (32-bit ASCII string: 'LVIN', 'LVCC', 'LVAR')
   4 | 'LBVW'       // S32 VI sub-type (32-bit ASCII string: 'LBVW')
   4 | RSRC_OFFSET  // I32 byte offset to RSRC_HEADER2
   4 | RSRC_SIZE    // I32 byte size of RSRC_HEADER..VI_NAME
   4 | DATA_OFFSET  // I32 byte offset to CHUNK_DATA[]
   4 | DATA_SIZE    // I32 byte size of CHUNK_DATA[]

Example:

'R  S  R  C | \r \f XX XX |  L  V  I  N |  L  B  V  W'
52 53 52 43 | 0D 0A 00 03 | 4C 56 49 4E | 4C 42 56 57

       2320 |         832 |          32 |        2288   
00 00 09 10 | 00 00 03 40 | 00 00 00 20 | 00 00 08 F0

The first resource header RSRC_HEADER1.RSRC_OFFSET points to the starting location of the second resource header RSRC_HEADER2 where the chunk definitions start (towards the end of the file). The RSRC_HEADER1.DATA_OFFSET points to the starting location of the CHUNK_DATA section. Both RSRC_HEADER1 & RSRC_HEADER2 should be identical, otherwise it means the file is corrupt.

The sum of the RSRC_HEADER (32 bytes) + RSRC_SIZE + DATA_SIZE should equal the total file size.

Top | File Structure

VI Signature

The two 32-bit ASCII strings in the resource header RSRC_HEADER define the VI file type LVIN and sub-Type LBVW. Here are the known VI Types and Sub-Types:

  • LVINLBVW
    • VI (*.vi)
    • VI Template (*.vtt)
    • VI Polymorphic (*.vi)
    • VI Global (*.vi)
    • VI Malleable (*.vim)
  • LVCCLBVW
    • Control (*.ctl)
    • Control Template (*.ctt)
  • LVARLBVW
    • LabVIEW Library (*.llb)

Other LabVIEW file types (Unsupported):

  • XML 1.0
    • LabVIEW Projects (*.lvproj)
    • LabVIEW Library (*.lvlib)
    • LabVIEW Class (*.lvclass)
    • LabVIEW XControl (*.xctl)
  • <binary>
    • LabVIEW Run-Time Menus (*.rtm)
    • LabVIEW Palettes (*.mnu)

Top | File Structure

CHUNK_DATA[]

After the first resource header RSRC_HEADER1 are the Data Chunk sections CHUNK_DATA[]. Each data chunk is a flattened string with a prefixed I32 length + data. Data chunks only contain the raw chunk data (no metadata, name, etc.) and vary in size depending on chunk type. These sections are read last.

CHUNK_DATA[] (4 bytes + n bytes each)

Bytes| Parameter    // Description
----------------------------------- 
   4 | DATA_LENGTH  // I32 byte size of proceeding data
   n | DATA         // Flattened chunk data bytes

Data chunks may not be in the same order as the chunk identifiers, use the chunk information sections CHUNK_INFO[] to determine the data chunk file position.

Top | File Structure

RSRC_INFO

After the second resource header RSRC_HEADER2 (See RSRC_HEADERx section for definition), the Resource Information RSRC_INFO defines the Chunk Count CHUNK_CNT byte offset (relative from RSRC_HEADER1). This section contains other integer metadata (purpose unknown) but the only field needed is the data byte offset that points to the CHUNK_CNT starting position.

RSRC_INFO (20 bytes)

Bytes| Parameter    // Description
----------------------------------- 
   4 | INT1         // I32 metadata (unknown purpose)
   4 | INT2         // I32 metadata (unknown purpose)
   4 | INT3         // I32 metadata (unknown purpose)
   4 | DATA_OFFSET  // I32 offset to CHUNK_CNT from RSRC_HEADER1
   4 | INT4         // I32 metadata (unknown purpose)

Top | File Structure

CHUNK_CNT

The Resource Information RSRC_INFO section points to the starting position of the Chunk Count CHUNK_CNT (Relative from the RSRC_HEADER2). This parameter defines the number of chunks to load + 1. For example, a chunk count 0 == 1 chunk, 1 == 2 chunks, …

CHUNK_CNT (4 bytes)

Bytes| Parameter  // Description
----------------------------------- 
   4 | COUNT      // I32 number of chunks + 1

For loop through the following sections to load each chunk identifier CHUNK_ID[].

Top | File Structure

CHUNK_ID[]

The Chunk Count CHUNK_CNT identifies the number of chunks to load + 1. The Chunk Idenifier CHUNK_ID[] contains the chunk name, chunk info count and chunk info offset to the Chunk Information sections CHUNK_INFO[] (relative byte offset from RSRC_HEADER2).

CHUNK_ID[] (12 bytes each)

Bytes| Parameter    // Description
----------------------------------- 
   4 | CHUNK_NAME   // S32 ASCII string chunk name (i.e. 'LVPS', 'ICON')
   4 | INFO_COUNT   // I32 number of info chunks to load
   4 | INFO_OFFSET  // I32 byte offset to 'CHUNK_INFO' relative from 'RSRC_HEADER2'

Top | File Structure

CHUNK_INFO[]

The Chunk Identifier CHUNK_ID defines the start position (relative byte offset from RSRC_HEADER2) to the Chunk Information section CHUNK_INFO. The chunk information section defines the position of the Chunk Data (at the beginning of the file). We only care about the Data Offset parameter to the chunk’s data.

CHUNK_INFO[] (20 bytes each)

Bytes| Parameter    // Description
----------------------------------- 
   4 | INT1         // I32 metadata (unknown purpose)
   4 | INT2         // I32 metadata (unknown purpose)
   4 | INT3         // I32 metadata (unknown purpose)
   4 | DATA_OFFSET  // I32 offset to CHUNK_DATA from RSRC_HEADER1
   4 | INT4         // I32 metadata (unknown purpose)

Top | File Structure

VI_NAME

The end of the file contains the qualified filename as 1 byte length + string.

VI_NAME (1 byte + n bytes)

Bytes| Parameter    // Description
----------------------------------- 
   1 | LENGTH       // U8 filename length
   4 | FILENAME     // STR filename string

The VI filename should always be the last section in the file. The VI Name byte offset can be determined by the MAX(CHUNK_INFO[].DATA_OFFSET + 20 bytes).

Top | File Structure

Chunks

These are the known VI Chunks and there data formats (in order by name):

BDEx

BDHb

Block Diagram objects binary tree.

BDPW

Block diagram passwords and hashes. The VI password is hashed multiple times. This chunk stores the 2 hashes used against the hashed VI Password to lock the block diagram out.

LabVIEW 2012+ added another md5 salt to the 2 hashes to strengthen security. VI-Explorer-VI (Github) has a useful tool to brute force the salt and reset the VI Password hashes.

BDPW (384 bytes)

Bytes| Parameter   // Description
----------------------------------
   16 | Password   // U128 md5 VI Password
   16 | Hash1      // U128 md5 hash 1
   16 | Hash2      // U128 md5 hash 2

Chunk BDPW

Download VI (LV2021)

Top | Chunks

BDSE

CONP

Connector pane terminals.

CPC2

DTHP

FPEx

FPHb

Front Panel objects binary tree.

FPSE

FTAB

HIST

VI History

LIbd

LIBN

Owning LabVIEW libraries (*.lvlib, *.lvclass). This chunk does not exist if the VI is not part of an owning library.

LIBN (x bytes)

Bytes| Parameter      // Description
----------------------------------
   4 | Array Size     // I32 array size
// For each element
   1 | String Length  // U8 string length
   n | Library Name   // String library name

Chunk LIBN

Download VI (LV2021)

Top | Chunks

LIfp

LIvi

LVSR

LabVIEW Source chunk. Contains the U32 saved version (See vers), file flags and the library/VI password as a md5 checksum.

LVSR (112 bytes)

Bytes| Parameter   // Description
----------------------------------
   4 | Version     // U32 Version
                   //   U8 major (x/16*10+x%16)
                   //   U4 minor
                   //   U4 fix
                   //   U8 stage (0x20:Dev, 0x40: Alpha, 0x60: Beta, 0x80: Release)
                   //   U8 build (x/16*10+x%16)
   2 | Flags       // U16 (Purpose Unknown)
   2 | FileFlags   // U16 file flags
                   //   0x2000 - Password protected
  ... Goto 96 byte offset ...
  16 | LibPassword // U128 (@96) md5 VI password

Chunk LVSR

Download VI (LV2021)

Top | Chunks

MUID

RTSG

VCTP

VITS

Persistent VI Tags as an array of name and data clusters [{'name':string, 'data':string}]. The data parameters are flattened variants. First unflatten the data to a string then to the variant.

VITS (x bytes)

Bytes| Parameter    // Description
----------------------------------
   4 | Array Size   // I32 array size
For each element:
 4+n | Tag Name     // I32+STR tag name string
 4+n | Tag Data     // I32+STR tag flattened data string

Chunk VITS

Download VI (LV2021)

VI Tags are accessable through private invoke nodes, add SuperSecretPrivateSpecialStuff=true to the LabVIEW.ini configuration file to enable the hidden Tag invoke & property nodes.

Invoke Node > VI > Tags > Get Tag

VI Tag API

Top | Chunks

Tag: NI_IconEditor

After the rework of the Icon Editor, the Icon Editor data is stored as a persistent VI Tag named NI_IconEditor. The data is stored as:

NI_IconEditor (x bytes)

'Version': String = '21.0\r\n",
'IconVersion': U8 = 0
    if 0:
        `Load & Unload.lvclass': StringFlattened
    if 1:
        [{
            'Layer Name': String,
            'Color Image': {<ImageData.ctl>},
            'B/W Image': {<ImageData.ctl>}
        }],
        `Load & Unload.lvclass': StringFlattened
    if 2:
        'Load & Unload.lvclass': StringFlattened,
        {
            'Layer Name':String,
            'Color Image':{<ImageData.ctl>},
            'B/W Image':{<ImageData.ctl>}
        }

VI Tag NI_IconEditor

The Icon Editor API Load & Unload.lvclass is located at: <LabVIEW>\vi.lib\LabVIEW Icon API\lv_icon\Classes\Load_Unload\Load & Unload.lvclass

Refer to my other post on how to parse the Icon Editor data.

Top | Chunks

Tag: NI.LV.All.SourceOnly

Boolean tag, if true Separate Compiled Code, otherwise compiled code is embedded in the VI.

VPDP

icI4

Icon 16 image bitmap (32 x 32 pixels at 4-bit image).

icI4 (512 bytes)

Bytes| Parameter    // Description
----------------------------------
512 | Image Data    // U8[] 4-bits per pixel (32 * 32 * 4 / 8 = 512)

Chunk icI4

Download VI (LV2021)

The equivalent invoke node: VI > Icon > Get Image Data

VI Icon Get Image

Top | Chunks

icI8

Icon 256 image bitmap (32 x 32 pixels at 8-bit image).

icI8 (1024 bytes)

Bytes| Parameter    // Description
----------------------------------
1024 | Image Data   // U8[] 8-bits per pixel (32 * 32 = 1024)

Chunk icI8

Download VI (LV2021)

The equivalent invoke node: VI > Icon > Get Image Data

VI Icon Get Image

Top | Chunks

ICON

Icon B/W image bitmap (32 x 32 pixels at 1-bit image).

ICON (128 bytes)

Bytes| Parameter    // Description
----------------------------------
128 | Image Data    // U8[] 1-bits per pixel (32 * 32 / 8 = 128)

Chunk ICON

Download VI (LV2021)

The equivalent invoke node: VI > Icon > Get Image Data

VI Icon Get Image

Top | Chunks

vers

VI version information.

vers (128 bytes)

Bytes| Parameter   // Description
----------------------------------
   4 | Version     // U32 Version
                   //   U8 major (x/16*10+x%16)
                   //   U4 minor
                   //   U4 fix
                   //   U8 stage (0x20:Dev, 0x40: Alpha, 0x60: Beta, 0x80: Release)
                   //   U8 build (x/16*10+x%16)
   2 | Flags       // U16 (Purpose Unknown)
   1 | VerStrLen   // U8 version string length
   n | VerStr      // Str version as string
   1 | VerInfoLen  // U8 version info length
   n | VerInfo     // Str version info as string

Chunk vers

Download VI (LV2021)

The equivalent property node: VI > Icon > Saved Version

VI Get Saved Version

Top | Chunks