Rudimentary Binary File Reader

Enables you to peek inside binary files to to see what is inside of them. Useful if, e.g., you are making your own file loader and need to read headers and find offsets and see what kind of data is in the file.
#pragma rtGlobals=1     // Use modern global access method.
#pragma IgorVersion=5
#pragma IndependentModule=BinaryReader
//BinaryReader helps you peek into binary files on disk to see what is inside of them.
// Useful if, e.g.,  you are making your own file loader and need to read headers and find offsets and see what kind of data is in the file.

//modified Nov 19 2007 // made independent module

Constant kSegmentSize = 10000 //how many bytes of the file we are going to show at one time

menu "Data"
    Submenu "packages"
    "Binary File Reader", BinaryReader# BinaryFileReader ()
    end
end

//*****************************************************************************************************
//make the packages folder and the global variables and the control panel
Function BinaryFileReader ()
   
    if (!(datafolderexists ("root:packages:")))
        newdatafolder root:packages
    endif
    if (!(datafolderexists ("root:packages:BinaryReader")))
        newdatafolder root:packages:BinaryReader
        //make waves for the list box showing the first segment of data in the selected file
        make/o/t/n =  (kSegmentSize,2) root:packages:BinaryReader:FileListWave
        make/o/n = (kSegmentSize,2) root:packages:BinaryReader:FileListSelWave
        //Make a text wave for the listbox that shows the data after being loaded according to the chosen specs
        make/t/o/n=(1,2) root:packages:BinaryReader:OutPutWave
        //make a temporary wave to read data into before displaying it in the listbox
        make/o/n = (kSegmentSize) root:packages:BinaryReader:TempWave
        //First row of waves will show byte offset
        SetDimLabel 1,0,BytePos,root:packages:BinaryReader:FileListWave
        SetDimLabel 1,0,BytePos root:packages:BinaryReader:OutPutWave
        //Second row will show the data as a character
        SetDimLabel 1,1,Char,root:packages:BinaryReader:FileListWave
        //a global string to contain a list of segments in the file for loading
        string/G root:packages:BinaryReader:SegmentsList = ""
        //global variable to hold reference nmber to the open file
        variable/G root:packages:BinaryReader:BRrefNum = nan
        //global string to display name of open file in a title box
        string/G root:packages:BinaryReader:fileNameStr = ""
    endif
    //try to bring panel to the front
    doWindow/F Binary_Reader
    if (V_Flag)
        return 1
    endif
    //make the panel, as it was not already open
    NewPanel /K=1 /W=(78,105,534,452) as "Binary Reader"   
    DoWindow/C Binary_Reader
    ListBox FileAsByteList,pos={215,25},size={101,319}
    ListBox FileAsByteList,help={"Shows the selected segment of data in the selected file as characters"}
    ListBox FileAsByteList,listWave=root:packages:BinaryReader:FileListWave
    ListBox FileAsByteList,selWave=root:packages:BinaryReader:FileListSelWave
    ListBox FileAsByteList,mode= 3,widths={20,13}
    Button OpenFileButton,pos={7,2},size={67,22},proc=BRopenFIleProc,title="Open File"
    TitleBox FileNameTitle,pos={79,3},size={383,20}
    TitleBox FileNameTitle,variable= root:packages:BinaryReader:fileNameStr
    PopupMenu SegmentsPopup,pos={7,31},size={99,20},proc=BRLoadSegmentProc,title="Load Segment"
    PopupMenu SegmentsPopup,mode=1,popvalue="0",value= #"root:packages:BinaryReader:SegmentsList"
    PopupMenu EndianPopUp,pos={11,121},size={178,20},title="Byte Order"
    PopupMenu EndianPopUp,mode=1,popvalue="Big-endian (Mac)",value= #"\"Big-endian (Mac);Little-endian (Win)\""
    PopupMenu UnSignedPopup,pos={9,96},size={134,20},title="Integer Data is"
    PopupMenu UnSignedPopup,mode=1,popvalue="Signed",value= #"\"Signed;UnSigned\""
    PopupMenu DataFormatPopUp,pos={10,72},size={140,20},title="Data Format"
    PopupMenu DataFormatPopUp,mode=1,popvalue="1 byte int",value= #"\"1 byte int;2 byte int (word);4 byte int;4 byte float;8 byte float (double)\""
    Button ShowSelectionButton,pos={39,149},size={101,27},proc=BRShowSelection,title="Show Selection"
    ListBox OutPutList,pos={323,27},size={129,315}
    ListBox OutPutList,listWave=root:packages:BinaryReader:OutPutWave
    SetWindow kwTopWin,hook=BRCloseHook
