diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
index 3007fdc653165710f47d9b3368a9f4661e127421..0877874ee0e6397837441fe60ce3cbcfa3382892 100644
--- a/NIF_Blocks.cpp
+++ b/NIF_Blocks.cpp
@@ -64,7 +64,7 @@ ABlock::~ABlock() {
 	}
 }
 
-void ABlock::AddAttr( string type, string name ) {
+void ABlock::AddAttr( string type, string name, unsigned int first_ver, unsigned int last_ver ) {
 	IAttr * attr;
 	if ( type == "int" ) {
 		attr = new IntAttr( name, this );
@@ -78,11 +78,11 @@ void ABlock::AddAttr( string type, string name ) {
 		attr = new Float3Attr( name, this );
 	} else if ( type == "string" ) {
 		attr = new StringAttr( name, this );
-	} else if ( type == "index" ) {
-		attr = new IndexAttr( name, this );
+	} else if ( type == "link" ) {
+		attr = new LinkAttr( name, this );
 	} else if ( type == "flags" ) {
 		attr = new FlagsAttr( name, this );
-	} else if ( type == "matrix" ) {
+	} else if ( type == "matrix33" ) {
 		attr = new MatrixAttr( name, this );
 	} else if ( type == "linkgroup" ) {
 		attr = new LinkGroupAttr( name, this );
@@ -90,7 +90,7 @@ void ABlock::AddAttr( string type, string name ) {
 		attr = new BoneAttr( name, this );
 	} else if ( type == "bbox" ) {
 		attr = new BBoxAttr( name, this );
-	} else if ( type == "cint" ) {
+	} else if ( type == "condint" ) {
 		attr = new CIntAttr( name, this );
 	} else if ( type == "vertmode" ) {
 		attr = new VertModeAttr( name, this );
@@ -112,8 +112,10 @@ void ABlock::AddAttr( string type, string name ) {
 		attr = new AlphaFormatAttr( name, this );
 	} else if ( type == "nodeancestor" ) {
 		attr = new NodeAncestorAttr( name, this );
-	} else if ( type == "root" ) {
-		attr = new RootAttr( name, this );
+	} else if ( type == "skeletonroot" ) {
+		attr = new SkeletonRootAttr( name, this );
+	} else if ( type == "particlegroup" ) {
+		attr = new ParticleGroupAttr( name, this );
 	} else {
 		cout << type << endl;
 		throw runtime_error("Unknown attribute type requested.");
@@ -121,6 +123,7 @@ void ABlock::AddAttr( string type, string name ) {
 
 	_attr_map[name] = attr_ref(attr);
 	_attr_vect.push_back(attr_ref(attr));
+	_attr_vers.push_back( pair<unsigned int, unsigned int>(first_ver, last_ver) );
 }
 
 attr_ref ABlock::GetAttr(string attr_name) {
@@ -146,17 +149,16 @@ blk_ref ABlock::GetParent() {
 		return blk_ref(-1);
 }
 
-void ABlock::Read( ifstream& in ) {
+void ABlock::Read( ifstream& in, unsigned int version ) {
 	for (unsigned int i = 0; i < _attr_vect.size(); ++i ) {
-		_attr_vect[i]->Read( in );
-		//cout << "   " << _attr_vect[i]->GetName() << endl;
+		if ( version >= _attr_vers[i].first && version <= _attr_vers[i].second ) {
+			_attr_vect[i]->Read( in );
+			//cout << "   " << _attr_vect[i]->GetName() << ":  " << _attr_vect[i]->asString() << endl;
+		}
 	}
 }
 
-void ABlock::Write( ofstream& out ) {
-	//Write Block Type
-	WriteString( this->GetBlockType() , out );
-
+void ABlock::Write( ofstream& out, unsigned int version ) {
 	//Write Attributes
 	for (unsigned int i = 0; i < _attr_vect.size(); ++i ) {
 		//cout << "Writing " << blk_ref(this) << " " << _attr_vect[i]->GetName() << endl;
@@ -552,7 +554,7 @@ string NiNode::asString() {
 /**
  * NiTriShapeData::Read - Assumes block name has already been read from in
  */
-void NiTriShapeData::Read( ifstream& in ){
+void NiTriShapeData::Read( ifstream& in, unsigned int version ){
 
 	short vert_count = ReadUShort( in );
 	int hasVertices = ReadUInt( in );
@@ -731,9 +733,7 @@ string NiTriShapeData::asString() {
 /**
  * NiTriShapeData::Write - Writes block name to out, in addition to data.
  */
-void NiTriShapeData::Write( ofstream& out ){
-
-	WriteString( "NiTriShapeData", out );
+void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 
 	WriteUShort( short(vertices.size()), out );
 
@@ -888,7 +888,7 @@ void NiTriShapeData::SetTriangles( const vector<Triangle> & in ) {
  * NiSkinData methods
  **********************************************************/
 
-void NiSkinData::Read( ifstream& in ) {
+void NiSkinData::Read( ifstream& in, unsigned int version ) {
 	
 	for (int c = 0; c < 3; ++c) {
 		for (int r = 0; r < 3; ++r) {
@@ -919,12 +919,10 @@ void NiSkinData::Read( ifstream& in ) {
 	}
 }
 
-void NiSkinData::Write( ofstream& out ) {
+void NiSkinData::Write( ofstream& out, unsigned int version ) {
 	//Calculate offset matrices prior to writing data
 	CalculateBoneOffsets();
 
-	WriteString( "NiSkinData", out );
-
 	for (int c = 0; c < 3; ++c) {
 		for (int r = 0; r < 3; ++r) {
 			WriteFloat( rotation[r][c], out );
@@ -1404,7 +1402,7 @@ string NiGeomMorpherController::asString() {
  * NiKeyframeData methods
  **********************************************************/
 
-void NiKeyframeData::Read( ifstream& in ) {
+void NiKeyframeData::Read( ifstream& in, unsigned int version ) {
 
 	scaleType = rotationType = translationType = KeyType(0);
 
@@ -1493,7 +1491,6 @@ void NiKeyframeData::Read( ifstream& in ) {
 
 	if (numScalings > 0) {
 		scaleType = KeyType(ReadUInt( in ));
-		cout << "Scale Type:  " << scaleType << endl;
 
 		scaleKeys.resize( numScalings );
 		for ( unsigned int i = 0; i < numScalings; ++i ) {
@@ -1513,9 +1510,7 @@ void NiKeyframeData::Read( ifstream& in ) {
 	}
 }
 
-void NiKeyframeData::Write( ofstream& out ) {
-
-	WriteString( "NiKeyframeData", out );
+void NiKeyframeData::Write( ofstream& out, unsigned int version ) {
 
 	//--Rotation--//
 	WriteUInt( uint(rotKeys.size()), out );
@@ -1690,7 +1685,7 @@ string NiKeyframeData::asString() {
  * NiColorData methods
  **********************************************************/
 
-void NiColorData::Read( ifstream& in ) {
+void NiColorData::Read( ifstream& in, unsigned int version ) {
 	colorCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
@@ -1731,7 +1726,7 @@ string NiColorData::asString() {
  * NiFloatData methods
  **********************************************************/
 
-void NiFloatData::Read( ifstream& in ) {	
+void NiFloatData::Read( ifstream& in, unsigned int version ) {	
 	colorCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
@@ -1774,7 +1769,7 @@ string NiFloatData::asString() {
  * NiStringExtraData methods
  **********************************************************/
 
-void NiStringExtraData::Read( ifstream& in ) {
+void NiStringExtraData::Read( ifstream& in, unsigned int version ) {
 	GetAttr("Next Extra Data")->Read( in );
 
 	//Read Bytes Remaining but don't bother to store it
@@ -1783,8 +1778,7 @@ void NiStringExtraData::Read( ifstream& in ) {
 	GetAttr("String Data")->Read( in );
 }
 
-void NiStringExtraData::Write( ofstream& out ) {
-	WriteString( "NiStringExtraData", out );
+void NiStringExtraData::Write( ofstream& out, unsigned int version ) {
 
 	GetAttr("Next Extra Data")->Write( out );
 
@@ -1815,7 +1809,7 @@ string NiStringExtraData::asString() {
  * NiMorphData methods
  **********************************************************/
 
-void NiMorphData::Read( ifstream& in ) {
+void NiMorphData::Read( ifstream& in, unsigned int version ) {
 	uint morphCount = ReadUInt( in );
 	vertCount = ReadUInt( in );
 
@@ -1848,9 +1842,7 @@ void NiMorphData::Read( ifstream& in ) {
 	}
 }
 
-void NiMorphData::Write( ofstream& out ) {
-	WriteString( "NiMorphData", out );
-
+void NiMorphData::Write( ofstream& out, unsigned int version ) {
 	WriteUInt( uint(morphs.size()), out );
 	WriteUInt( vertCount, out );
 
@@ -1933,7 +1925,7 @@ void NiMorphData::SetMorphVerts( int n, const vector<Vector3> & in ) {
  * NiPixelData methods
  **********************************************************/
 
-void NiPixelData::Read( ifstream& in ) {
+void NiPixelData::Read( ifstream& in, unsigned int version ) {
 	unknownInt = ReadUInt( in );
 	rMask = ReadUInt( in );
 	gMask = ReadUInt( in );
@@ -1984,7 +1976,7 @@ string NiPixelData::asString() {
 	}
 	out << endl;
 
-	out << "Unknown Index:  " <<  GetAttr("Next Extra Data")->asLink() << endl
+	out << "Unknown Index:  " <<  GetAttr("Unknown Index")->asLink() << endl
 		<< "Mipmap Count:  " << mipCount << endl
 		<< "Bytes Per Pixel:  " << bytesPerPixel << endl;
 
@@ -2004,7 +1996,7 @@ string NiPixelData::asString() {
  * NiPosData methods
  **********************************************************/
 
-void NiPosData::Read( ifstream& in ) {
+void NiPosData::Read( ifstream& in, unsigned int version ) {
 	posCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
@@ -2053,7 +2045,7 @@ string NiPosData::asString() {
  * NiRotatingParticlesData methods
  **********************************************************/
 
-void NiRotatingParticlesData::Read( ifstream& in ) {
+void NiRotatingParticlesData::Read( ifstream& in, unsigned int version ) {
 
  //   short count1
 
@@ -2185,7 +2177,7 @@ void NiRotatingParticlesData::Read( ifstream& in ) {
  * NiTextKeyExtraData methods
  **********************************************************/
 
-void NiTextKeyExtraData::Read( ifstream& in ) {
+void NiTextKeyExtraData::Read( ifstream& in, unsigned int version ) {
 
 	GetAttr("Next Extra Data")->Read( in );
 	GetAttr("Unknown Int")->Read( in );
@@ -2199,9 +2191,7 @@ void NiTextKeyExtraData::Read( ifstream& in ) {
 	}
 }
 
-void NiTextKeyExtraData::Write( ofstream& out ) {
-
-	WriteString( "NiTextKeyExtraData", out );
+void NiTextKeyExtraData::Write( ofstream& out, unsigned int version ) {
 
 	GetAttr("Next Extra Data")->Write( out );
 	GetAttr("Unknown Int")->Write( out );
@@ -2239,7 +2229,7 @@ string NiTextKeyExtraData::asString() {
  * NiUVData methods
  **********************************************************/
 
-void NiUVData::Read( ifstream& in ) {	
+void NiUVData::Read( ifstream& in, unsigned int version ) {	
 	for (uint i = 0; i < 4; ++i) {
 		groups[i].count = ReadUInt( in );
 
@@ -2294,7 +2284,7 @@ string NiUVData::asString() {
  * NiVertWeightsExtraData methods
  **********************************************************/
  
-void NiVertWeightsExtraData ::Read( ifstream& in ) {
+void NiVertWeightsExtraData ::Read( ifstream& in, unsigned int version ) {
 	GetAttr("Next Extra Data")->Read( in );
 	bytes = ReadUInt( in );
 	verts = ReadUShort( in );
@@ -2329,7 +2319,7 @@ string NiVertWeightsExtraData::asString() {
  * NiVisData methods
  **********************************************************/
 
-void NiVisData ::Read( ifstream& in ) {
+void NiVisData ::Read( ifstream& in, unsigned int version ) {
 	visCount = ReadUInt( in );
 
 	keys.resize( visCount );
@@ -2361,7 +2351,7 @@ string NiVisData::asString() {
  * UnknownMixIn methods
  **********************************************************/
 
-void UnknownMixIn::Read( ifstream &in ) {
+void UnknownMixIn::Read( ifstream &in, unsigned int version ) {
 	len = BlockSearch(in);
 
 	//Create byte array and read in unknown block
@@ -2398,7 +2388,7 @@ string UnknownMixIn::asString() {
 	return out.str();
 }
 
-void UnknownMixIn::Write( ofstream& out ) {
+void UnknownMixIn::Write( ofstream& out, unsigned int version ) {
 	out.write( (const char*)data, len );
 }
 
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
index 37650fda02c0a4510a026b2a54660a8a010021ed..50e0b153db56cdf63f752de77d7033776e4355a6 100644
--- a/NIF_Blocks.h
+++ b/NIF_Blocks.h
@@ -71,15 +71,15 @@ public:
 	virtual void FixUpLinks( const vector<blk_ref> & blocks ) = 0;
 
 	//File I/O
-	virtual void Read( ifstream& in ) = 0;
-	virtual void Write( ofstream& out ) = 0;	
+	virtual void Read( ifstream& in, unsigned int version ) = 0;
+	virtual void Write( ofstream& out, unsigned int version ) = 0;	
 };
 
 class ABlock : public IBlock, public IBlockInternal {
 public:
 	ABlock();
 	~ABlock();
-	void AddAttr( string type, string name );
+	void AddAttr( string type, string name, unsigned int first_ver = 0, unsigned int last_ver = 0xFFFFFFFF );
 	attr_ref GetAttr(string attr_name);
 	vector<attr_ref> GetAttrs();
 	int GetBlockNum() { return _block_num; }
@@ -108,11 +108,12 @@ public:
 	void SetBlockNum( int n ) { _block_num = n; }
 	void FixUpLinks( const vector<blk_ref> & blocks );
 
-	void Read( ifstream& in );
-	void Write( ofstream& out );
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
 private:
 	map<string, attr_ref> _attr_map;
 	vector<attr_ref> _attr_vect;
+	vector< pair< unsigned int, unsigned int > > _attr_vers;
 	int _block_num;
 	unsigned int _ref_count;
 	vector<IBlock*> _parents;
@@ -121,7 +122,7 @@ private:
 class AExtraData : public ABlock {
 public:
 	AExtraData() {
-		AddAttr( "index", "Next Extra Data" );
+		AddAttr( "link", "Next Extra Data" );
 	}
 	~AExtraData() {};
 };
@@ -130,8 +131,8 @@ class ANamed : public ABlock {
 public:
 	ANamed(){
 		AddAttr( "string", "Name" );
-		AddAttr( "index", "Extra Data" );
-		AddAttr( "index", "Controller" );
+		AddAttr( "link", "Extra Data" );
+		AddAttr( "link", "Controller" );
 	}
 	~ANamed(){}
 };
@@ -147,18 +148,19 @@ public:
 	ANode(){
 		AddAttr( "flags", "Flags" );
 		AddAttr( "float3", "Translation" );
-		AddAttr( "matrix", "Rotation" );
+		AddAttr( "matrix33", "Rotation" );
 		AddAttr( "float", "Scale" );
-		AddAttr( "float3", "velocity" );
+		AddAttr( "float3", "Velocity" );
 		AddAttr( "linkgroup", "Properties" );
-		AddAttr( "bbox", "Bounding Box" );
+		AddAttr( "bbox", "Bounding Box", 0, VER_4_0_0_2 );
+		AddAttr( "byte" , "Unknown Byte", VER_4_2_0_2 );
 
 		SetIdentity44(bindPosition);
 	}
 	~ANode();
 	void * QueryInterface( int id );
-	void Read( ifstream& in ) {
-		ABlock::Read( in );
+	void Read( ifstream& in, unsigned int version ) {
+		ABlock::Read( in, version );
 		Matrix44 transform;
 		transform = GetLocalTransform();
 		SetBindPosition( transform );
@@ -200,7 +202,7 @@ public:
 class AController : public ABlock {
 public:
 	AController() {
-		AddAttr( "index", "Next Controller" );
+		AddAttr( "link", "Next Controller" );
 		AddAttr( "flags", "Flags" );
 		AddAttr( "float", "Frequency" );
 		AddAttr( "float", "Phase" );
@@ -211,11 +213,11 @@ public:
 	~AController() {}
 };
 
-class ALight   : public ANamed {
+class ALight   : public ANode {
 public:
 	ALight  (){
-		AddAttr( "int", "Unknown Int 1" );
-		AddAttr( "int", "Unknown Int 2" );
+		//AddAttr( "int", "Unknown Int 1" );
+		//AddAttr( "int", "Unknown Int 2" );
 		AddAttr( "float", "Dimmer" );
 		AddAttr( "float3", "Ambient Color" );
 		AddAttr( "float3", "Diffuse Color" );
@@ -315,7 +317,9 @@ class NiZBufferProperty : public AProperty {
 
 	public:
 
-		NiZBufferProperty(){}
+		NiZBufferProperty(){
+			AddAttr( "int", "Unknown Int", VER_4_2_0_2 );
+		}
 		~NiZBufferProperty(){}
 
 		string GetBlockType() { return "NiZBufferProperty"; }
@@ -396,8 +400,8 @@ class NiVertexColorProperty : public AProperty{
 class NiTriShape : public ANode {
 	public:
 		NiTriShape() {
-			AddAttr( "index", "Data" );
-			AddAttr( "index", "Skin Instance" );
+			AddAttr( "link", "Data" );
+			AddAttr( "link", "Skin Instance" );
 		}
 		~NiTriShape(){}
 		string GetBlockType() { return "NiTriShape"; }
@@ -464,12 +468,12 @@ class NiPixelData : public ABlock{
 
 	public:
 		NiPixelData(){
-			AddAttr( "index", "Unknown Index" );
+			AddAttr( "link", "Unknown Index" );
 			data = NULL; }
 		~NiPixelData(){ if (data != NULL) delete [] data; }
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ){}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ){}
 		string asString();
 		string GetBlockType() { return "NiPixelData"; }
 
@@ -548,8 +552,8 @@ class NiTriShapeData : public ABlock, public ITriShapeData {
 		}
 		~NiTriShapeData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out );
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version );
 		string asString();
 		string GetBlockType() { return "NiTriShapeData"; }
 		void * QueryInterface( int id );
@@ -594,7 +598,7 @@ class NiTriShapeData : public ABlock, public ITriShapeData {
 class NiKeyframeController : public AController {
 public:
 	NiKeyframeController(){
-		AddAttr( "index", "Data" );
+		AddAttr( "link", "Data" );
 	}
 	~NiKeyframeController(){}
 	string GetBlockType() { return "NiKeyframeController"; }
@@ -606,7 +610,7 @@ public:
 class NiAlphaController : public AController {
 public:
 	NiAlphaController(){
-		AddAttr( "index", "Data" );
+		AddAttr( "link", "Data" );
 	}
 	~NiAlphaController(){}
 	string GetBlockType() { return "NiAlphaController"; }
@@ -618,7 +622,7 @@ public:
 class NiVisController : public AController {
 public:
 	NiVisController(){
-		AddAttr( "index", "Data" );
+		AddAttr( "link", "Data" );
 	}
 	~NiVisController(){}
 	string GetBlockType() { return "NiVisController"; }
@@ -631,7 +635,7 @@ class NiMaterialColorController : public AController {
 public:
 
 	NiMaterialColorController(){
-		AddAttr( "index", "Data" );
+		AddAttr( "link", "Data" );
 	}
 	~NiMaterialColorController(){}
 	string GetBlockType() { return "NiMaterialColorController"; }
@@ -643,7 +647,7 @@ public:
 class NiUVController : public AController {
 public:
 	NiUVController (){
-		AddAttr( "index", "Data" );
+		AddAttr( "link", "Data" );
 		AddAttr( "short", "Unknown Short" );
 	}
 	~NiUVController (){}
@@ -661,8 +665,8 @@ public:
 		AddAttr( "int", "Unknown Int 2" );
 		AddAttr( "int", "Unknown Int 3" );
 		AddAttr( "short", "Unknown Short" );
-		AddAttr( "index", "Pos Data" );
-		AddAttr( "index", "Float Data" );
+		AddAttr( "link", "Pos Data" );
+		AddAttr( "link", "Float Data" );
 	}
 	~NiPathController  (){}
 	string GetBlockType() { return "NiPathController"; }
@@ -697,8 +701,8 @@ public:
 class NiAutoNormalParticles : public ANode {
 public:
 	NiAutoNormalParticles  (){
-		AddAttr( "index", "Data" );
-		AddAttr( "index", "Unknown Index?" ); // Always -1
+		AddAttr( "link", "Data" );
+		AddAttr( "link", "Unknown Index?" ); // Always -1
 	}
 	~NiAutoNormalParticles  (){}
 	string GetBlockType() { return "NiAutoNormalParticles"; }
@@ -711,8 +715,8 @@ public:
 class NiRotatingParticles : public ANode {
 public:
 	NiRotatingParticles  (){
-		AddAttr( "index", "Data" );
-		AddAttr( "index", "Unknown Index?" ); // Always -1
+		AddAttr( "link", "Data" );
+		AddAttr( "link", "Unknown Index?" ); // Always -1
 	}
 	~NiRotatingParticles  (){}
 	string GetBlockType() { return "NiRotatingParticles"; }
@@ -725,7 +729,7 @@ public:
 class NiTextureEffect : public ANode {
 public:
 	NiTextureEffect  (){
-		AddAttr( "cint", "Conditional Int" );
+		AddAttr( "condint", "Conditional Int" );
 		AddAttr( "float", "Unknown Float 1" );
 		AddAttr( "float", "Unknown Float 2" );
 		AddAttr( "float", "Unknown Float 3" );
@@ -742,7 +746,7 @@ public:
 		AddAttr( "int", "Unknown Int 2" );
 		AddAttr( "int", "Unknown Int 3" );
 		AddAttr( "int", "Unknown Int 4" );
-		AddAttr( "index", "Source Texture" );
+		AddAttr( "link", "Source Texture" );
 		AddAttr( "byte", "Unknown Byte" );
 		AddAttr( "float", "Unknown Float 13" );
 		AddAttr( "float", "Unknown Float 14" );
@@ -774,7 +778,7 @@ public:
 		AddAttr( "float", "Viewport Top" );
 		AddAttr( "float", "Viewport Bottom" );
 		AddAttr( "float", "LOLAdjust" );
-		AddAttr( "index", "Unknown Index?" ); // Always -1
+		AddAttr( "link", "Unknown Index?" ); // Always -1
 		AddAttr( "int", "Unknown Int" ); // Always 0
 	}
 	~NiCamera  (){}
@@ -788,8 +792,8 @@ public:
 class NiGravity : public ABlock {
 public:
 	NiGravity  (){
-		AddAttr( "index", "Extra Data" );
-		AddAttr( "index", "Controller" );
+		AddAttr( "link", "Extra Data" );
+		AddAttr( "link", "Controller" );
 		AddAttr( "float", "Unknown Float 1" );
 		AddAttr( "float", "Unknown Float 2" );
 		AddAttr( "int", "Unknown Int 1" );
@@ -811,8 +815,8 @@ public:
 class NiPlanarCollider : public ABlock {
 public:
 	NiPlanarCollider  (){
-		AddAttr( "index", "Extra Data" );
-		AddAttr( "index", "Controller" );
+		AddAttr( "link", "Extra Data" );
+		AddAttr( "link", "Controller" );
 		AddAttr( "float", "Unknown Float 1" );
 		AddAttr( "float", "Unknown Float 2" );
 		AddAttr( "float", "Unknown Float 3" );
@@ -841,8 +845,8 @@ public:
 class NiParticleGrowFade : public ABlock {
 public:
 	NiParticleGrowFade  (){
-		AddAttr( "index", "Extra Data" );
-		AddAttr( "index", "Controller" );
+		AddAttr( "link", "Extra Data" );
+		AddAttr( "link", "Controller" );
 		AddAttr( "float", "Unknown Float 1" );
 		AddAttr( "float", "Unknown Float 2" );
 	}
@@ -857,9 +861,9 @@ public:
 class NiParticleColorModifier : public ABlock {
 public:
 	NiParticleColorModifier  (){
-		AddAttr( "index", "Extra Data" );
-		AddAttr( "index", "Controller" );
-		AddAttr( "index", "Color Data" );
+		AddAttr( "link", "Extra Data" );
+		AddAttr( "link", "Controller" );
+		AddAttr( "link", "Color Data" );
 	}
 	~NiParticleColorModifier  (){}
 	string GetBlockType() { return "NiParticleColorModifier"; }
@@ -872,8 +876,8 @@ public:
 class NiParticleRotation : public ABlock {
 public:
 	NiParticleRotation  (){
-		AddAttr( "index", "Extra Data" );
-		AddAttr( "index", "Controller" );
+		AddAttr( "link", "Extra Data" );
+		AddAttr( "link", "Controller" );
 		AddAttr( "byte", "Unknown Byte" );
 		AddAttr( "float", "Unknown Float 1" );
 		AddAttr( "float", "Unknown Float 2" );
@@ -918,8 +922,8 @@ class NiKeyframeData : public ABlock, public IKeyframeData {
 		NiKeyframeData(){}
 		~NiKeyframeData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out );
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version );
 		string asString();
 		string GetBlockType() { return "NiKeyframeData"; }
 		
@@ -982,8 +986,8 @@ class NiSkinInstance : public ABlock, public ISkinInstInternal {
 	public:
 
 		NiSkinInstance(){
-			AddAttr( "index", "Data" );
-			AddAttr( "root", "Skeleton Root" );
+			AddAttr( "link", "Data" );
+			AddAttr( "skeletonroot", "Skeleton Root" );
 			AddAttr( "bones", "Bones" );
 		}
 		~NiSkinInstance(){}
@@ -1034,8 +1038,8 @@ class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
 		}
 		~NiSkinData();
 
-		void Read( ifstream& in );
-		void Write( ofstream& out );
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version );
 		string asString();
 		string GetBlockType() { return "NiSkinData"; }
 		void * QueryInterface( int id );
@@ -1076,7 +1080,7 @@ class NiGeomMorpherController : public AController{
 	public:
 
 		NiGeomMorpherController(){
-			AddAttr( "index", "Morph Data" );
+			AddAttr( "link", "Morph Data" );
 			AddAttr( "byte", "Unknown Byte" );
 		}
 		~NiGeomMorpherController(){}
@@ -1092,8 +1096,8 @@ class NiColorData : public ABlock{
 		NiColorData(){}
 		~NiColorData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString();
 		string GetBlockType() { return "NiColorData"; };
 
@@ -1109,8 +1113,8 @@ class NiFloatData : public ABlock{
 		NiFloatData(){}
 		~NiFloatData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString();
 		string GetBlockType() { return "NiFloatData"; };
 
@@ -1128,8 +1132,8 @@ class NiStringExtraData : public AExtraData{
 		}
 		~NiStringExtraData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out );
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version );
 		string asString();
 		string GetBlockType() { return "NiStringExtraData"; };
 
@@ -1146,8 +1150,8 @@ class NiMorphData : public ABlock, public IMorphData {
 		}
 		~NiMorphData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out );
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version );
 		string asString();
 		string GetBlockType() { return "NiMorphData"; };
 
@@ -1189,8 +1193,8 @@ class NiPosData : public ABlock{
 		NiPosData(){}
 		~NiPosData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString();
 		string GetBlockType() { return "NiPosData"; }
 
@@ -1206,8 +1210,8 @@ class NiRotatingParticlesData : public ABlock{
 		NiRotatingParticlesData(){}
 		~NiRotatingParticlesData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString() { return string(""); };
 		string GetBlockType() { return "NiRotationparticlesData"; }
 
@@ -1224,8 +1228,8 @@ class NiTextKeyExtraData : public AExtraData, public ITextKeyExtraData {
 		}
 		~NiTextKeyExtraData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out );
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version );
 		string asString();
 		string GetBlockType() { return "NiTextKeyExtraData"; }
 
@@ -1252,8 +1256,8 @@ class NiUVData : public ABlock{
 		NiUVData(){}
 		~NiUVData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString();
 		string GetBlockType() { return "NiUVData"; }
 
@@ -1273,8 +1277,8 @@ class NiVertWeightsExtraData : public AExtraData{
 		NiVertWeightsExtraData(){}
 		~NiVertWeightsExtraData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString();
 		string GetBlockType() { return "NiVertWeightsExtraData"; }
 
@@ -1291,8 +1295,8 @@ class NiVisData : public ABlock{
 		NiVisData(){}
 		~NiVisData(){}
 
-		void Read( ifstream& in );
-		void Write( ofstream& out ) {}
+		void Read( ifstream& in, unsigned int version );
+		void Write( ofstream& out, unsigned int version ) {}
 		string asString();
 		string GetBlockType() { return "NiVisData"; }
 
@@ -1308,8 +1312,8 @@ public:
 		_block_type = block_type;
 	}
 	~UnknownMixIn(){ if (data != NULL) delete [] data; }
-	void Read( ifstream& in );
-	void Write( ofstream& out );
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
 	string asString();
 	string GetBlockType() { return _block_type; }
 
@@ -1323,14 +1327,14 @@ class UnknownBlock : public ABlock, public UnknownMixIn {
 public:
 	UnknownBlock( string block_type ) : UnknownMixIn(block_type) {}
 	~UnknownBlock(){}
-	void Read( ifstream& in ) {
+	void Read( ifstream& in, unsigned int version ) {
 		//cout << endl << "Unknown Block Type found:  " << GetBlockType() << "\a" << endl;
-		ABlock::Read( in );
-		UnknownMixIn::Read( in );
+		ABlock::Read( in, version );
+		UnknownMixIn::Read( in, version );
 	}
-	void Write( ofstream& out ) {
-		ABlock::Write( out );
-		UnknownMixIn::Write( out );
+	void Write( ofstream& out, unsigned int version ) {
+		ABlock::Write( out, version );
+		UnknownMixIn::Write( out, version );
 	}
 	void asString( ostream & out ) {
 		out << ABlock::asString();
@@ -1343,13 +1347,13 @@ class UnknownControllerBlock : public AController, public UnknownMixIn {
 public:
 	UnknownControllerBlock( string block_type ) : UnknownMixIn(block_type) {}
 	~UnknownControllerBlock(){}
-	void Read( ifstream& in ) {
-		ABlock::Read( in );
-		UnknownMixIn::Read( in );
+	void Read( ifstream& in, unsigned int version ) {
+		ABlock::Read( in, version );
+		UnknownMixIn::Read( in, version );
 	}
-	void Write( ofstream& out ) {
-		ABlock::Write( out );
-		UnknownMixIn::Write( out );
+	void Write( ofstream& out, unsigned int version ) {
+		ABlock::Write( out, version );
+		UnknownMixIn::Write( out, version );
 	}
 	string asString() {
 		stringstream out;
@@ -1368,13 +1372,13 @@ class UnknownPropertyBlock : public AProperty, public UnknownMixIn {
 public:
 	UnknownPropertyBlock( string block_type ) : UnknownMixIn(block_type) {}
 	~UnknownPropertyBlock(){}
-	void Read( ifstream& in ) {
-		ABlock::Read( in );
-		UnknownMixIn::Read( in );
+	void Read( ifstream& in, unsigned int version ) {
+		ABlock::Read( in, version );
+		UnknownMixIn::Read( in, version );
 	}
-	void Write( ofstream& out ) {
-		ABlock::Write( out );
-		UnknownMixIn::Write( out );
+	void Write( ofstream& out, unsigned int version ) {
+		ABlock::Write( out, version );
+		UnknownMixIn::Write( out, version );
 	}
 	string asString() {
 		stringstream out;
@@ -1389,105 +1393,114 @@ public:
 	string GetBlockType() { return UnknownMixIn::GetBlockType(); }
 };
 
-///**
-// * NiParticleSystemController
-// */
-//class NiParticleSystemController : public UnknownControllerBlock {
-//public:
-//	NiParticleSystemController() : UnknownControllerBlock("NiParticleSystemController") {	
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "byte", "Byte?" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "float", "Guess" );
-//		AddAttr( "short", "Count?" );
-//		
-//	
-//	}
-//	
-//	//void Read( ifstream& in ) {
-//
-//	//	ABlock::Read( in );
-//
-//
-//	//	
-//
-//
-//	//	uint count = GetAttr("Count")->asInt();
-//
-//	//	cout << "Count:  " << count << endl;
-//
-//	//	short num = 0;
-//	//	short last_num = -1;
-//	//	int n = 0;
-//	//	//IAttr * attr = new MatrixAttr( "" );
-//	//	//cout << setprecision(3);
-//	//	while (true) {
-//	//		//attr->Read( in );
-//	//		//attr->Print( cout );
-//	//		ReadFloat( in );	ReadFloat( in );	ReadFloat( in );
-//	//		ReadFloat( in );	ReadFloat( in );	ReadFloat( in );
-//	//		ReadFloat( in );	ReadFloat( in );	ReadFloat( in );
-//	//		ReadUShort( in );
-//	//		num = ReadUShort( in );
-//	//		//cout << "  " << num << endl;
-//
-//	//		if ( num != last_num + 1 )
-//	//			break;
-//
-//	//		last_num = num;
-//	//		++n;
-//
-//	//	}
-//
-//	//	in.seekg( -40, ios::cur );
-//
-//	//	cout << "True Count:  " << n << endl;
-//
-//	//	if ( n != count ) {
-//	//		cout << "\a";
-//	//		cin.get();
-//	//	}
-//
-//	//	unk.Read( in );
-//
-//	//	
-//	//	//if (!b) {
-//	//	//	cout << ReadFloat( in ) << endl
-//	//	//		<< ReadFloat( in ) << endl
-//	//	//		<< ReadFloat( in ) << endl
-//	//	//		<< ReadFloat( in ) << endl;
-//	//	//}
-//	//	
-//	//	
-//	//}
-//
-//	~NiParticleSystemController(){}
-//	string GetBlockType() { return "NiParticleSystemController"; }
-//};
+/**
+ * NiParticleSystemController
+ */
+class NiParticleSystemController : public AController {
+public:
+	NiParticleSystemController() {	
+		AddAttr( "float", "Unknown 16 Floats[0]" );
+		AddAttr( "float", "Unknown 16 Floats[1]" );
+		AddAttr( "float", "Unknown 16 Floats[2]" );
+		AddAttr( "float", "Unknown 16 Floats[3]" );
+		AddAttr( "float", "Unknown 16 Floats[4]" );
+		AddAttr( "float", "Unknown 16 Floats[5]" );
+		AddAttr( "float", "Unknown 16 Floats[6]" );
+		AddAttr( "float", "Unknown 16 Floats[7]" );
+		AddAttr( "float", "Unknown 16 Floats[8]" );
+		AddAttr( "float", "Unknown 16 Floats[9]" );
+		AddAttr( "float", "Unknown 16 Floats[10]" );
+		AddAttr( "float", "Unknown 16 Floats[11]" );
+		AddAttr( "float", "Unknown 16 Floats[12]" );
+		AddAttr( "float", "Unknown 16 Floats[13]" );
+		AddAttr( "float", "Unknown 16 Floats[14]" );
+		AddAttr( "float", "Unknown 16 Floats[15]" );
+		AddAttr( "byte", "Unknown Byte" );
+		AddAttr( "float3", "Unknown 3 Floats" );
+		AddAttr( "short", "Unknown Short" );
+		AddAttr( "float3", "Unknown 3 Floats 2" );
+		AddAttr( "link", "Emitter" );
+		AddAttr( "byte", "Unknown 16 Bytes[0]" );
+		AddAttr( "byte", "Unknown 16 Bytes[1]" );
+		AddAttr( "byte", "Unknown 16 Bytes[2]" );
+		AddAttr( "byte", "Unknown 16 Bytes[3]" );
+		AddAttr( "byte", "Unknown 16 Bytes[4]" );
+		AddAttr( "byte", "Unknown 16 Bytes[5]" );
+		AddAttr( "byte", "Unknown 16 Bytes[6]" );
+		AddAttr( "byte", "Unknown 16 Bytes[7]" );
+		AddAttr( "byte", "Unknown 16 Bytes[8]" );
+		AddAttr( "byte", "Unknown 16 Bytes[9]" );
+		AddAttr( "byte", "Unknown 16 Bytes[10]" );
+		AddAttr( "byte", "Unknown 16 Bytes[11]" );
+		AddAttr( "byte", "Unknown 16 Bytes[12]" );
+		AddAttr( "byte", "Unknown 16 Bytes[13]" );
+		AddAttr( "byte", "Unknown 16 Bytes[14]" );
+		AddAttr( "byte", "Unknown 16 Bytes[15]" );
+		AddAttr( "particlegroup", "Particles" );
+		AddAttr( "link", "Unknown Link" );
+		AddAttr( "link", "Particle Extra" );
+		AddAttr( "link", "Unknown Link 2" );
+		AddAttr( "byte", "Trailer" );
+		
+	
+	}
+	
+	//void Read( ifstream& in, unsigned int version ) {
+
+	//	ABlock::Read( in, version );
+
+	//	uint count = GetAttr("Num Particles")->asInt();
+
+	//	//cout << "Count:  " << count << endl;
+
+	//	//short num = 0;
+	//	//short last_num = -1;
+	//	//int n = 0;
+	//	//IAttr * attr = new MatrixAttr( "" );
+	//	//cout << setprecision(3);
+	//	for ( int i = 0; i < count; ++i ) {
+	//		//attr->Read( in );
+	//		//attr->Print( cout );
+	//		ReadFloat( in );	ReadFloat( in );	ReadFloat( in );
+	//		ReadFloat( in );	ReadFloat( in );	ReadFloat( in );
+	//		ReadFloat( in );	ReadFloat( in );	ReadFloat( in );
+	//		ReadUShort( in );
+	//		ReadUShort( in );
+	//		//cout << "  " << num << endl;
+
+	//		//if ( num != last_num + 1 )
+	//		//	break;
+
+	//		//last_num = num;
+	//		//++n;
+
+	//	}
+
+	//	//in.seekg( -40, ios::cur );
+
+	//	//cout << "True Count:  " << n << endl;
+
+	//	//if ( n != count ) {
+	//	//	cout << "\a";
+	//	//	cin.get();
+	//	//}
+
+	//	
+	//	UnknownMixIn::Read( in, version );
+
+	//	
+	//	//if (!b) {
+	//	//	cout << ReadFloat( in ) << endl
+	//	//		<< ReadFloat( in ) << endl
+	//	//		<< ReadFloat( in ) << endl
+	//	//		<< ReadFloat( in ) << endl;
+	//	//}
+	//	
+	//	
+	//}
+
+	~NiParticleSystemController(){}
+	string GetBlockType() { return "NiParticleSystemController"; }
+};
 
 #endif // TAH_NIF_LIB_NIF_BLOCKS_H
diff --git a/nif_attrs.h b/nif_attrs.h
index a0da1c35f5bd581495aed04fd82133520ad5a10f..8b62ab6b17cb984148f739909ba2b3561d1712ac 100644
--- a/nif_attrs.h
+++ b/nif_attrs.h
@@ -305,11 +305,11 @@ private:
 	string data;
 };
 
-class IndexAttr : public AAttr {
+class LinkAttr : public AAttr {
 public:
-	IndexAttr( string name, IBlock * owner ) : AAttr( name, owner ), link( owner ) {}
-	~IndexAttr() {}
-	string GetType() const { return "index"; }
+	LinkAttr( string name, IBlock * owner ) : AAttr( name, owner ), link( owner ) {}
+	~LinkAttr() {}
+	string GetType() const { return "link"; }
 	void Read( ifstream& in ) {
 		////Remove all links beloning to this attribute
 		//_owner->RemoveAttrLinks(this);
@@ -392,7 +392,7 @@ public:
 		data[2][0] = 0.0f;	data[2][1] = 0.0f;	data[2][2] = 1.0f;
 	}
 	~MatrixAttr() {}
-	string GetType() const { return "matrix"; }
+	string GetType() const { return "matrix33"; }
 	void Read( ifstream& in ) { 
 		for (int c = 0; c < 3; ++c) {
 			for (int r = 0; r < 3; ++r) {
@@ -526,9 +526,8 @@ public:
 		int len = ReadUInt( in );
 		//cout << "Link Group Size:  " << len << endl;
 
-		if ( len > 30 ) {
-			cout << _owner->asString() << endl;
-			return;
+		if ( len > 1000 ) {
+			throw runtime_error("Unlikley number of links found. (>1000)");
 		}
 
 		for (int i = 0; i < len; ++i ) {
@@ -542,9 +541,8 @@ public:
 		WriteUInt( uint(links.size()), out );
 		//cout << "Link Group Size:  " << uint(links.size()) << endl;
 
-		if ( links.size() > 30 ) {
-			cout << "\a" << endl;
-			cin.get();
+		if ( links.size() > 1000 ) {
+			throw runtime_error("You probably shouldn't write more than 1000 links");
 		}
 
 		//Write the block indices
@@ -707,7 +705,7 @@ public:
 		data.unknownInt = 0;
 	}
 	~CIntAttr() {}
-	string GetType() const { return "cint"; }
+	string GetType() const { return "condint"; }
 	void Read( ifstream& in ) {
 		data.isUsed = ( ReadUInt( in ) != 0 );
 		if (data.isUsed) {
@@ -821,9 +819,9 @@ private:
     //                 0 - lighting emmisive
     //  1 - lighting emmisive amb diff
 
-class TextureAttr : public IndexAttr {
+class TextureAttr : public LinkAttr {
 public:
-	TextureAttr( string name, IBlock * owner, bool isBumpMap = false ) : IndexAttr(name, owner),  _isBumpMap(isBumpMap) {
+	TextureAttr( string name, IBlock * owner, bool isBumpMap = false ) : LinkAttr(name, owner),  _isBumpMap(isBumpMap) {
 		memset( &data, 0, sizeof(data) );
 	}
 	~TextureAttr() {}
@@ -832,7 +830,7 @@ public:
 		data.isUsed = ( ReadUInt( in ) != 0 );
 		if ( data.isUsed ) {	
 			//Read in link for TextureSource
-			IndexAttr::Read( in );
+			LinkAttr::Read( in );
 
 			data.clampMode = TexClampMode( ReadUInt( in ) );
 			data.filterMode = TexFilterMode( ReadUInt( in ) );
@@ -854,7 +852,7 @@ public:
 		WriteUInt( uint(data.isUsed), out );
 		if ( data.isUsed ) {
 			//Write link
-			IndexAttr::Write( out );
+			LinkAttr::Write( out );
 
 			WriteUInt( data.clampMode, out );
 			WriteUInt( data.filterMode, out );
@@ -1029,9 +1027,9 @@ private:
     //                    3 - hilight
     //                    4 - hilight2
 
-class TexSourceAttr : public IndexAttr {
+class TexSourceAttr : public LinkAttr {
 public:
-	TexSourceAttr( string name, IBlock * owner ) : IndexAttr(name, owner) {}
+	TexSourceAttr( string name, IBlock * owner ) : LinkAttr(name, owner) {}
 	~TexSourceAttr() {
 		memset(&data, 0, sizeof(data) );
 	}
@@ -1044,7 +1042,7 @@ public:
 			data.unknownByte = ReadByte( in );
 
 			//Read link for Pixel Data
-			IndexAttr::Read( in );
+			LinkAttr::Read( in );
 		}
 	}
 	void Write( ofstream& out ) {
@@ -1054,7 +1052,7 @@ public:
 		} else {
 			WriteByte ( data.unknownByte, out );
 			//Write link for Pixel Data
-			IndexAttr::Write( out );
+			LinkAttr::Write( out );
 		}
 	}
 	string asString() const {
@@ -1261,11 +1259,11 @@ public:
 	blk_ref asLink() const { return FindNodeAncestor(); }
 };
 
-class RootAttr : public AAttr {
+class SkeletonRootAttr : public AAttr {
 public:
-	RootAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
-	~RootAttr() {}
-	string GetType() const { return "root"; }
+	SkeletonRootAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
+	~SkeletonRootAttr() {}
+	string GetType() const { return "skeletonroot"; }
 	void Read( ifstream& in ) {
 		original_root = ReadUInt( in );  //Read data but do nothing with it
 	}
@@ -1317,4 +1315,76 @@ private:
 	int original_root;
 };
 
+class ParticleGroupAttr : public AAttr {
+public:
+	ParticleGroupAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
+	~ParticleGroupAttr() {}
+	string GetType() const { return "particlegroup"; }
+
+	void Read( ifstream& in ) {
+		num_particles = ReadUShort( in );
+		num_valid = ReadUShort( in );
+
+		particles.resize(num_particles);
+		for ( int i = 0; i < num_particles; ++i ) {
+			for (int c = 0; c < 3; ++c) {
+				for (int r = 0; r < 3; ++r) {
+					particles[i].unk_matrix[r][c] = ReadFloat( in );
+				}
+			}
+			particles[i].unk_short = ReadUShort( in );
+			particles[i].vert_id = ReadUShort( in );
+		}
+	}
+
+	void Write( ofstream& out ) {
+		WriteUShort( num_particles, out );
+		WriteUShort( num_valid, out );
+
+		for ( int i = 0; i < num_particles; ++i ) {
+			for (int c = 0; c < 3; ++c) {
+				for (int r = 0; r < 3; ++r) {
+					WriteFloat( particles[i].unk_matrix[r][c], out );
+				}
+			}
+
+			WriteUShort( particles[i].unk_short, out );
+			WriteUShort( particles[i].vert_id, out );
+		}
+	}
+
+	string asString() const {
+		stringstream out;
+		out.setf(ios::fixed, ios::floatfield);
+		out << setprecision(1);
+
+		out << "Num Particles:  " << num_particles << endl
+			<< "Num Valid:  " << num_valid << endl
+			<< "Particles:" << endl;
+
+		for ( int i = 0; i < num_particles; ++i ) {
+			out << "   Particle " << i << ":" << endl;
+			const Matrix33 & m = particles[i].unk_matrix;
+			out << "      |" << setw(6) << m[0][0] << "," << setw(6) << m[0][1] << "," << setw(6) << m[0][2] << " |" << endl
+				<< "      |" << setw(6) << m[1][0] << "," << setw(6) << m[1][1] << "," << setw(6) << m[1][2] << " |" << endl
+				<< "      |" << setw(6) << m[2][0] << "," << setw(6) << m[2][1] << "," << setw(6) << m[2][2] << " |" << endl;
+
+			out << "      Unknown Short:  " << particles[i].unk_short << endl
+				<< "      Vertex ID:  " << particles[i].vert_id << endl;
+		}
+		return out.str();
+	}
+	
+private:
+	struct Particle {
+		Matrix33 unk_matrix;
+		short unk_short;
+		short vert_id;
+	};
+	short num_particles;
+	short num_valid;
+	vector<Particle> particles;
+};
+
+
 #endif
diff --git a/niflib.cpp b/niflib.cpp
index de2901afdf7e26c056e4be1240f94c124e360341..80234ceec66e8fd3c6844df966efec6fb8bb6783 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -102,8 +102,8 @@ blk_ref CreateBlock( string block_type ) {
 		block = new NiParticleGrowFade;
 	} else if (block_type == "NiParticleRotation") {
 		block = new NiParticleRotation;
-	//} else if (block_type == "NiParticleSystemController") {
-	//	block = new NiParticleSystemController;
+	} else if (block_type == "NiParticleSystemController") {
+		block = new NiParticleSystemController;
 	} else if (block_type == "NiPathController") {
 		block = new NiPathController;
 	} else if (block_type == "NiPixelData") {
@@ -208,19 +208,17 @@ vector<blk_ref> ReadNifList( string file_name ) {
 	ifstream in( file_name.c_str(), ifstream::binary );
 
 	//--Read Header--//
-	char header_string[HEADER_STRING_LEN];
-	in.read( header_string, HEADER_STRING_LEN );
-	byte unknownByte = ReadByte( in );
-	char ver[4];
-	in.read(ver, 4);
+	char header_string[256];
+	in.getline( header_string, 256 );
+	uint version = ReadUInt( in );
 	uint numBlocks = ReadUInt( in );
 
-	////Output
-	//cout << "====[ NiHeader ]====" << endl <<
-	//		"Header:  " << Str(header_string, HEADER_STRING_LEN) << endl <<
-	//		"Unknown Byte:  " << Hex(unknownByte) << endl <<
-	//		"Version:  " << int(ver[3]) << "." << int(ver[2]) << "." << int(ver[1]) << "." << int(ver[0]) << endl <<
-	//		"Number of blocks: " << int(numBlocks) << endl;
+	//Output
+	//cout << endl 
+	//	 << "====[ File Header ]====" << endl
+	//	 << "Header:  " << header_string << endl
+	//	 << "Version:  " << Hex(version) << endl
+	//	 << "Number of blocks: " << int(numBlocks) << endl;
 
 	//vector<blk_ref> v;
 	//return v;
@@ -256,7 +254,7 @@ vector<blk_ref> ReadNifList( string file_name ) {
 			throw runtime_error("Read failue - Bad block position");
 		}
 
-		//cout << i << " " << blockName << endl;
+		//cout << endl << i << ":  " << blockName << endl;
 
 		//Create Block of the type that was found
 		blocks[i] = CreateBlock(blockName);
@@ -269,7 +267,9 @@ vector<blk_ref> ReadNifList( string file_name ) {
 			bk_intl->SetBlockNum(i);
 
 			//Read the block from the file
-			bk_intl->Read( in );
+			bk_intl->Read( in, version );
+
+			//cout << blocks[i]->asString() << endl;
 		}
 		else {
 			throw runtime_error("Failed to create block.");
@@ -349,7 +349,7 @@ vector<blk_ref> ReadNifList( string file_name ) {
 }
 
 //Writes a valid Nif File given a file name, a pointer to the root block of a file tree
-void WriteNifTree( string file_name, blk_ref & root_block ) {
+void WriteNifTree( string file_name, blk_ref & root_block, unsigned int version ) {
 	// Walk tree, resetting all block numbers
 	//int block_count = ResetBlockNums( 0, root_block );
 	
@@ -369,10 +369,19 @@ void WriteNifTree( string file_name, blk_ref & root_block ) {
 
 	//--WriteBlocks--//
 	for (uint i = 0; i < blk_list.size(); ++i) {
+		if (version < 0x05000001) {
+			//Write Block Type
+			WriteString( blk_list[i]->GetBlockType() , out );
+		}
+
 		//Get internal interface
 		IBlockInternal * bk_intl = (IBlockInternal*)blk_list[i]->QueryInterface( BlockInternal );
 
-		bk_intl->Write( out );
+		bk_intl->Write( out, version );
+
+		if (version >= 0x05000001) {
+			WriteUInt( 0, out );
+		}
 	}
 
 	//--Write Footer--//
diff --git a/niflib.h b/niflib.h
index 3b9e4076cb63598a969c4471413ad36bb311a865..3d2cb8862d8e30562411f086f4fb445cc4abd40c 100644
--- a/niflib.h
+++ b/niflib.h
@@ -78,6 +78,10 @@ const int ID_KEYFRAME_DATA = 3;
 const int ID_TEXT_KEY_EXTRA_DATA = 4;
 const int ID_MORPH_DATA = 5;
 
+//NIF Versions
+const int VER_4_0_0_2 = 0x04000002;
+const int VER_4_2_0_2 = 0x04020002;
+
 #ifndef NULL
 #define NULL 0
 #endif
@@ -94,7 +98,7 @@ vector<blk_ref> ReadNifList( string file_name );
 blk_ref ReadNifTree( string file_name );
 
 //Writes a valid Nif File given a file name, a pointer to the root block of a file tree
-void WriteNifTree( string file_name, blk_ref & root_block );
+void WriteNifTree( string file_name, blk_ref & root_block, unsigned int version = VER_4_2_0_2 );
 
 ////Returns the NIF spec version of a file, given a file name.
 //string GetFileVersion(string file_name);
@@ -499,6 +503,131 @@ public:
 	virtual void SetMorphVerts( int n, const vector<Vector3> & in ) = 0;
 };
 
+//struct ComplexVertex {
+//	ComplexVertex() : has_color(false), has_normal(false), vertex_index(0), normal_index(0), color_index(0) {}
+//	~ComplexVertex();
+//	bool has_color, has_normal;
+//	int vertex_index, color_index, normal_index;
+//	vector<int> uv_indices;
+//}
+//
+//struct ComplexFace {
+//	vector<ComplexVertex> points;
+//	int base_map_index;
+//	int glow_map_index;
+//};
+//
+//class ComplexShape {
+//
+//	void CombineTriShapes( list<blk_ref> & tri_shapes );
+////	void SetVertices( vector<Vector3> & vertices );
+////	void SetUVs( vector<UVCoord> & uvs );
+////	void SetColors( vector<Color> & colors );
+////	void SetNormals( vector<Vector3> & normals );
+////	void SetBones( vector<blk_ref> & bones );
+////	void SetFaces( list< vector< ComplexVertex > > & faces );
+////
+////	vector<Vector3> GetVertices();
+////	vector<UVCoord> GetUVs();
+////	vector<Color> GetColors();
+////	vector<Vector3> GetNormals();
+////	vector<blk_ref> GetBones();
+////	list< vector< ComplexVertex > > GetFaces();
+//
+//private:
+//	vector<Vector3> _vertices;
+//	vector<Color> _colors;
+//	vector<Vector3> _normals;
+//	list<ComplexFace> _faces;
+//	map<string, blk_ref> _materials;
+//	map<string, vector<UVCoord> > _uvs;
+//	map<blk_ref, map<int, float> > _bones;
+//};
+//
+//void ComplexShape::CombineTriShapes( list<blk_ref> & tri_shapes ) {
+//	//Clear all internal datea
+//	_vertices.clear();
+//	_colors.clear();
+//	_normals.clear();
+//	_materials.clear();
+//	_uvs.clear();
+//	_faces.clear();
+//	_bones.clear();
+//
+//	//Create a temporary spot to hold the triangle lists from each TriShape
+//	vector< vector<Triangle> > ts_faces;
+//
+//	//Create lists to hold the lookup tables
+//	vector<int> tri_lookup, nor_lookup, col_lookup;
+//	map<string, vector<int> > mat_lookup, uv_lookup;
+//	
+//	//--Cycle through all the TriShapes, adding their data to the lists--//
+//	list<blk_ref>::iterator it;
+//
+//	for (it = tri_shapes.begin(); it != tri_shapes.end(); ++it) {
+//		ITriShapeData * data = QueryTriShapeData(*it);
+//
+//		//Vertices
+//		vector<Vector3> ts_verts = data->GetVertices();
+//		_vertices.insert(_vertices.end(), ts_verts.begin(), ts_verts.end();
+//
+//		//Normals
+//		vector<Vector3> ts_norms = data->GetNormals();
+//		_normals.insert(_normals.end(), ts_norms.begin(), ts_norms.end();
+//		
+//		//Colors
+//		vector<Colors> ts_cols = data->GetColors();
+//		_colors.insert(_colors.end(), ts_colors.begin(), ts_colors.end();
+//
+//		//Triangles
+//		ts_faces[i] = data->GetTriangles();
+//
+//		//UV Coords
+//		vector< vector<UVCoords> > uvs(data->GetUVSetCount());
+//		for (int i = 0; i < data->GetUVSetCount(); ++i) {
+//			uvs[i] = data->GetUVSet(i);
+//		}
+//
+//		//Associate UV Coord Data with proper map name
+//		blk_ref tx_prop = par["Properties"]->FindLink( "NiTexturingProperty");
+//		if ( tx_prop.is_null() == false ) {
+//			int uv_set = 0;
+//			for (int i = 0; i < 7; ++i) {
+//				string attr_name, map;
+//				switch(i) {
+//					case 0:	attr_name = "Base Texture";     map = "map1";   break;
+//					case 1:	attr_name = "Dark Texture";     map = "dark";   break;
+//					case 2:	attr_name = "Detail Texture";   map = "detail"; break;
+//					case 3:	attr_name = "Gloss Texture";    map = "gloss";  break;
+//					case 4:	attr_name = "Glow Texture";     map = "glow";   break;
+//					case 5:	attr_name = "Bump Map Texture"; map = "bump";   break;
+//					case 6:	attr_name = "Decal 0 Texture";  map = "decal0";
+//				}
+//
+//				if ( tx_prop[attr_name]->asTexture().isUsed == true ) {
+//					//How to merge all UVs?
+//				}
+//
+//		}
+//
+//
+//		//blk_ref material = (*it)->GetAttr("Propreties")->FindLink("NiMaterialProperty");
+//		//blk_ref skin_inst = (*it)->GetAttr("Skin Instance")->asLink();
+//		//blk_ref skin_data;
+//		//vector<blk_ref> bones;
+//		//map<int, float> weights;
+//		//if ( skin_inst.is_null() == false ) {
+//		//	skin_block = skin_inst->GetAttr("Data");
+//		//	if (skin_block.is_null() == false ) {
+//		//		ISkinData * skin_data = QuerySkinData(skin_block);
+//		//		weights = skin_data->GetWeights();
+//		//		bones = skin_data->GetBones();
+//		//	}
+//		//}
+//
+//	}
+//}
+
 //--Attribute Reference--//
 class attr_ref {
 public:
@@ -542,11 +671,11 @@ public:
 	}
 
 	//Assignment operators
-	attr_ref & operator=(int & n) {
+	attr_ref & operator=(int n) {
 		_attr->Set(n);
 		return *this;
 	}
-	attr_ref & operator=(float & n) {
+	attr_ref & operator=(float n) {
 		_attr->Set(n);
 		return *this;
 	}