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.
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
- Read the
RSRC_HEADER1
to determine the byte offset toRSRC_HEADER2
- Read
RSRC_HEADER2
and verify thatRSRC_HEADER1
andRSRC_HEADER2
match (if not the file is corrupt) - Next read
RSRC_INFO
to determine the byte offset toCHUNK_CNT
- For each chunk
- Read the
CHUNK_ID
to get the chunk name andCHUNK_INFO
byte offset - Read the
CHUNK_INFO
to determine the byte offset toCHUNK_DATA
- Return the chunk name +
CHUNK_DATA
flattened data string
- Read the
- Map each chunk name to data
Chunk Data ‘LVPS’ 0x FF..
‘ICON’ 0x FF..
‘FPHb’ 0x FF..
‘BDHb’ 0x FF..
- Unflatten each Chunk into LabVIEW Data Types
- See Chunks
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.
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)
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.
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)
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[]
.
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'
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)
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)
.
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
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
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
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
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
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>}
}
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.
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)
The equivalent invoke node: VI > Icon > Get Image Data
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)
The equivalent invoke node: VI > Icon > Get Image Data
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)
The equivalent invoke node: VI > Icon > Get Image Data
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
The equivalent property node: VI > Icon > Saved Version