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 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More