Vehicle Spy VSB file Spec

File Specification

Description Length in Bytes Notes
Text Identifier 6 single byte char array with text "icsbin"used to identify file type.
File Version 4 single byte char array indicate version.
Version Section Variable (see below) This data structure is determinant the File Version

Version Specification

Version 0x101

This format is no longer created. Click to see Network ID's

Description Length in Bytes Notes
Length of Vs3 File 4 int value indicating the length of the next section (the vs3 file). A zero indicates no vs3 file is present.
Vs3 file variable (see above) vs3 file used to save this binary file. This vs3 file is later used to decode the binary data into usable information.
Length of text comment 4 int indicating the length of the text header comment of the saved file. SINCE THE COMMENT IS UNICODE THEREFORE HIS IS THE LENGTH IN CHARACTERS - NOT BYTES.
Text comment 2 bytes per character (see above for number of characters) Unicode text comment.
Size of Buffer of Messages 4 sizeof(VSBSpyMessage) multiplied by Number of messages saved to the file
Current Buffer Pointer 4 int value which is the pointer to the most recent buffer item +1. (only needed if Number of All time messages > Original buffer size)
Original Buffer Size 4 int value which is the size of the buffer memory originally allocated by this buffer
Number of All time messages 4 this indicates how many messages were received by this buffer (this number will indicate overflows)
Buffer of Messages variable (see Size of Buffer of Messages) VSBSpyMessage structures
Start Time of Collection struct icsspyMsgTime this is a comparison value between the system time stamp and the neoVI time stamp.

Version 0x102

This format is created by the extractor. Click to see Network ID's

Description Length in Bytes Notes
Length of EDP Section 4 int value indicating the length of the next section (the EDP). A zero indicates no EDP section file is present.
EDP Section variable (see above) used to save the extra data bytes for networks such as Ethernet, Flexray, and CANFD
Buffer of Messages variable (see Size of Buffer of Messages) VSBSpyMessage structures
Start Time of Collection struct icsspyMsgTime this is a comparison value between the system time stamp and the neoVI time stamp.

Version 0x103

This format is created using VSpy. Click to see Network ID's

Description Length in Bytes Notes
Length of EDP Section 4 int value indicating the length of the next section (the EDP). A zero indicates no EDP section is present.
EDP Section variable (see above) used to save the extra data bytes for networks such as Ethernet, Flexray, and CANFD
Length of text comment 4 int indicating the length of the text header comment of the saved file. SINCE THE COMMENT IS UNICODE THEREFORE HIS IS THE LENGTH IN CHARACTERS - NOT BYTES.
Text comment 2 bytes per character (see above for number of characters) Unicode text comment.
Size of Buffer of Messages 4 sizeof(VSBSpyMessage) multiplied by Number of messages saved to the file
Current Buffer Pointer 4 int value which is the pointer to the most recent buffer item +1. (only needed if Number of All time messages > Original buffer size)
Original Buffer Size 4 int value which is the size of the buffer memory originally allocated by this buffer
Number of All time messages 4 this indicates how many messages were received by this buffer (this number will indicate overflows)
Buffer of Messages variable (see Size of Buffer of Messages) VSBSpyMessage structures
Start Time of Collection struct icsspyMsgTime this is a comparison value between the system time stamp and the neoVI time stamp.

EDP section

Used by version 0x102 and 0x103. The ExtraDataPtr -1 of each message is a index to the EDP in EDP section. Click to see Network ID's

Description Length in Bytes Notes
Text identifier 12 single byte char array with text "EDP_SECTION" used to identify file type. please note the null terminator at the end
EDP Size 4 int indicating the length of the EDP
EDP variable (see above) used to save the extra data bytes for networks such as Ethernet, Flexray, and CANFD
...... variable (see above) EDP Size and EDP continue to repeat until Length of EDP Section is met.

Version 0x104

This format is created by the Loggers and VSpy Stream to Disk.