EndMacro

//*****************************************************************************************************
//Close the open file when the Binary_Reader panel is closed and also kill the packages folder
Function BRCloseHook (infoStr)
    String InfoStr
   
    if ((cmpstr (stringByKey ("EVENT", infoStr), "kill")) == 0)
        NVAR BRrefNum = root:packages:BinaryReader:BRrefNum
        variable LocalRefNum = BRrefNum
        FStatus LocalRefNum
        if (V_Flag)
            Close LocalRefNum
        endif
        killdatafolder/Z root:packages:BinaryReader
    endif
end
       
//*****************************************************************************************************
//Open a file on disk, save the refernce number in a global variable, and load the first segment of the file
Function BRopenFIleProc(ctrlName) : ButtonControl
    String ctrlName
   
    //make reference to global variables
    SVAR fileNameStr =root:packages:binaryReader:fileNameStr
    NVAR BRrefNum = root:packages:BinaryReader:BRrefNum
    variable localRefNum = BRrefNum
    SVAR segmentsList = root:packages:BinaryReader:SegmentsList
    segmentsList = ""
    WAVE/T fileListWave = root:packages:BinaryReader:FileListWave
    WAVE fileListSelWave = root:packages:BinaryReader:FileListSelWave
    WAVE tempwave = root:packages:BinaryReader:tempwave
    //close the file that was previously open, if any
    FStatus localRefNum
    if (V_Flag)
        Close localRefNum
        BRrefNum = Nan
    endif
    //Open a new file chosen by the user and save the refnum in the global variable
    Open /M="Choose a file to examine"/R localRefNum
    fileNameStr = S_fileName
    if ((cmpstr (S_fileName, "")) == 0)
        BRrefNum = nan
        fileListWave [] [1] = ""
        return 0
    else
        BRrefNum = localRefNum
    endif
    //Find the number of segments in the file, and make the list of segments used in the popmenu
    FStatus localRefNum
    variable ii, numSegments = ceil (V_logEOF/kSegmentSize)
    for (ii = 0; ii < numSegments; ii += 1)
        segmentsList += num2str (ii) + ";"
    endfor
    //Load the first Segment in the file
    BRLoadSegmentProc("",1,"")
    PopupMenu SegmentsPopup mode=1 
End

//*****************************************************************************************************
//Show the data selected in the file listbox in the output list box by loading the data from the file with the selected options
Function BRShowSelection(ctrlName) : ButtonControl
    String ctrlName
   
    //Make references to globals
    NVAR BRrefNum = root:packages:BinaryReader:BRrefNum
    variable localRefNum = BRrefNum
    WAVE/T fileListWave = root:packages:BinaryReader:FileListWave
    WAVE fileListSelWave = root:packages:BinaryReader:FileListSelWave
    WAVE tempwave = root:packages:BinaryReader:tempwave
    WAVE/T outputWave = root:packages:BinaryReader:OutPutWave
    //make sure file reference is valid
    FStatus localRefNum
    if (!(V_Flag))
        SVAR FileNameStr = root:packages:BinaryReader:FileNameStr
        doalert 0, "The selected file, " + FileNameStr +", is not open."
        FileNameStr = ""
        return 1
    endif
    //find the start and end of the selection in the file listbox
    variable ii, startpos =0,numBytes=0
    for (ii = 0; ii < kSegmentSize; ii += 1)
        if (fileListSelWave [ii] [0] == 1)
            break
        endif
    endfor
    StartPos = ii
    for (;ii < kSegmentSize; ii += 1)
        if (fileListSelWave [ii] [0] == 0)
            break
        endif
    endfor
    numBytes = (ii - StartPos)
    //we will read the selected bit of the file into the temporary wave according to the choices on these panel popups
    controlinfo DataFormatPopUp
    variable DataFormat = V_Value   //choices in menu listed in same order as for /F= option in FBinRead command
    controlinfo UnSignedPopup
    variable UnSigned = V_Value -1 //0 for Signed (default in FBInRead) 1 for unsigned (requires/U option)
    controlinfo EndianPopUp
    variable byteOrder = V_Value + 1 //byteorder will be 2 for bigendian, 3 for small endian to specify endian in FBinRead command
    //set the numtype variable according to Wavemetrics conventions
    variable theNumType = 0
    variable dataBytes //this variable will hold the number of bytes per data point
    if ((DataFormat < 4) && (UnSigned))
        theNumType += 64
    endif
    switch (DataFormat)
        case 1: //byte
            theNumType += 8
            dataBytes = 1
            SetDimLabel 1,1,Byte,root:packages:BinaryReader:OutPutWave
            break
        case 2: // 2 byte word
            theNumtype += 16
            numbytes =floor(numBytes/2)
            dataBytes = 2
            SetDimLabel 1,1,Word,root:packages:BinaryReader:OutPutWave
            break
        case 3: // 32 bit int
            theNumtype += 32
            numBytes = floor(numBytes/4)
            dataBytes = 4
            SetDimLabel 1,1,LongInt,root:packages:BinaryReader:OutPutWave
            break
        case 4: //32 bit float
            theNumType += 2
            numBytes= floor(numBytes/4)
            dataBytes = 4
            SetDimLabel 1,1,Float,root:packages:BinaryReader:OutPutWave
            break
        case 5: //64 bit floating point
            theNumType += 4
            numBytes=floor(numBytes/8)
            databytes = 8
            SetDimLabel 1,1,Double,root:packages:BinaryReader:OutPutWave
            break
        default:
            doalert 0, "uh oh, the Numtype Switch did not recognize the value, " + num2str (DataFormat) + "."
            return 1
            break
    endswitch
    //check that data is selected
    if (numBytes == 0)
        doalert 0, "First select part of the file to show. For multibyte data formats, you need to select enough bytes to show at least one point"
        return 1
    endif
    //Redimension the temp wave for the amount and kind of data expected
    Redimension/N = (numBytes)/Y=(theNumType) TempWave
    Redimension/N = ((numBytes),2)  OutPutWave
    //load the selected data into the temp wave
    fsetpos localRefNum, (str2num (fileListWave [StartPos] [0]))
    if (UnSigned)
        FBinRead /B=(byteOrder)/F=(DataFormat) localRefNum, TempWave
    else
        FBinRead /B=(byteOrder)/F=(DataFormat)/U localRefNum, TempWave
    endif
    //Show the data in the output wave
    OutPutWave [] [0] = num2Str (str2Num (fileListWave [StartPos] [0])+ (p * databytes))
    OutPutWave [] [1]= num2str (tempwave [p])
