int main(int argc,char* argv[])
	{
	/* Open the input file: */
	IO::FilePtr inputDeviceDataFile(IO::openFile(argv[1]));
	inputDeviceDataFile->setEndianness(Misc::LittleEndian);
	
	/* Read file header: */
	int numInputDevices=inputDeviceDataFile->read<int>();
	Vrui::InputDevice** inputDevices=new Vrui::InputDevice*[numInputDevices];
	
	/* Initialize devices: */
	for(int i=0;i<numInputDevices;++i)
		{
		/* Read device's layout from file: */
		DeviceFileHeader header;
		inputDeviceDataFile->read(header.name,sizeof(header.name));
		inputDeviceDataFile->read(header.trackType);
		inputDeviceDataFile->read(header.numButtons);
		inputDeviceDataFile->read(header.numValuators);
		inputDeviceDataFile->read(header.deviceRayDirection.getComponents(),3);
		
		/* Create new input device: */
		Vrui::InputDevice* newDevice=new Vrui::InputDevice;
		newDevice->set(header.name,header.trackType,header.numButtons,header.numValuators);
		newDevice->setDeviceRayDirection(header.deviceRayDirection);
		
		/* Store the input device: */
		inputDevices[i]=newDevice;
		}
	
	/* Read all data frames from the input device data file: */
	while(true)
		{
		/* Read the next time stamp: */
		double timeStamp;
		try
			{
			timeStamp=inputDeviceDataFile->read<double>();
			}
		catch(IO::File::ReadError)
			{
			/* At end of file */
			break;
			}
		
		std::cout<<"Time stamp: "<<std::fixed<<std::setw(8)<<std::setprecision(3)<<timeStamp;
		
		/* Read data for all input devices: */
		for(int device=0;device<numInputDevices;++device)
			{
			/* Update tracker state: */
			if(inputDevices[device]->getTrackType()!=Vrui::InputDevice::TRACK_NONE)
				{
				Vrui::TrackerState::Vector translation;
				inputDeviceDataFile->read(translation.getComponents(),3);
				Vrui::Scalar quat[4];
				inputDeviceDataFile->read(quat,4);
				inputDevices[device]->setTransformation(Vrui::TrackerState(translation,Vrui::TrackerState::Rotation(quat)));
				}
			
			/* Update button states: */
			for(int i=0;i<inputDevices[device]->getNumButtons();++i)
				{
				int buttonState=inputDeviceDataFile->read<int>();
				inputDevices[device]->setButtonState(i,buttonState);
				}
			
			/* Update valuator states: */
			for(int i=0;i<inputDevices[device]->getNumValuators();++i)
				{
				double valuatorState=inputDeviceDataFile->read<double>();
				inputDevices[device]->setValuator(i,valuatorState);
				}
			}
		
		std::cout<<std::endl;
		}
	
	return 0;
	}
