Rudimentary Binary File Reader
Posted November 13th, 2008 by jamie
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
