diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
index 2441fc5a14640eb40b97d8fdf8568c4d09d62278..192c02de3ab433b17a418c82bd2bd88865d9877d 100644
--- a/NIF_Blocks.cpp
+++ b/NIF_Blocks.cpp
@@ -116,6 +116,8 @@ void ABlock::AddAttr( AttrTypes type, string name, unsigned int first_ver, unsig
 		attr = new SkeletonRootAttr( name, this, first_ver, last_ver );
 	} else if ( type == attr_particlegroup ) {
 		attr = new ParticleGroupAttr( name, this, first_ver, last_ver );
+	} else if ( type == attr_lodrangegroup ) {
+		attr = new LODRangeGroupAttr( name, this, first_ver, last_ver );
 	} else {
 		cout << type << endl;
 		throw runtime_error("Unknown attribute type requested.");
@@ -159,7 +161,9 @@ void ABlock::Read( ifstream& in, unsigned int version ) {
 	//Read Attributes
 	for (unsigned int i = 0; i < _attr_vect.size(); ++i ) {
 		_attr_vect[i]->Read( in, version );
-		//cout << "   " << _attr_vect[i]->GetName() << ":  " << _attr_vect[i]->asString() << endl;
+		//if ( _attr_vect[i]->GetType() != "bones" ) {
+		//	cout << "   " << _attr_vect[i]->GetName() << ":  " << _attr_vect[i]->asString() << endl;
+		//}
 	}
 }
 
@@ -1001,16 +1005,24 @@ void NiTriShapeData::Read( ifstream& in, unsigned int version ){
 	AShapeData::Read( in, version );
 
 	short numTriangles = ReadUShort( in );
-	if (numTriangles > 0) {
-		int numVertexIndices = ReadUInt( in );
-		triangles.resize( numTriangles );
-		for ( int i = 0; i < numTriangles; ++i ){
-			triangles[i].v1 = ReadUShort( in );
-			triangles[i].v2 = ReadUShort( in );
-			triangles[i].v3 = ReadUShort( in );
+	int numVertexIndices = ReadUInt( in );
+	
+	//From version 10.1.0.0 on there is a bool to check whether or not there are any triangles
+	//We already know the answer to this from the numTriangles count, don't we?
+	//Jus in case, set numTriangles to zero if this is false.
+	if ( version >= VER_10_1_0_0 ) {
+		if ( ReadBool( in, version ) == false ) {
+			numTriangles = 0;
 		}
 	}
 
+	triangles.resize( numTriangles );
+	for ( uint i = 0; i < triangles.size(); ++i ){
+		triangles[i].v1 = ReadUShort( in );
+		triangles[i].v2 = ReadUShort( in );
+		triangles[i].v3 = ReadUShort( in );
+	}
+
 	short matchGroupCount = ReadUShort( in );
 	match_group_mode = ( matchGroupCount != 0 );  // Only record whether or not file prefers to have match data generated
 
@@ -1062,16 +1074,17 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 	AShapeData::Write( out, version );
 
 	WriteUShort( ushort(triangles.size()), out );
+	WriteUInt( ushort(triangles.size()) * 3, out );
+	
+	//From version 10.1.0.0 on there is a bool to check whether or not there are any triangles
+	if ( version >= VER_10_1_0_0 ) {
+		WriteBool( triangles.size() > 0, out, version );
+	}
 
-	if ( triangles.size() > 0 ) {
-		//Write number of shorts:  triCount * 3
-		WriteUInt( ushort(triangles.size()) * 3, out );
-
-		for ( uint i = 0; i < triangles.size(); ++i ){
-			WriteUShort( triangles[i].v1, out );
-			WriteUShort( triangles[i].v2, out );
-			WriteUShort( triangles[i].v3, out );
-		}
+	for ( uint i = 0; i < triangles.size(); ++i ){
+		WriteUShort( triangles[i].v1, out );
+		WriteUShort( triangles[i].v2, out );
+		WriteUShort( triangles[i].v3, out );
 	}
 
 	if ( match_group_mode ) {
@@ -1138,6 +1151,15 @@ void NiTriStripsData::Read( ifstream& in, unsigned int version ){
 		strips[i].resize( stripSize );
 	}
 
+	//From version 10.1.0.0 on there is a bool to check whether or not there are any points
+	//We already know the answer to this from the counts above, don't we?
+	//Jus in case, clear all strips if this is false.
+	if ( version >= VER_10_1_0_0 ) {
+		if ( ReadBool( in, version ) == false ) {
+			strips.clear();
+		}
+	}
+
 	//Read points
 	for ( uint i = 0; i < strips.size(); ++i ) {
 		for ( uint j = 0; j < strips[i].size(); ++j ) {
@@ -1150,14 +1172,8 @@ void NiTriStripsData::Write( ofstream& out, unsigned int version ){
 
 	AShapeData::Write( out, version );
 
-	//Calculate number of triangles
-	//Sum of length of each strip - 2
-	short numTriangles = 0;
-	for ( uint i = 0; i < strips.size(); ++i ) {
-		numTriangles += short(strips[i].size() - 2);
-	}
-
 	//Write number of triangles and strips
+	short numTriangles = GetTriangleCount();
 	WriteUShort( numTriangles, out );
 	WriteUShort( ushort(strips.size()), out );
 
@@ -1166,6 +1182,11 @@ void NiTriStripsData::Write( ofstream& out, unsigned int version ){
 		WriteUShort( ushort(strips[i].size()), out );
 	}
 
+	//From version 10.1.0.0 on there is a bool to check whether or not there are any points
+	if ( version >= VER_10_1_0_0 ) {
+		WriteBool( numTriangles > 0, out, version );
+	}
+
 	//Write points
 	for ( uint i = 0; i < strips.size(); ++i ) {
 		for ( uint j = 0; j < strips[i].size(); ++j ) {
@@ -1181,14 +1202,7 @@ string NiTriStripsData::asString() {
 
 	out << AShapeData::asString();
 
-	//Calculate number of triangles
-	//Sum of length of each strip - 2
-	short numTriangles = 0;
-	for ( uint i = 0; i < strips.size(); ++i ) {
-		numTriangles += short(strips[i].size() - 2);
-	}
-
-	out << "Triangles:  " << numTriangles << endl
+	out << "Triangles:  " << GetTriangleCount() << endl
 		<< "Strips:  " << ushort(strips.size()) << endl;
 
 	if (verbose) {
@@ -1205,6 +1219,163 @@ string NiTriStripsData::asString() {
 	return out.str();
 }
 
+void * NiTriStripsData::QueryInterface( int id ) {
+	// Contains TriShapeData Interface
+	if ( id == ID_TRI_STRIPS_DATA ) {
+		return (void*)static_cast<ITriStripsData*>(this);
+	} else {
+		return AShapeData::QueryInterface( id );
+	}
+}
+
+short NiTriStripsData::GetStripCount() {
+	return short(strips.size());
+}
+
+void NiTriStripsData::SetStripCount(int n) {
+	strips.resize( n );
+}
+
+//Getters
+vector<short> NiTriStripsData::GetStrip( int index ) {
+	return strips[index];
+}
+
+vector<Triangle> NiTriStripsData::GetTriangles() {
+
+	//Create a vector to hold the triangles
+	vector<Triangle> triangles( GetTriangleCount() );
+	int n = 0; // Current triangle
+
+	//Cycle through all strips
+	vector< vector<short> >::iterator it;
+	for (it = strips.begin(); it != strips.end(); ++it ) {
+		//The first three values in the strip are the first triangle
+		triangles[n].Set( (*it)[0], (*it)[1], (*it)[2] );
+
+		//Move to the next triangle
+		++n;
+
+		//The remaining triangles use the previous two indices as their first two indices.
+		for( uint i = 3; i < it->size(); ++i ) {
+			//Odd numbered triangles need to be reversed to keep the vertices in counter-clockwise order
+			if ( i % 2 == 0 )
+				triangles[n].Set( (*it)[i - 2], (*it)[i - 1], (*it)[i] );
+			else
+				triangles[n].Set( (*it)[i], (*it)[i - 1], (*it)[i - 2] );
+
+			//Move to the next triangle
+			++n;
+		}
+	}
+
+	return triangles;
+}
+
+//Setter
+void NiTriStripsData::SetStrip( int index, const vector<short> & in ) {
+	strips[index] = in;
+}
+
+short NiTriStripsData::GetTriangleCount() {
+
+	//Calculate number of triangles
+	//Sum of length of each strip - 2
+	short numTriangles = 0;
+	for ( uint i = 0; i < strips.size(); ++i ) {
+		numTriangles += short(strips[i].size() - 2);
+	}
+
+	return numTriangles;
+}
+
+	
+
+/***********************************************************
+ * NiCollisionData methods
+ **********************************************************/
+
+void NiCollisionData::Read( ifstream& in, unsigned int version ){
+	//Read parent node but don't store it
+	ReadUInt( in );
+
+	unknownInt1 = ReadUInt( in );
+	unknownByte = ReadByte( in );
+	collisionType = ReadUInt( in );
+
+	if ( collisionType == 0 ) {
+		unknownInt2 = ReadUInt( in );
+		ReadFVector3( unknown3Floats, in );
+	} 
+	else if ( collisionType == 1 ) {
+		for (int i = 0; i < 15; ++i ) {
+			unknown15Floats[i] = ReadFloat( in );
+		}
+	} 
+	else if ( collisionType == 2) {
+		for ( int i = 0; i < 8; ++i ) {
+			unknown8Floats[i] = ReadFloat( in );
+		}
+	} 
+}
+
+void NiCollisionData::Write( ofstream& out, unsigned int version ){
+
+	//Write Parent node number
+	WriteUInt( GetParent().get_index(), out );
+
+	WriteUInt( unknownInt1, out );
+	WriteByte( unknownByte, out );
+	WriteUInt( collisionType, out );
+
+	if ( collisionType == 0 ) {
+		WriteUInt( unknownInt2, out );
+		WriteFVector3( unknown3Floats, out );
+	} 
+	else if ( collisionType == 1 ) {
+		for (int i = 0; i < 15; ++i ) {
+			WriteFloat( unknown15Floats[i], out );
+		}
+	} 
+	else if ( collisionType == 2) {
+		for ( int i = 0; i < 8; ++i ) {
+			WriteFloat( unknown8Floats[i], out );
+		}
+	} 
+}
+
+string NiCollisionData::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	//Parent is already written, so don't do anything with it
+
+	out << "Unknown Int 1:  " << unknownInt1 << endl
+		<< "Unknown Byte:  " << unknownByte << endl
+		<< "Collision Type:  " << collisionType << endl
+		<< "Collision Data:" << endl;
+
+	if ( collisionType == 0 ) {
+		out << "   Unknown Int 2:  " << unknownInt2 << endl
+			<< "   Unknown 3 Floats:   " << unknown3Floats << endl;
+	} 
+	else if ( collisionType == 1 ) {
+		out << "   Unknown 15 Floats:" << endl;
+		for (int i = 0; i < 15; ++i ) {
+			out << "      " << i + 1 << ":  " << unknown15Floats[i] << endl;
+		}
+	} 
+	else if ( collisionType == 2) {
+		out << "   Unknown 8 Floats:" << endl;
+		for ( int i = 0; i < 8; ++i ) {
+			out << "      " << i + 1 << ":  " << unknown8Floats[i] << endl;
+		}
+	} 
+
+	return out.str();
+}
+
 /***********************************************************
  * NiSkinData methods
  **********************************************************/
@@ -1219,7 +1390,7 @@ void NiSkinData::Read( ifstream& in, unsigned int version ) {
 	ReadFVector3( translation, in );
 	scale = ReadFloat( in );
 	int boneCount = ReadUInt( in );
-	unknownInt = ReadUInt( in );
+	GetAttr("Skin Partition")->Read( in, version );
 	//unknownByte exists from version 4.2.1.0 on
 	if ( version >= VER_4_2_1_0 ) {
 		unknownByte = ReadByte( in );
@@ -1256,7 +1427,7 @@ void NiSkinData::Write( ofstream& out, unsigned int version ) {
 	WriteFVector3( translation, out );
 	WriteFloat( scale, out );
 	WriteUInt(short(bone_map.size()), out);
-	WriteUInt(unknownInt, out);
+	GetAttr("Skin Partition")->Write( out, version );
 	//unknownByte exists from version 4.2.1.0 on
 	if ( version >= VER_4_2_1_0) {
 		WriteByte( unknownByte, out );
@@ -1340,8 +1511,8 @@ string NiSkinData::asString() {
 		<< "Translate:  " << translation << endl
 		<< "Scale:  " << scale << endl
 		<< "Bone Count:  " << uint(bone_map.size()) << endl
-		<< "Unknown Index:  " << unknownInt << endl
-		<< "Unknown Byte:  " << unknownByte << endl
+		<< "Skin Partition:  " << GetAttr("Skin Partition")->asLink() << endl
+		<< "Unknown Byte:  " << int(unknownByte) << endl
 		<< "Bones:" << endl;
 
 	map<IBlock*, Bone>::iterator it;
@@ -1732,191 +1903,110 @@ string NiGeomMorpherController::asString() {
  * NiKeyframeData methods
  **********************************************************/
 
-void NiKeyframeData::Read( ifstream& in, unsigned int version ) {
+void NiKeyframeData::Read( ifstream& file, unsigned int version ) {
 
-	scaleType = rotationType = translationType = KeyType(0);
+	scaleType = rotationType = translationType = xyzTypes[0] = xyzTypes[1] = xyzTypes[2] = KeyType(0);
 
 	//--Rotation--//
-	uint numRotations = ReadUInt( in );
+	uint numRotations = ReadUInt( file );
 
 	if (numRotations > 0) {
-		rotationType = KeyType(ReadUInt( in ));
-
-		rotKeys.resize( numRotations );
-		for ( unsigned int i = 0; i < numRotations; ++i ) {
-			rotKeys[i].time = ReadFloat( in );
+		NifStream( rotationType, file );
 
-			if (rotationType != 4) {
-				rotKeys[i].data.w = ReadFloat( in );
-				rotKeys[i].data.x = ReadFloat( in );
-				rotKeys[i].data.y = ReadFloat( in );
-				rotKeys[i].data.z = ReadFloat( in );
+		if ( rotationType != 4 ) {
+			rotKeys.resize( numRotations );
+			for ( unsigned int i = 0; i < rotKeys.size(); ++i ) {
+				NifStream(rotKeys[i], file, rotationType );
 			}
+		}
+		else {
+			//Read vestigial time and discard
+			ReadFloat( file );
 
-			if (rotationType == 3) {
-				rotKeys[i].tension = ReadFloat( in );
-				rotKeys[i].bias = ReadFloat( in );
-				rotKeys[i].continuity = ReadFloat( in );
-			} else if (rotationType == 4) {
-				throw runtime_error("NiKeyframeData rotation type 4 currently unsupported");
-				//cout << "Rotation Type 4 Unsupported - Data will not be read" << endl;
-
-				////cout << endl;
-				//for (int j = 0; j < 3; j++) {
-				//	//cout << "--Rotation Group " << j + 1 << "--" << endl;
-				//	int subCount = ReadUInt( in );
-				//	//cout << "Sub Count:  " << subCount << endl;
-				//	int subType = ReadUInt( in );
-				//	//cout << "Sub Type:  " << subType << endl;
-
-				//	for (int k = 0; k < subCount; k++) {
-				//		float subTime = ReadFloat( in );
-				//		//cout << "KeyTime:  " << subTime << "  ";
-				//		float subUnk1 = ReadFloat( in );
-				//		//cout << "Data:  " << subUnk1;
-				//		if (subType == 2) {
-				//			float subUnk2 = ReadFloat( in );
-				//			float subUnk3 = ReadFloat( in );
-				//			//cout << ", " << subUnk2 << ", " << subUnk3;
-				//		}
-				//		//cout << endl;
-				//	}
-				//}
+			for (int i = 0; i < 3; i++) {
+				int subCount = ReadUInt( file );
+				NifStream( xyzTypes[i], file );
+
+				xyzKeys[i].resize( subCount );
+				for (uint j = 0; j < xyzKeys[i].size(); j++) {
+					NifStream(xyzKeys[i][j], file, xyzTypes[i] );
+				}
 			}
 		}
 	}
 
 	//--Translation--//
-	uint numTranslations = ReadUInt( in );
+	uint numTranslations = ReadUInt( file );
 
 	if (numTranslations > 0) {
-		translationType = KeyType(ReadUInt( in ));
+		NifStream( translationType, file );
 
 		transKeys.resize( numTranslations );
-		for ( unsigned int i = 0; i < numTranslations; ++i ) {
-			transKeys[i].time = ReadFloat( in );
-			
-			transKeys[i].data.x = ReadFloat( in );
-			transKeys[i].data.y = ReadFloat( in );
-			transKeys[i].data.z = ReadFloat( in );
-
-			if (translationType == 2) {
-				transKeys[i].forward_tangent.x = ReadFloat( in );
-				transKeys[i].forward_tangent.y = ReadFloat( in );
-				transKeys[i].forward_tangent.y = ReadFloat( in );
-
-				transKeys[i].backward_tangent.x = ReadFloat( in );
-				transKeys[i].backward_tangent.y = ReadFloat( in );
-				transKeys[i].backward_tangent.y = ReadFloat( in );
-			}else if (translationType == 3) {
-				transKeys[i].tension = ReadFloat( in );
-				transKeys[i].bias = ReadFloat( in );
-				transKeys[i].continuity = ReadFloat( in );
-			}
+		for ( unsigned int i = 0; i < transKeys.size(); ++i ) {
+			NifStream(transKeys[i], file, translationType );
 		}
 	}
 
 	//--Scale--//
-	uint numScalings = ReadUInt( in );
+	uint numScalings = ReadUInt( file );
 
 	if (numScalings > 0) {
-		scaleType = KeyType(ReadUInt( in ));
+		NifStream( scaleType, file );
 
 		scaleKeys.resize( numScalings );
-		for ( unsigned int i = 0; i < numScalings; ++i ) {
-			scaleKeys[i].time = ReadFloat( in );
-
-			scaleKeys[i].data = ReadFloat( in );
-
-			if (scaleType == 2) {
-				scaleKeys[i].forward_tangent = ReadFloat( in );
-				scaleKeys[i].backward_tangent = ReadFloat( in );
-			} else if (scaleType == 3) {
-				scaleKeys[i].tension = ReadFloat( in );
-				scaleKeys[i].bias = ReadFloat( in );
-				scaleKeys[i].continuity = ReadFloat( in );
-			}
+		for ( unsigned int i = 0; i < scaleKeys.size(); ++i ) {
+			NifStream(scaleKeys[i], file, scaleType );
 		}
 	}
 }
 
-void NiKeyframeData::Write( ofstream& out, unsigned int version ) {
+void NiKeyframeData::Write( ofstream& file, unsigned int version ) {
 
 	//--Rotation--//
-	WriteUInt( uint(rotKeys.size()), out );
+	WriteUInt( uint(rotKeys.size()) , file );
 
-	if (rotKeys.size() > 0) {
-		WriteUInt( rotationType, out );
-
-		for ( unsigned int i = 0; i < rotKeys.size(); ++i ) {
-			WriteFloat( rotKeys[i].time, out );
+	if ( rotKeys.size() > 0) {
+		NifStream( rotationType, file );
 
-			if (rotationType != 4) {
-				WriteFloat( rotKeys[i].data.w, out );
-				WriteFloat( rotKeys[i].data.x, out );
-				WriteFloat( rotKeys[i].data.y, out );
-				WriteFloat( rotKeys[i].data.z, out );
+		if ( rotationType != 4 ) {
+			for ( unsigned int i = 0; i < rotKeys.size(); ++i ) {
+				NifStream(rotKeys[i], file, rotationType );
 			}
+		}
+		else {
+			//Write vestigial time
+			WriteFloat( 0.0, file );
 
-			if (rotationType == 3) {
-				WriteFloat( rotKeys[i].tension, out );
-				WriteFloat( rotKeys[i].bias, out );
-				WriteFloat( rotKeys[i].continuity, out );
-			} else if (rotationType == 4) {
-				throw runtime_error("NiKeyframeData rotation type 4 currently unsupported");
+			for (int i = 0; i < 3; i++) {
+				WriteUInt( uint(xyzKeys[i].size()) , file );
+				NifStream( xyzTypes[i], file );
+
+				for (uint j = 0; j < xyzKeys[i].size(); j++) {
+					NifStream(xyzKeys[i][j], file, xyzTypes[i] );
+				}
 			}
 		}
 	}
 
 	//--Translation--//
+	WriteUInt( uint(transKeys.size()) , file );
 
-	WriteUInt( uint(transKeys.size()), out );
-
-	if (transKeys.size() > 0) {
-		WriteUInt( translationType, out );
+	if ( transKeys.size() > 0) {
+		NifStream( translationType, file );
 
 		for ( unsigned int i = 0; i < transKeys.size(); ++i ) {
-			WriteFloat( transKeys[i].time, out );
-
-			WriteFloat( transKeys[i].data.x, out );
-			WriteFloat( transKeys[i].data.y, out );
-			WriteFloat( transKeys[i].data.z, out );
-
-			if (translationType == 2) {
-				WriteFloat( transKeys[i].forward_tangent.x, out );
-				WriteFloat( transKeys[i].forward_tangent.y, out );
-				WriteFloat( transKeys[i].forward_tangent.z, out );
-
-				WriteFloat( transKeys[i].backward_tangent.x, out );
-				WriteFloat( transKeys[i].backward_tangent.y, out );
-				WriteFloat( transKeys[i].backward_tangent.z, out );
-			}else if (translationType == 3) {
-				WriteFloat( transKeys[i].tension, out );
-				WriteFloat( transKeys[i].bias, out );
-				WriteFloat( transKeys[i].continuity, out );
-			}
+			NifStream(transKeys[i], file, translationType );
 		}
 	}
-                        
+
 	//--Scale--//
-	WriteUInt( uint(scaleKeys.size()), out );
+	WriteUInt( uint(scaleKeys.size()), file );
 
-	if ( scaleKeys.size() > 0) {
-		WriteUInt( scaleType, out );
+	if (scaleKeys.size() > 0) {
+		NifStream( scaleType, file );
 
 		for ( unsigned int i = 0; i < scaleKeys.size(); ++i ) {
-			WriteFloat( scaleKeys[i].time, out );
-
-			WriteFloat( scaleKeys[i].data, out );
-
-			if (scaleType == 2) {
-				WriteFloat( scaleKeys[i].forward_tangent, out );
-				WriteFloat( scaleKeys[i].backward_tangent, out );
-			} else if (scaleType == 3) {
-				WriteFloat( scaleKeys[i].tension, out );
-				WriteFloat( scaleKeys[i].bias, out );
-				WriteFloat( scaleKeys[i].continuity, out );
-			}
+			NifStream(scaleKeys[i], file, scaleType );
 		}
 	}
 }
@@ -2015,36 +2105,22 @@ string NiKeyframeData::asString() {
  * NiColorData methods
  **********************************************************/
 
-void NiColorData::Read( ifstream& in, unsigned int version ) {
-	uint colorCount = ReadUInt( in );
-	keyType = ReadUInt( in );
-
-	if (keyType != 1) {
-		stringstream str;
-        str << "NiColorData is thought to only support keyType of 1, but this NIF has a keyType of " << keyType << "." << endl;
-		throw runtime_error( str.str() );
-	}
+void NiColorData::Read( ifstream& file, unsigned int version ) {
+	uint keyCount = ReadUInt( file );
+	NifStream( keyType, file );
 
-	keys.resize( colorCount );
+	keys.resize( keyCount );
 	for (uint i = 0; i < keys.size(); i++) {
-		keys[i].time = ReadFloat( in );
-		ReadFVector4( keys[i].data, in );
+		NifStream( keys[i], file, keyType );
 	}
 }
 
-void NiColorData::Write( ofstream& out, unsigned int version ) {
-	WriteUInt( uint(keys.size()), out );
-	WriteUInt( keyType, out );
-
-	if (keyType != 1) {
-		stringstream str;
-        str << "NiColorData is thought to only support keyType of 1, but this NIF has a keyType of " << keyType << "." << endl;
-		throw runtime_error( str.str() );
-	}
+void NiColorData::Write( ofstream& file, unsigned int version ) {
+	WriteUInt( uint(keys.size()), file );
+	NifStream( keyType, file );
 
 	for (uint i = 0; i < keys.size(); i++) {
-		WriteFloat( keys[i].time, out );
-		WriteFVector4( keys[i].data, out );
+		NifStream( keys[i], file, keyType );
 	}
 }
 
@@ -2058,9 +2134,9 @@ string NiColorData::asString() {
 		<< "Key Type:  " << keyType << endl;
 
 	if (verbose) {
-		vector<Key<fVector4> >::iterator it;
+		vector< Key<Color> >::iterator it;
 		for ( it = keys.begin(); it != keys.end(); ++it ) {
-			out << "Key Time:  " << (*it).time << "  Color:  " << (*it).data << endl;
+			out << "Key Time:  " <<  it->time << "  Color:  " << it->data.r << ", " << it->data.g << ", " << it->data.b << ", " << it->data.a << endl;
 		}
 	} else {
 		out << "<<Data Not Shown>>" << endl;
@@ -2077,18 +2153,27 @@ void NiFloatData::Read( ifstream& in, unsigned int version ) {
 	uint keyCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
-	if (keyType != 2) {
-		stringstream str;
-		str << "NiFloatata is thought to only support keyType of 2, but this NIF has a keyType of " << keyType << ".";
-		throw runtime_error( str.str() );
+	if (keyCount > 0 && (keyType < 1 || keyType > 3 ) ) {
+		stringstream s;
+		s << "NiFloatData is thought to only support keyType of 1, 2, or 3, but this NIF has a keyType of " << keyType << ".";
+		throw runtime_error(s.str());
 	}
 
 	keys.resize( keyCount );
 	for (uint i = 0; i < keys.size(); i++) {
+		//Always read the time and data
 		keys[i].time = ReadFloat( in );
 		keys[i].data = ReadFloat( in );
-		keys[i].forward_tangent = ReadFloat( in );
-		keys[i].backward_tangent = ReadFloat( in );
+		if ( keyType == 2 ) {
+			//Uses Quadratic interpolation
+			keys[i].forward_tangent = ReadFloat( in );
+			keys[i].backward_tangent = ReadFloat( in );
+		} else if ( keyType == 3 ) {
+			//Uses TBC interpolation
+			keys[i].tension = ReadFloat( in );
+			keys[i].bias = ReadFloat( in );
+			keys[i].continuity = ReadFloat( in );
+		}
 	}
 }
 
@@ -2096,17 +2181,26 @@ void NiFloatData::Write( ofstream& out, unsigned int version ) {
 	WriteUInt( uint(keys.size()), out );
 	WriteUInt( keyType, out );
 
-	if (keyType != 2) {
-		stringstream str;
-		str << "NiFloatata is thought to only support keyType of 2, but this NIF has a keyType of " << keyType << ".";
-		throw runtime_error( str.str() );
+	if (keys.size() > 0 && (keyType < 1 || keyType > 3 ) ) {
+		stringstream s;
+		s << "NiFloatData is thought to only support keyType of 1, 2, or 3, but this NIF has a keyType of " << keyType << ".";
+		throw runtime_error(s.str());
 	}
 
 	for (uint i = 0; i < keys.size(); i++) {
+		//Always write the time and data
 		WriteFloat( keys[i].time, out );
 		WriteFloat( keys[i].data, out );
-		WriteFloat( keys[i].forward_tangent, out );
-		WriteFloat( keys[i].backward_tangent, out );
+		if ( keyType == 2 ) {
+			//Uses Quadratic interpolation
+			WriteFloat( keys[i].forward_tangent, out );
+			WriteFloat( keys[i].backward_tangent, out );
+		} else if ( keyType == 3 ) {
+			//Uses TBC interpolation
+			WriteFloat( keys[i].tension, out );
+			WriteFloat( keys[i].bias, out );
+			WriteFloat( keys[i].continuity, out );
+		}
 	}
 }
 
@@ -2116,15 +2210,27 @@ string NiFloatData::asString() {
 	out << setprecision(1);
 
 	out << "Key Count:  " << uint(keys.size()) << endl
-		<< "Key Type:  " << keyType << endl;
+		<< "Key Type:  " << keyType << endl
+		<< "Keys:" << endl;
 
 	if (verbose) {
 		vector<Key<float> >::iterator it;
 		for ( it = keys.begin(); it != keys.end(); ++it ) {
-			out << "Key Time:  " << it->time << "  Data: " << it->data << "  Forward: " << it->forward_tangent << "  Back: " << it->backward_tangent << endl;
+			for (uint i = 0; i < keys.size(); i++) {
+				//Always print the time and data
+				out << "   " << i + 1 << ")   Time:  " << it->time << "   Data:  " << it->data;
+				if ( keyType == 2 ) {
+					//Uses Quadratic interpolation
+					out << "   FT:  " << it->forward_tangent << "   BT:  " << it->backward_tangent;
+				} else if ( keyType == 3 ) {
+					//Uses TBC interpolation
+					out << "   T:  " << it->tension << "   B:  " << it->bias << "   C:  " << it->continuity;
+				}
+				out << endl;
+			}
 		}
 	} else {
-		out << "<<Data Not Shown>>" << endl;
+		out << "   <<Data Not Shown>>" << endl;
 	}
 	
 	return out.str();
@@ -2391,6 +2497,223 @@ string NiPalette::asString() {
 	return out.str();
 }
 
+/***********************************************************
+ * NiSkinPartition methods
+ **********************************************************/
+
+void NiSkinPartition::Read( ifstream& file, unsigned int version ) {
+
+	uint numPartitions = ReadUInt( file );
+	partitions.resize( numPartitions );
+	
+	vector<SkinPartition>::iterator it;
+	for (it = partitions.begin(); it != partitions.end(); ++it ) {
+
+		//Read counts
+		ushort numVertices = ReadUShort( file );
+		ushort numTriangles = ReadUShort( file );
+		ushort numBones = ReadUShort( file );
+		ushort numStrips = ReadUShort( file );
+		ushort numWeights = ReadUShort( file );
+
+		//Read bones
+		it->bones.resize( numBones );
+		NifStream( it->bones, file );
+
+		//Read vertex map
+		//After version 10.1.0.0, the vertex map is conditioned on a bool
+		bool hasVertexMap = true;
+		if ( version >= VER_10_1_0_0 ) {
+			hasVertexMap = ReadBool( file, version );
+		}
+
+		if ( hasVertexMap ) {
+			it->vertexMap.resize( numVertices );
+			NifStream( it->vertexMap, file );
+		}
+
+		//Read vertex weights
+		//After version 10.1.0.0, the vertex weights are conditioned on a bool
+		bool hasVertexWeights = true;
+		if ( version >= VER_10_1_0_0 ) {
+			hasVertexWeights = ReadBool( file, version );
+		}
+
+		if ( hasVertexWeights ) {
+			//Resize vectors 2 deep
+			it->vertexWeights.resize( numVertices );
+			for ( uint i = 0; i < it->vertexWeights.size(); ++i ) {
+				it->vertexWeights[i].resize( numWeights );
+			}
+			//Read it all
+			NifStream( it->vertexWeights, file );
+		}
+
+		//Read strip lenghts, resize strip vectors as we go.
+		it->strips.resize( numStrips );
+		for ( uint i = 0; i < it->strips.size(); ++i ) {
+			it->strips[i].resize( ReadUShort( file ) );
+		}
+
+		//Read triangle strip points
+		//After version 10.1.0.0, the triangle strip points are conditioned on a bool
+		bool hasStrips = true;
+		if ( version >= VER_10_1_0_0 ) {
+			hasStrips = ReadBool( file, version );
+		}
+
+		if ( hasStrips ) {
+			//Read 2 deep
+			NifStream( it->strips, file );
+		}
+
+		//Read triangles
+		//Triangles only exist if numStrips == 0
+		if ( it->strips.size() == 0 ) {
+			it->triangles.resize( numTriangles );
+			NifStream( it->triangles, file );
+		}
+
+		//This bool exists in all versions
+		bool hasBoneIndices = ReadBool( file, version );
+
+		if ( hasBoneIndices ) {
+			//Resize vectors 2 deep
+			it->boneIndices.resize( numVertices );
+			for ( uint i = 0; i < it->vertexWeights.size(); ++i ) {
+				it->boneIndices[i].resize( numWeights );
+			}
+			//Read it all
+			NifStream( it->boneIndices, file );
+		}
+	}
+}
+
+void NiSkinPartition::Write( ofstream& file, unsigned int version ) {
+
+	WriteUInt( uint(partitions.size()), file );
+
+	vector<SkinPartition>::iterator it;
+	for (it = partitions.begin(); it != partitions.end(); ++it ) {
+		//Write counts
+		WriteUShort( ushort( it->vertexMap.size()), file );
+		WriteUShort( ushort( it->triangles.size()), file );
+		WriteUShort( ushort( it->bones.size()), file );
+		WriteUShort( ushort( it->strips.size()), file );
+		WriteUShort( ushort( it->vertexWeights.size()), file );
+
+		//Write bones
+		NifStream( it->bones, file );
+
+		//Write vertex map
+		//After version 10.1.0.0, the vertex map is conditioned on a bool
+		if ( version >= VER_10_1_0_0 ) {
+			WriteBool( it->vertexMap.size() > 0, file, version );
+		}
+
+		NifStream( it->vertexMap, file );
+
+		//Write vertex weights
+		//After version 10.1.0.0, the vertex weights are conditioned on a bool
+		if ( version >= VER_10_1_0_0 ) {
+			WriteBool( it->vertexWeights.size() > 0, file, version );
+		}
+
+		//Write vertex weights - 2 deep
+		NifStream( it->vertexWeights, file );
+
+		//Write strip lenghts
+		for ( uint i = 0; i < it->strips.size(); ++i ) {
+			WriteUShort( ushort(it->strips.size()), file );
+		}
+
+		//Write triangle strip points
+		//After version 10.1.0.0, the triangle strip points are conditioned on a bool
+		if ( version >= VER_10_1_0_0 ) {
+			WriteBool( it->strips.size() > 0, file, version );
+		}
+
+		//Write strip points - 2 deep
+		NifStream( it->strips, file );
+
+		//Write triangles
+		//Triangles only exist if numStrips == 0
+		if ( it->strips.size() == 0 ) {
+			NifStream( it->triangles, file );
+		}
+
+		//This bool exists in all versions
+		WriteBool( it->boneIndices.size() > 0, file, version );
+
+		//Write bone indices - 2 deep
+		NifStream( it->boneIndices, file );
+	}
+}
+
+
+string NiSkinPartition::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	int count = 0;
+	vector<SkinPartition>::iterator it;
+	for (it = partitions.begin(); it != partitions.end(); ++it ) {
+		count++;
+		//Write counts
+		out << "Skin Partition " << count << ":" << endl
+			<< "   Vertex Count:  " << ushort(it->vertexMap.size()) << endl
+			<< "   Triangle Count:  " << ushort(it->triangles.size()) << endl
+			<< "   Bone Count:  " << ushort(it->bones.size()) << endl
+			<< "   Triangle Strip Count:  " << ushort(it->strips.size()) << endl
+			<< "   Vertex Weight Count:  " << ushort(it->vertexWeights.size()) << endl;
+
+		if (verbose) {
+			out << "   Bones:" << endl;
+			for ( uint i = 0; i < it->bones.size(); ++i ) {
+				out << "      " << i + 1 << ":  " << it->bones[i] << endl;
+			}
+
+			out << "   Vertex Map:" << endl;
+			for ( uint i = 0; i < it->vertexMap.size(); ++i ) {
+				out << "      " << i + 1 << ":  " << it->vertexMap[i] << endl;
+			}
+
+			out << "   Vertex Weights:" << endl;
+			for ( uint i = 0; i < it->vertexWeights.size(); ++i ) {
+				out << "Group " << i + 1 << ":" << endl;
+				for ( uint j = 0; j < it->vertexWeights[i].size(); ++j ) {
+					out << "         " << j + 1 << ":  " << it->vertexWeights[i][j] << endl;
+				}
+			}
+
+			out << "   Triangle Strips:" << endl;
+			for ( uint i = 0; i < it->strips.size(); ++i ) {
+				out << "Strip " << i + 1 << ":" << endl;
+				for ( uint j = 0; j < it->strips[i].size(); ++j ) {
+					out << "         " << j + 1 << ":  " << it->strips[i][j] << endl;
+				}
+			}
+
+			out << "   Triangles:" << endl;
+			for ( uint i = 0; i < it->triangles.size(); ++i ) {
+				out << "      " << i + 1 << ":  " << setw(10) << it->triangles[i].v1 << "," << setw(10) << it->triangles[i].v2 << "," << setw(10) << it->triangles[i].v3 << endl;
+			}
+
+			out << "   Bone Indices:" << endl;
+			for ( uint i = 0; i < it->boneIndices.size(); ++i ) {
+				out << "Group " << i + 1 << ":" << endl;
+				for ( uint j = 0; j < it->boneIndices[i].size(); ++j ) {
+					out << "         " << j + 1 << ":  " << it->boneIndices[i][j] << endl;
+				}
+			}
+		} else {
+			out << "   <<Data Not Shown>>" << endl;
+		}
+	}
+
+	return out.str();
+}
 
 /***********************************************************
  * NiPixelData methods
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
index c1638c6df9b649bccee84cde4227f9ea787e05e5..3968c02567938610d7086e77003815e920929d7f 100644
--- a/NIF_Blocks.h
+++ b/NIF_Blocks.h
@@ -183,6 +183,13 @@ public:
 	~AParentNode() {}
 };
 
+class AParticleNode : public ANode {
+public:
+	AParticleNode();
+	void Init() {}
+	~AParticleNode() {}
+};
+
 class AShape : public ANode {
 public:
 	AShape();
@@ -197,6 +204,13 @@ public:
 	~AProperty() {}
 };
 
+class AParticleProperty : public AControllable {
+public:
+	AParticleProperty();
+	void Init() {}
+	~AParticleProperty() {}
+};
+
 class AController : public ABlock {
 public:
 	AController();
@@ -660,7 +674,7 @@ private:
 /**
  * NiTriStripsData - Holds mesh data using strips of triangles.
  */
-class NiTriStripsData : public AShapeData {
+class NiTriStripsData : public AShapeData, public ITriStripsData {
 public:
 	NiTriStripsData() {}
 	~NiTriStripsData() {}
@@ -670,6 +684,19 @@ public:
 
 	string GetBlockType() { return "NiTriStripsData"; }
 
+	void * QueryInterface( int id );
+
+	//--ITriStripsData--//
+	//Counts
+	short GetStripCount();
+	void SetStripCount(int n);
+	short GetTriangleCount();
+	//Getters
+	vector<short> GetStrip( int index );
+	vector<Triangle> GetTriangles();
+	//Setter
+	void SetStrip( int index, const vector<short> & in );
+
 private:
 	vector< vector<short> > strips;
 };
@@ -679,17 +706,19 @@ private:
  */
 class NiCollisionData : public AData {
 public:
-	NiCollisionData() {
-		AddAttr( attr_int, "Unknown Int 1" );
-		AddAttr( attr_int, "Unknown Int 2" );
-		AddAttr( attr_byte, "Unknown Byte" );
-		AddAttr( attr_int, "Unknown Int 3" );
-		AddAttr( attr_int, "Unknown Int 4" );
-		AddAttr( attr_float3, "Radius" );
-	}
+	NiCollisionData() {}
 	~NiCollisionData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
 
 	string GetBlockType() { return "NiCollisionData"; }
+private:
+	int unknownInt1, collisionType, unknownInt2;
+	byte unknownByte;
+	fVector3 unknown3Floats;
+	float unknown15Floats[15];
+	float unknown8Floats[8]; 
 };
 
 
@@ -811,7 +840,7 @@ public:
  * NiAutoNormalParticles
  */
 
-class NiAutoNormalParticles : public ANode {
+class NiAutoNormalParticles : public AParticleNode {
 public:
 	NiAutoNormalParticles();
 	void Init() {}
@@ -823,7 +852,7 @@ public:
  * NiRotatingParticles
  */
 
-class NiRotatingParticles : public ANode {
+class NiRotatingParticles : public AParticleNode {
 public:
 	NiRotatingParticles();
 	void Init() {}
@@ -859,7 +888,7 @@ public:
  * NiParticleMeshes
  */
 
-class NiParticleMeshes : public ANode {
+class NiParticleMeshes : public AParticleNode {
 public:
 	NiParticleMeshes();
 	void Init() {}
@@ -871,7 +900,7 @@ public:
  * NiGravity
  */
 
-class NiGravity : public AControllable {
+class NiGravity : public AParticleProperty {
 public:
 	NiGravity();
 	void Init() {}
@@ -883,7 +912,7 @@ public:
  * NiParticleBomb
  */
 
-class NiParticleBomb : public AControllable {
+class NiParticleBomb : public AParticleProperty {
 public:
 	NiParticleBomb();
 	void Init() {}
@@ -895,7 +924,7 @@ public:
  * NiPlanarCollider
  */
 
-class NiPlanarCollider : public AControllable {
+class NiPlanarCollider : public AParticleProperty {
 public:
 	NiPlanarCollider();
 	void Init() {}
@@ -904,10 +933,10 @@ public:
 }; 
 
 /**
- * NiPlanarCollider
+ * NiSphericalCollider
  */
 
-class NiSphericalCollider : public AControllable {
+class NiSphericalCollider : public AParticleProperty {
 public:
 	NiSphericalCollider();
 	void Init() {}
@@ -919,7 +948,7 @@ public:
  * NiParticleGrowFade
  */
 
-class NiParticleGrowFade : public AControllable {
+class NiParticleGrowFade : public AParticleProperty {
 public:
 	NiParticleGrowFade();
 	void Init() {}
@@ -931,7 +960,7 @@ public:
  * NiParticleMeshModifier
  */
 
-class NiParticleMeshModifier : public AControllable {
+class NiParticleMeshModifier : public AParticleProperty {
 public:
 	NiParticleMeshModifier();
 	void Init() {}
@@ -943,7 +972,7 @@ public:
  * NiParticleColorModifier
  */
 
-class NiParticleColorModifier : public AControllable {
+class NiParticleColorModifier : public AParticleProperty {
 public:
 	NiParticleColorModifier();
 	void Init() {}
@@ -955,7 +984,7 @@ public:
  * NiGravity
  */
 
-class NiParticleRotation : public AControllable {
+class NiParticleRotation : public AParticleProperty {
 public:
 	NiParticleRotation();
 	void Init() {}
@@ -1011,6 +1040,9 @@ class NiKeyframeData : public AData, public IKeyframeData {
 
 		KeyType scaleType;
 		vector< Key<float> > scaleKeys;
+
+		KeyType xyzTypes[3];
+		vector< Key<float> > xyzKeys[3];
 };
 
 /**
@@ -1038,10 +1070,24 @@ private:
 
 class NiSkinPartition : public AData {
 public:
-	NiSkinPartition();
+	NiSkinPartition() {}
 	void Init() {}
 	~NiSkinPartition() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+
 	string GetBlockType() { return "NiSkinPartition"; }
+private:
+	struct SkinPartition {
+		vector<ushort> bones;
+		vector<ushort> vertexMap;
+		vector< vector<float> > vertexWeights;
+		vector< vector<ushort> > strips;
+		vector<Triangle> triangles;
+		vector< vector<byte> > boneIndices;
+	};
+	vector<SkinPartition> partitions;
 };
 
 
@@ -1102,6 +1148,7 @@ class NiSkinData : public AData, public ISkinData, public ISkinDataInternal {
 	public:
 
 		NiSkinData() { 
+			AddAttr( attr_link, "Skin Partition" );
 			SetIdentity33(rotation);
 			translation[0] = 0.0f;
 			translation[1] = 0.0f;
@@ -1171,8 +1218,8 @@ public:
 	string GetBlockType() { return "NiColorData"; };
 
 private:
-	uint keyType;
-	vector< Key<fVector4> > keys;
+	KeyType keyType;
+	vector< Key<Color> > keys;
 };
 
 /**
diff --git a/NIF_IO.cpp b/NIF_IO.cpp
index 0bd87225865abf92c1a9b92d316193dd3b3ca24a..560466009bfc0336e953e87c4f5cc6b663096b2d 100644
--- a/NIF_IO.cpp
+++ b/NIF_IO.cpp
@@ -356,3 +356,69 @@ ostream & operator<<(ostream & lh, Bin & rh) {
 	}
 	return lh;
 }
+
+//--Overloaded versions of Read/Write functions ReadData/WriteData
+
+void NifStream( uint & val, ifstream& in ) { val = ReadUInt( in ); };
+void NifStream( ushort & val, ifstream& in ) { val = ReadUShort( in ); };
+void NifStream( byte & val, ifstream& in ) { val = ReadByte( in ); };
+void NifStream( float & val, ifstream& in ) { val = ReadFloat( in ); };
+void NifStream( string & val, ifstream& in ) { val = ReadString( in ); };
+void NifStream( KeyType & val, ifstream& in ) { val = KeyType(ReadUInt( in )); };
+
+void NifStream( Vector3 & val, ifstream& in ) {
+	val.x = ReadFloat( in );
+	val.y = ReadFloat( in );
+	val.z = ReadFloat( in );
+};
+
+void NifStream( Quaternion & val, ifstream& in ) {
+	val.w = ReadFloat( in );
+	val.x = ReadFloat( in );
+	val.y = ReadFloat( in );
+	val.z = ReadFloat( in );
+};
+
+void NifStream( Color & val, ifstream& in ) {
+	val.r = ReadFloat( in );
+	val.g = ReadFloat( in );
+	val.b = ReadFloat( in );
+	val.a = ReadFloat( in );
+};
+
+void NifStream( Triangle & val, ifstream& in ) {
+	val.v1 = ReadUShort( in );
+	val.v2 = ReadUShort( in );
+	val.v3 = ReadUShort( in );
+};
+
+
+
+void NifStream( uint & val, ofstream& out ) { WriteUInt( val, out ); }
+void NifStream( ushort & val, ofstream& out ) { WriteUShort( val, out ); }
+void NifStream( byte & val, ofstream& out ) { WriteByte( val, out ); }
+void NifStream( float & val, ofstream& out ) { WriteFloat( val, out ); }
+void NifStream( string & val, ofstream& out ) { WriteString( val, out ); }
+void NifStream( KeyType & val, ofstream& out ) { WriteUInt( val, out ); }
+void NifStream( Vector3 & val, ofstream& out ) {
+	WriteFloat( val.x, out );
+	WriteFloat( val.y, out );
+	WriteFloat( val.z, out );
+};
+void NifStream( Quaternion & val, ofstream& out ) {
+	WriteFloat( val.w, out );
+	WriteFloat( val.x, out );
+	WriteFloat( val.y, out );
+	WriteFloat( val.z, out );
+};
+void NifStream( Color & val, ofstream& out ) {
+	WriteFloat( val.r, out );
+	WriteFloat( val.g, out );
+	WriteFloat( val.b, out );
+	WriteFloat( val.a, out );
+};
+void NifStream( Triangle & val, ofstream& out ) {
+	WriteUShort( val.v1, out );
+	WriteUShort( val.v2, out );
+	WriteUShort( val.v3, out );
+};
\ No newline at end of file
diff --git a/NIF_IO.h b/NIF_IO.h
index b70ba8631c58e57147221b0af0bfa5f321b9a46a..431bcc0a38b21f7ce4cde367ea62456572637599 100644
--- a/NIF_IO.h
+++ b/NIF_IO.h
@@ -40,6 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. */
 #include <fstream>
 #include <iomanip>
 #include <string>
+#include "niflib.h"
 using namespace std;
 
 /* TYPE DEFINITIONS */
@@ -187,25 +188,60 @@ ostream & operator<<(ostream & lh, nifAlphaFormat & rh);
  * Read utility functions
  */
 uint ReadUInt( ifstream& in );
-
 ushort ReadUShort( ifstream& in );
-
 byte ReadByte( ifstream& in );
-
 float ReadFloat( ifstream &in );
-
 string ReadString( ifstream &in );
-
 bool ReadBool( ifstream &in, unsigned int version );
-
 void ReadUSVector3( usVector3& vec, ifstream& in );
-
 void ReadFVector2( fVector2& fvec, ifstream& in );
-
 void ReadFVector3( fVector3& fvec, ifstream& in );
-
 void ReadFVector4( fVector4& fvec, ifstream& in );
 
+//Read
+void NifStream( uint & val, ifstream& in );
+void NifStream( ushort & val, ifstream& in );
+void NifStream( byte & val, ifstream& in );
+void NifStream( float & val, ifstream& in );
+void NifStream( string & val, ifstream& in );
+void NifStream( Vector3 & val, ifstream& in );
+void NifStream( Quaternion & val, ifstream& in );
+void NifStream( KeyType & val, ifstream& in );
+void NifStream( Color & val, ifstream& in );
+void NifStream( Triangle & val, ifstream& in );
+
+template <class T> 
+void NifStream( Key<T> & key, ifstream& file, KeyType type ) {
+	key.time = ReadFloat( file );
+
+	//If key type is not 1, 2, or 3, throw an exception
+	if ( type < 1 || type > 3 ) {
+		throw runtime_error("Invalid key type.");
+	}
+
+	//Read data based on the type of key
+	NifStream( key.data, file );
+	if ( type == QUADRATIC_KEY ) {
+		//Uses Quadratic interpolation
+		NifStream( key.forward_tangent, file );
+		NifStream( key.backward_tangent, file );
+	} else if ( type == TBC_KEY ) {
+		//Uses TBC interpolation
+		key.tension = ReadFloat( file );
+		key.bias = ReadFloat( file );
+		key.continuity = ReadFloat( file );
+	}
+}
+
+template <class T>
+void NifStream( vector<T> & val, ifstream& file ) {
+	vector<T>::iterator it;
+	for ( it = val.begin(); it != val.end(); ++it ) {
+		NifStream( *it, file );
+	}
+}
+
+
 /**
  * Write utility functions.
  */
@@ -231,6 +267,49 @@ void WriteFVector4( fVector4& fvec, ofstream& out );
 
 void WriteBlockName( const char* name, uint nameLength, ofstream& out );
 
+//Write
+void NifStream( uint & val, ofstream& out );
+void NifStream( ushort & val, ofstream& out );
+void NifStream( byte & val, ofstream& out );
+void NifStream( float & val, ofstream& out );
+void NifStream( string & val, ofstream& out );
+void NifStream( Vector3 & val, ofstream& out );
+void NifStream( Quaternion & val, ofstream& out );
+void NifStream( KeyType & val, ofstream& out );
+void NifStream( Color & val, ofstream& out );
+void NifStream( Triangle & val, ofstream& out );
+
+template <class T> 
+void NifStream( Key<T> & key, ofstream& file, KeyType type ) {
+	WriteFloat( key.time, file );
+
+	//If key type is not 1, 2, or 3, throw an exception
+	if ( type < 1 || type > 3 ) {
+		throw runtime_error("Invalid key type.");
+	}
+
+	//Read data based on the type of key
+	NifStream( key.data, file );
+	if ( type == QUADRATIC_KEY ) {
+		//Uses Quadratic interpolation
+		NifStream( key.forward_tangent, file );
+		NifStream( key.backward_tangent, file );
+	} else if ( type == TBC_KEY ) {
+		//Uses TBC interpolation
+		WriteFloat( key.tension, file);
+		WriteFloat( key.bias, file);
+		WriteFloat( key.continuity, file);
+	}
+}
+
+template <class T>
+void NifStream( vector<T> & val, ofstream& file ) {
+	vector<T>::iterator it;
+	for ( it = val.begin(); it != val.end(); ++it ) {
+		NifStream( *it, file );
+	}
+}
+
 class NIF;
 
 class nifIndex {
diff --git a/nif_attrs.h b/nif_attrs.h
index c613606a0abca5e2ea1790f31314f2ff8b3a92ea..2bc7aa186ea32292e026fddeb134a7c1f030e9ce 100644
--- a/nif_attrs.h
+++ b/nif_attrs.h
@@ -859,6 +859,19 @@ public:
 				data.unknownShort = ReadUShort( in );
 			}
 
+			//From version 10.1.0.0 and up, this unknown data block may exist
+			if ( version >= VER_10_1_0_0 ) {
+				data.hasUnknownData = ReadBool( in, version );
+				if ( data.hasUnknownData == true ) {
+					for (int i = 0; i < 5; ++i ) {
+						data.unknown5Floats[i] = ReadFloat( in );;
+					}
+					data.unknownInt = ReadUInt( in );
+					data.unknownFloat1 = ReadFloat( in );
+					data.unknownFloat2 = ReadFloat( in );
+				}
+			}
+
 			if ( _isBumpMap ) {
 				data.bmLumaScale = ReadFloat( in );
 				data.bmLumaOffset = ReadFloat( in );
@@ -880,10 +893,25 @@ public:
 			WriteUInt( data.textureSet, out );
 			WriteUShort( data.PS2_L, out );
 			WriteUShort( data.PS2_K, out );
+
 			//unknownShort exists up to version 4.1.0.12
 			if ( version <= VER_4_1_0_12 ) {
 				WriteUShort( data.unknownShort, out );
 			}
+
+			//From version 10.1.0.0 and up, this unknown data block may exist
+			if ( version >= VER_10_1_0_0 ) {
+				WriteBool( data.hasUnknownData, out, version );
+				if ( data.hasUnknownData == true ) {
+					for (int i = 0; i < 5; ++i ) {
+						WriteFloat( data.unknown5Floats[i], out );;
+					}
+					WriteUInt( data.unknownInt, out );
+					WriteFloat( data.unknownFloat1, out );
+					WriteFloat( data.unknownFloat2, out );
+				}
+			}
+
 			if ( _isBumpMap ) {
 				WriteFloat( data.bmLumaScale, out );
 				WriteFloat( data.bmLumaOffset, out );
@@ -950,7 +978,24 @@ public:
 				<< "   Texture Set:  " << data.textureSet << endl
 				<< "   PS2 L Setting:  " << data.PS2_L << endl
 				<< "   PS2 K Setting:  " << data.PS2_K << endl
-				<< "   Unknown Short:  " << data.unknownShort;
+				<< "   Unknown Short:  " << data.unknownShort
+				<< "   Unknown Data:   ";
+
+
+			//From version 10.1.0.0 and up, this unknown data block may exist
+			if ( data.hasUnknownData == true ) {
+				out << endl
+					<< "      Unknown 5 Floats:" << endl;
+				for (int i = 0; i < 5; ++i ) {
+					out << "         " << i + 1 << ":  " << data.unknown5Floats[i] << endl;
+				}
+				out << "      Unknown Int:  " << data.unknownInt << endl
+					<< "      Unknown Float 1:  " << data.unknownFloat1 << endl
+					<< "      Unknown Float 2:  " << data.unknownFloat2 << endl;
+			} else {
+				out << "None" << endl;
+			}
+
 			if ( _isBumpMap ) {
 				out << endl
 					<< "   BumpMap Info:" << endl
@@ -1411,4 +1456,52 @@ private:
 };
 
 
+
+class LODRangeGroupAttr : public AAttr {
+public:
+	LODRangeGroupAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {}
+	~LODRangeGroupAttr() {}
+	string GetType() const { return "lodrangegroup"; }
+
+	void ReadAttr( ifstream& in, unsigned int version ) {
+		int numRanges = ReadUInt( in );
+		ranges.resize( numRanges );
+		for ( uint i = 0; i < ranges.size(); ++i ) {
+			ranges[i].near = ReadFloat( in );
+			ranges[i].far = ReadFloat( in );		
+		}
+	}
+
+	void WriteAttr( ofstream& out, unsigned int version ) {
+		WriteUInt( uint(ranges.size()), out );
+
+		for ( uint i = 0; i < ranges.size(); ++i ) {
+			WriteFloat( ranges[i].near, out );
+			WriteFloat( ranges[i].far, out );		
+		}
+	}
+
+	string asString() const {
+		stringstream out;
+		out.setf(ios::fixed, ios::floatfield);
+		out << setprecision(1);
+
+		out << uint(ranges.size()) << endl;
+
+		for ( uint i = 0; i < ranges.size(); ++i ) {
+			out << "   " << i + 1 << ")   Near:  " << ranges[i].near << "   Far:  " << ranges[i].far << endl;
+		}
+
+		return out.str();
+	}
+	
+private:
+	struct LODRange {
+		float near;
+		float far;
+	};
+	vector<LODRange> ranges;
+};
+
+
 #endif
diff --git a/niflib.cpp b/niflib.cpp
index c9036f78bf4d0e0acd43e33b31297049aa597ebe..633694478b43e4cf58679d72e9fd771ccc214cc9 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -183,6 +183,11 @@ vector<blk_ref> ReadNifList( string file_name ) {
 	vector<blk_ref> blocks( numBlocks );
 	string blockName;
 	for (uint i = 0; i < numBlocks; i++) {
+
+		//Check for EOF
+		if (in.eof() ) {
+			throw runtime_error("End of file reached prematurely.  This NIF may be corrupt or improperly supported.");
+		}
 	
 		//There are two ways to read blocks, one before version 5.0.0.1 and one after that
 		if ( version >= 0x05000001 ) {
@@ -485,3 +490,7 @@ ITextKeyExtraData * QueryTextKeyExtraData ( blk_ref & block ) {
 IMorphData * QueryMorphData ( blk_ref & block ) {
 	return (IMorphData*)block->QueryInterface( ID_MORPH_DATA );
 }
+
+ITriStripsData * QueryTriStripsData ( blk_ref & block ) {
+	return (ITriStripsData*)block->QueryInterface( ID_TRI_STRIPS_DATA );
+}
diff --git a/niflib.h b/niflib.h
index 03c8cb85b48775aab2e1d21f0b4f3dfcf65931ca..76c0eec4215b5a11730c71f166377313113fb4b4 100644
--- a/niflib.h
+++ b/niflib.h
@@ -58,6 +58,7 @@ class ISkinData;
 class IKeyframeData;
 class ITextKeyExtraData;
 class IMorphData;
+class ITriStripsData;
 class INode;
 class blk_ref;
 class attr_ref;
@@ -78,6 +79,7 @@ const int ID_KEYFRAME_DATA = 3;
 const int ID_TEXT_KEY_EXTRA_DATA = 4;
 const int ID_MORPH_DATA = 5;
 const int ID_SHAPE_DATA = 6;
+const int ID_TRI_STRIPS_DATA = 7;
 
 //Attribute types
 const enum AttrTypes {
@@ -105,7 +107,7 @@ const int VER_20_0_0_4 = 0x14000004;
 #endif
 
 //Key Types
-enum KeyType { LINEAR_KEY = 1, QUADRATIC_KEY = 2, TBC_KEY = 3 };
+enum KeyType { LINEAR_KEY = 1, QUADRATIC_KEY = 2, TBC_KEY = 3, XYZ_ROTATION_KEY = 4 };
 
 //--Main Functions--//
 
@@ -139,6 +141,7 @@ INode * QueryNode( blk_ref & block );
 IKeyframeData * QueryKeyframeData( blk_ref & block );
 ITextKeyExtraData * QueryTextKeyExtraData ( blk_ref & block );
 IMorphData * QueryMorphData ( blk_ref & block );
+ITriStripsData * QueryTriStripsData ( blk_ref & block );
 
 //--Simlpe Structures--//
 
@@ -484,6 +487,21 @@ public:
 	virtual void SetTriangles( const vector<Triangle> & in ) = 0;
 };
 
+class ITriStripsData {
+public:
+	ITriStripsData() {}
+	virtual ~ITriStripsData () {}
+	//Counts
+	virtual short GetTriangleCount() = 0;
+	virtual short GetStripCount() = 0;
+	virtual void SetStripCount(int n) = 0;
+	//Getter
+	virtual vector<short> GetStrip( int index ) = 0;
+	virtual vector<Triangle> GetTriangles() = 0;
+	//Setter
+	virtual void SetStrip( int index, const vector<short> & in ) = 0;
+};
+
 class ISkinData {
 public:
 	ISkinData() {}
@@ -1016,10 +1034,16 @@ struct Texture {
 	int textureSet;
 	short PS2_L;
 	short PS2_K;
-	short unknownShort;
+	short unknownShort;  //exists up to version 4.1.0.12
+	//Unknown Block in version 10.1.0.0 and up
+	bool hasUnknownData;
+	float unknown5Floats[5];
+	int unknownInt;
+	float unknownFloat1, unknownFloat2;
+	//Bitmap block - only exists if this texture is in the bitmap slot
 	float bmLumaOffset;
 	float bmLumaScale;
-	Matrix22 bmMatrix;
+	Matrix22 bmMatrix;	
 };
 
 struct TextureSource {
diff --git a/pyniflib.i b/pyniflib.i
index b78954fc6397a9ab58ae812af9fe2de30a3879bd..876e636bf6735fb0fd4b2147564d4394edef4c0f 100644
--- a/pyniflib.i
+++ b/pyniflib.i
@@ -62,6 +62,7 @@ struct Key {
 };
 
 %template(vector_float) std::vector<float>;
+%template(vector_short) std::vector<short>;
 %template(vector_attr_ref) std::vector<attr_ref>;
 %template(vector_blk_ref) std::vector<blk_ref>;
 %template(list_blk_ref) std::list<blk_ref>;