int main(int argc,char* argv[])
	{
	/* Open the input file: */
	IO::SeekableFilePtr inputDeviceDataFile(IO::openSeekableFile(argv[1]));
	inputDeviceDataFile->setEndianness(Misc::LittleEndian);
	
	/* Read the file header: */
	static const char* fileHeader="Vrui Input Device Data File v3.0\n";
	char header[34];
	inputDeviceDataFile->read<char>(header,34);
	header[33]='\0';
	
	int fileVersion;
	if(strncmp(header,fileHeader,29)!=0)
		{
		/* Pre-versioning file version: */
		fileVersion=1;
		
		/* Old file format doesn't have the header text: */
		inputDeviceDataFile->setReadPosAbs(0);
		}
	else if(strcmp(header+29,"2.0\n")==0)
		{
		/* File version without ray direction and velocities: */
		fileVersion=2;
		}
	else if(strcmp(header+29,"3.0\n")==0)
		{
		/* File version with ray direction and velocities: */
		fileVersion=3;
		}
	else
		{
		header[32]='\0';
		std::cerr<<"Unsupported input device data file version "<<header+29<<std::endl;
		return 1;
		}
	
	/* Skip random seed value: */
	inputDeviceDataFile->read<unsigned int>();
	
	/* Read file header: */
	int numInputDevices=inputDeviceDataFile->read<int>();
	Vrui::InputDevice** inputDevices=new Vrui::InputDevice*[numInputDevices];
	int* deviceFeatureBaseIndices=new int[numInputDevices];
	std::vector<std::string> deviceFeatureNames;
	
	/* Initialize devices: */
	for(int i=0;i<numInputDevices;++i)
		{
		/* Read device's name and layout from file: */
		std::string name;
		if(fileVersion>=2)
			name=Misc::readCppString(*inputDeviceDataFile);
		else
			{
			/* Read a fixed-size string: */
			char nameBuffer[40];
			inputDeviceDataFile->read(nameBuffer,sizeof(nameBuffer));
			name=nameBuffer;
			}
		int trackType=inputDeviceDataFile->read<int>();
		int numButtons=inputDeviceDataFile->read<int>();
		int numValuators=inputDeviceDataFile->read<int>();
		
		/* Create new input device: */
		Vrui::InputDevice* newDevice=new Vrui::InputDevice;
		newDevice->set(name.c_str(),trackType,numButtons,numValuators);
		
		if(fileVersion<3)
			{
			Vrui::Vector deviceRayDirection;
			inputDeviceDataFile->read(deviceRayDirection.getComponents(),3);
			newDevice->setDeviceRay(deviceRayDirection,Vrui::Scalar(0));
			}
		
		/* Store the input device: */
		inputDevices[i]=newDevice;
		
		/* Read or create the device's feature names: */
		deviceFeatureBaseIndices[i]=int(deviceFeatureNames.size());
		if(fileVersion>=2)
			{
			/* Read feature names from file: */
			for(int j=0;j<newDevice->getNumFeatures();++j)
				deviceFeatureNames.push_back(Misc::readCppString(*inputDeviceDataFile));
			}
		else
			{
			/* Create default feature names: */
			for(int j=0;j<newDevice->getNumFeatures();++j)
				deviceFeatureNames.push_back(getDefaultFeatureName(Vrui::InputDeviceFeature(newDevice,j)));
			}
		}
	
	/* Read all data frames from the input device data file: */
	while(true)
		{
		/* Read the next time stamp: */
		double timeStamp;
		try
			{
			timeStamp=inputDeviceDataFile->read<double>();
			}
		catch(IO::File::ReadError)
			{
			/* At end of file */
			break;
			}
		
		std::cout<<"Time stamp: "<<std::fixed<<std::setw(8)<<std::setprecision(3)<<timeStamp;
		
		/* Read data for all input devices: */
		for(int device=0;device<numInputDevices;++device)
			{
			/* Update tracker state: */
			if(inputDevices[device]->getTrackType()!=Vrui::InputDevice::TRACK_NONE)
				{
				if(fileVersion>=3)
					{
					Vrui::Vector deviceRayDir;
					inputDeviceDataFile->read(deviceRayDir.getComponents(),3);
					Vrui::Scalar deviceRayStart=inputDeviceDataFile->read<Vrui::Scalar>();
					inputDevices[device]->setDeviceRay(deviceRayDir,deviceRayStart);
					}
				Vrui::TrackerState::Vector translation;
				inputDeviceDataFile->read(translation.getComponents(),3);
				Vrui::Scalar quat[4];
				inputDeviceDataFile->read(quat,4);
				inputDevices[device]->setTransformation(Vrui::TrackerState(translation,Vrui::TrackerState::Rotation(quat)));
				if(fileVersion>=3)
					{
					Vrui::Vector linearVelocity,angularVelocity;
					inputDeviceDataFile->read(linearVelocity.getComponents(),3);
					inputDeviceDataFile->read(angularVelocity.getComponents(),3);
					inputDevices[device]->setLinearVelocity(linearVelocity);
					inputDevices[device]->setAngularVelocity(angularVelocity);
					}
				}
			
			/* Update button states: */
			if(fileVersion>=3)
				{
				unsigned char buttonBits=0x00U;
				int numBits=0;
				for(int i=0;i<inputDevices[device]->getNumButtons();++i)
					{
					if(numBits==0)
						{
						buttonBits=inputDeviceDataFile->read<unsigned char>();
						numBits=8;
						}
					inputDevices[device]->setButtonState(i,(buttonBits&0x80U)!=0x00U);
					buttonBits<<=1;
					--numBits;
					}
				}
			else
				{
				for(int i=0;i<inputDevices[device]->getNumButtons();++i)
					{
					int buttonState=inputDeviceDataFile->read<int>();
					inputDevices[device]->setButtonState(i,buttonState);
					}
				}
			
			/* Update valuator states: */
			for(int i=0;i<inputDevices[device]->getNumValuators();++i)
				{
				double valuatorState=inputDeviceDataFile->read<double>();
				inputDevices[device]->setValuator(i,valuatorState);
				}
			}
		
		std::cout<<std::endl;
		}
	
	return 0;
	}