Description Length in Bytes Notes
Buffer of Messages VSBSpyMessage structures VSBSpyMessage structures
EDP variable (see previous message's ExtraDataPtr) used to save the extra data bytes for networks such as Ethernet, Flexray, and CANFD
...... variable Buffer of Message and EDP continue to repeat until Start Time of Collection. If you are reading the VSB file, simple continue to read until Buffer of Message size read not equal sizeof(VSBSpyMessage)
Start Time of Collection struct icsspyMsgTime this is a comparison value between the system time stamp and the neoVI time stamp.

NetWork ID

version 0x101

Description Value
HSCAN 0
MSCAN 1
SWCAN 2
LSFTCAN 5
DEVICE 8
HSCAN2 9
HSCAN3 10
LIN2 11
LIN3 12
LIN4 13

version 0x102 , 0x103, 0x104

Description Value
DEVICE 0
HSCAN 1
MSCAN 2
SWCAN 3
LSFTCAN 4
J1708 6
JVPW 8
ISO 9
ISO2 14
LIN 16
ISO3 41
HSCAN2 42
HSCAN3 44
ISO4 47
LIN2 48
LIN3 49
LIN4 50
LIN5 84
MOST 51
CGI 53
HSCAN4 61
HSCAN5 62
SWCAN2 68
ETHERNET_DAQ 69
FLEXRAY1A 80
FLEXRAY1B 81
FLEXRAY2A 82
FLEXRAY2B 83
FLEXRAY 85
MOST25 90
MOST50 91
ETHERNET 93
GMFSA 94
TCP 95
HSCAN6 96
HSCAN7 97
LIN6 98
LSFTCAN2 99
OP_ETHERNET1 17
OP_ETHERNET2 18
OP_ETHERNET3 19
OP_ETHERNET4 45
OP_ETHERNET5 46
OP_ETHERNET6 73
OP_ETHERNET7 75
OP_ETHERNET8 76
OP_ETHERNET9 77
OP_ETHERNET10 78
OP_ETHERNET11 79
OP_ETHERNET12 87

Example

Below is a C++ example of how to open a .vsb file (v102 & v103 & 104) and display the network messages (Ethernet, CANFD, and CAN) with some other useful information You can view the full C++ code or Download the full project from the links provided.

Reading File Specification

bool Messages::Read(char * sFileName)
{
    unsigned long numEDP = 0;

    m_pFile = fopen(sFileName, "rb");
    if (m_pFile)
    {
        fSead( 0, SEEK_END);
        int length = ftell(m_pFile); //identify length of File 
        fSead( 0, SEEK_SET);

        char id[6];
        if (!fRead((char * )&id, 6) || memcmp(id, "icsbin", 6) != 0) //check for icsbin in the first 6 bytes to identify if it a VSB file
        {
            printf("Invalid VSB file\n");
            return false;
        }
        unsigned int fileversion;

      
        if (!fRead((char *)&fileversion, 4)){ //VSB has 4 versions 101 is no longer used 
            printf("Read Error\n");
            return false;
        }
        else if (fileversion == 0x102)//this is created by the extractor
        {
            int EDPSize = ReadEDPSelction(); //the extra data section contains extra data which is different for each message type
            if (EDPSize == 0)
                fSead( VSB_HEADERSIZE, SEEK_SET);
            unsigned long numofmessages = ((length - (EDPSize + SIZEOFEDPSIZE)) - VSB_HEADERSIZE) / sizeof(icsSpyMessage); //calculate number of messages in file; the plus 4
            m_nMessages =  numofmessages; // set number of messages in file
            if (numofmessages){
                if(!ReadMessage(numofmessages, fileversion)){ //read messages
                    printf("file was not read correctly \n");
                    return false;
                }
                
        }
        else if (fileversion == 0x103)//this is created by VSPY
        {
            ReadEDPSelction(); //read EDP
            unsigned long numofmessages = Read103Header(); //read 103 header
            m_nMessages = numofmessages; //set number of messages in file
            if (numofmessages){
                if(!ReadMessage(numofmessages, fileversion)){ //read messages
                    printf("file was not read correctly \n");
                    return false;
                }
            }
        }
        else if (fileversion == 0x104){//this is created by various hardware and VSPY for logging 
            if(!Read104()){ // 104 has a very different structure compared to the other file types
                printf("file was not read correctly \n");
                return false;
            }
        }
        else{
            printf("%s is an unsupported VSB version!\n", sFileName);
            return false;
        }
        fclose(m_pFile);
    }
    else
    {
        printf("Could not open %s to read!\n", sFileName);
        return false;
    }

    return true;
}

Reading EDP Section

int Messages::ReadEDPSelction()
{
    unsigned long  edpsize;	
    bool bFixTimeStamp = false;

    if (fRead((char *)&edpsize, SIZEOFEDPSIZE) && edpsize > 0)//read size of edp section
    {
		unsigned long tempEDPSize = edpsize;
        static const char* EDP_SECTION = "EDP_SECTION"; 
		char obItem [EDPSECTIONSTRINGSIZE];  

        // check if there's an EDP_SECTION in here
        if (fRead(obItem, EDPSECTIONSTRINGSIZE) && tempEDPSize >= EDPSECTIONSTRINGSIZE && strcmp(obItem, EDP_SECTION) == 0)//verify edp section
        {
			// the following lines of code is a bad idea for large files. but since it is an simple example I wanted to simplify the code normality you would want to use some sort of file stream. 

			tempEDPSize -= EDPSECTIONSTRINGSIZE;
			m_pEDPSection = new char[tempEDPSize];             
            
			
			if (!fRead(m_pEDPSection, tempEDPSize)){ //load edp section into memory
				printf("invalid file read\n");
				return 0;
			}

			char * edpPtr = m_pEDPSection;			
            while (tempEDPSize > 0)//parse edp section
            {
                int edpLen = *(int*)edpPtr;
                m_edps.push_back(std::pair<char*, int>(edpPtr + sizeof(int), edpLen));//each node in this vector pertains to a different message (this will make more sense when you look at Read Message )
                tempEDPSize -= sizeof(int);
                edpPtr += edpLen +sizeof(edpLen);			
                tempEDPSize -= edpLen;
            }
            return edpsize;
        }
    }
    return 0;
}

Reading 0x103 File Info

unsigned long Messages::Read103Header()
{
    unsigned long theNumOfMsgs;
    size_t msgslength, currbuffptr, numalltime, commentlength, origbuffsize;

    fRead((char *)&commentlength, 4); //check if there is a comment
    if (commentlength > 0)
        fSead( (long)commentlength * 2, SEEK_CUR); // skip comment (comment written in wchar_t)
    fRead((char *)&msgslength, 4);//size of message section 
    fRead((char *)&origbuffsize, 4); //original size of buffer
    fRead((char *)&currbuffptr, 4);  // current buffer postion 
    fRead((char *)&numalltime, 4); // number of message all time

    theNumOfMsgs = msgslength / sizeof(VSBSpyMessage);    // number of messages

    return theNumOfMsgs;
}

Reading 0x102 and 0x103 Messages

bool Messages::ReadMessage(unsigned long theNumOfMsgs, unsigned int fileversion)
{
    VSBSpyMessage * pMessageRead = new VSBSpyMessage();
	m_pMessages = new icsSpyMessage[theNumOfMsgs];
    bool firstMsg = true;
    SpyMsgTime timeStampRead;
    SpyMsgTime FirstMsgTime;

	for(unsigned int i = 0; i < theNumOfMsgs; i++){//read one message at a time
		if(!fRead((char *)pMessageRead, sizeof(VSBSpyMessage))){//read message
			delete pMessageRead;
			delete m_pMessages;
			m_pMessages = NULL;
			theNumOfMsgs = 0;
			return false;
		}

		if (firstMsg) {//record the start time of the first message
			FirstMsgTime.HardwareTimeStampID = m_pMessages[0].TimeStampHardwareID;
			FirstMsgTime.HardwareTime1 = m_pMessages[0].TimeHardware;
			FirstMsgTime.HardwareTime2 = m_pMessages[0].TimeHardware2;
			FirstMsgTime.SystemTimeStampID = m_pMessages[0].TimeStampSystemID;
			FirstMsgTime.SystemTime1 = m_pMessages[0].TimeSystem;
			FirstMsgTime.SystemTime2 = m_pMessages[0].TimeSystem2;
			firstMsg = false;
		}

		VSBMessageToNormal(&m_pMessages[i], pMessageRead);// convert structure 
		
		bool clear = true;
		if (pMessageRead->ExtraDataPtrEnabled && pMessageRead->iExtraDataPtr) // check for edp section
		{
			if (pMessageRead->iExtraDataPtr >= 1 && pMessageRead->iExtraDataPtr <= m_edps.size())
			{
				int iEDPIndex = pMessageRead->iExtraDataPtr - 1;// identify index in the m_edps vector we made in the ReadEDPSelction function
				int payloadLength = m_edps[iEDPIndex].second; 
				m_pMessages[i].pExtraDataPtr = new pExtraDataPtrHandler(); //using pExtraDataPtrHandler to simply make  to identify length of edp section
				((pExtraDataPtrHandler *)(m_pMessages[i].pExtraDataPtr))->pExtraDataPtr = m_edps[iEDPIndex].first;
				((pExtraDataPtrHandler *)(m_pMessages[i].pExtraDataPtr))->length = payloadLength;
				clear = false;
			}
		}
		if (clear)
		{
			m_pMessages[i].ColorID = 0; //reset values
		    m_pMessages[i].pExtraDataPtr = 0;  //reset values
		}
		if (fileversion == 0x102)//all 102s implicitly have bit 7 high. since we're converting to 103 we need to force it
			m_pMessages[i].TimeStampHardwareID |= 0x80;	
	}
  
    if (!fRead((char *)&timeStampRead, sizeof(timeStampRead))) //check if start logging time was recorded
        timeStampRead = FirstMsgTime; // if start logging time was not record simply set it to first message time
	m_startTimeStamp = timeStampRead;
	delete pMessageRead;
    return true;

}

Reading 0x104

bool Messages::Read104()  // 104 don't have a edp section they instead put the extra data after that message 
{
    unsigned long EDPLength = 0, Edpsize = 0, numOfMessages = 0;

	bool firstMsg = true;
    SpyMsgTime timeStampRead;
    SpyMsgTime FirstMsgTime;

    VSBSpyMessage *pMessage = new VSBSpyMessage();

	while (fRead((char *)pMessage, sizeof(VSBSpyMessage))) // read messages to identify number of message and edp size
    {
		 numOfMessages++;
		 if (pMessage->ExtraDataPtrEnabled && pMessage->iExtraDataPtr > 0)// as you can see we are using extraDataptr to identify the size of the edp section for that message
         {
			Edpsize +=  pMessage->iExtraDataPtr; 
            // The EDP is the length of the extra data following the message, skip it
			fSead((unsigned int)pMessage->iExtraDataPtr, SEEK_CUR); 
         }
	}	

	fSead( VSB_HEADERSIZE, SEEK_SET); //go back to the start
	//from here on out it is very similar to 103 and 102 but the location of the edp section is different 
	
	m_pMessages = new icsSpyMessage[numOfMessages];

    for (unsigned int i = 0; i < numOfMessages; i++)
    {
		if (!fRead((char *)pMessage, sizeof(VSBSpyMessage)))
            break; //error

		if (firstMsg) {
			FirstMsgTime.HardwareTimeStampID = pMessage[0].TimeStampHardwareID;
			FirstMsgTime.HardwareTime1 = pMessage[0].TimeHardware;
			FirstMsgTime.HardwareTime2 = pMessage[0].TimeHardware2;
			FirstMsgTime.SystemTimeStampID = pMessage[0].TimeStampSystemID;
			FirstMsgTime.SystemTime1 = pMessage[0].TimeSystem;
			FirstMsgTime.SystemTime2 = pMessage[0].TimeSystem2;
			firstMsg = false;
		}

        bool clear = true;

		VSBMessageToNormal(&m_pMessages[i], pMessage);

        if (pMessage->ExtraDataPtrEnabled &&  pMessage->iExtraDataPtr >= 1 ) {
            EDPLength = pMessage->iExtraDataPtr;
			char * pEDP = new char[EDPLength];
			if(!fRead(pEDP, EDPLength))
				return false; //error
			m_pMessages[i].pExtraDataPtr = new pExtraDataPtrHandler();
			((pExtraDataPtrHandler *)(m_pMessages[i].pExtraDataPtr))->pExtraDataPtr = (void *)pEDP;
			((pExtraDataPtrHandler *)(m_pMessages[i].pExtraDataPtr))->length = EDPLength;
            clear = false;
        }

        if (clear) {
            EDPLength = 0;
            m_pMessages[i].ColorID = 0;
			m_pMessages[i].pExtraDataPtr = 0;
        }
    }
		
    if (!fRead((char *)&timeStampRead, sizeof(timeStampRead)))
        timeStampRead = FirstMsgTime;
	m_startTimeStamp = timeStampRead;
    m_nMessages = numOfMessages;
    return true;
}