End

//*****************************************************************************************************
//Load the segment selected from the popup menu
Function BRLoadSegmentProc(ctrlName,popNum,popStr) : PopupMenuControl
    String ctrlName
    Variable popNum
    String popStr
   
    //References to globals
    SVAR fileNameStr =root:packages:binaryReader:fileNameStr
    NVAR BRrefNum = root:packages:BinaryReader:BRrefNum
    variable localRefNum = BRrefNum
    WAVE/T fileListWave = root:packages:BinaryReader:FileListWave
    WAVE FileListSelWave = root:packages:BinaryReader:FileListSelWave
    WAVE tempwave = root:packages:BinaryReader:tempwave
    popnum -= 1 //should be 0 based
    //check that the file reference is valid
    fstatus localRefNum
    if (!(V_Flag))
        doAlert 0, "The file, " + fileNameStr + " is not open."
        FileNameStr = ""
    endif
    //load the segment into the temp wave. If it is last segment, only load to the end of the file
    if (V_logEOF < ((popNum+1) * kSegmentSize))
        redimension/n = (V_logEOF-(popNum * kSegmentSize)) tempwave
        redimension/n = ((V_logEOF-(popNum * kSegmentSize)), 2) fileListWave, fileListSelWave
    else
        redimension/n = (kSegmentSize) tempwave
        redimension/n = ((kSegmentSize), 2) fileListWave, fileListSelWave
    endif
    FSetPos localRefNum, (popNum * kSegmentSize)
    FBinRead/f=1/u localRefNum, tempwave
    //show the loaded segment in the fileListBox in char format. Print out the most common escape codes
    variable ii, points = numpnts (tempwave)
    for (ii=0;ii< points;ii+=1)
        switch (tempWave [ii])
            case 0:
                fileListWave [ii] [1] = "NUL"
                break
            case 9:
                fileListWave [ii] [1] = "HorTab"
                break
            case 10:
                fileListWave [ii] [1] = "LineFeed"
                break
            case 11:
                fileListWave [ii] [1] = "VerTab"
                break
            case 12:
                fileListWave [ii] [1] = "FormFeed"
                break
            case 13:
                fileListWave [ii] [1] = "Return"
                break
            default:
                if (tempwave  [ii]  < 32)
                    fileListWave [ii] [1] = "Other Control"
                else
                    fileListWave [ii] [1] =num2char ( tempwave [ii])
                endif
                break
        endswitch
    endfor
    FileListWave [] [0] = num2Str((popNum * kSegmentSize)+ p)
End

Forum

Support

Gallery

Igor Pro 8

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More