From ba3074ad79b1146c7effcb13449ce6741c28800c Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Sat, 12 Nov 2005 21:06:48 +0000
Subject: [PATCH] =?UTF-8?q?Finished=20basic=20version=20support=20?=
 =?UTF-8?q?=EF=BF=BD=20still=20need=20to=20implement=20new=20block=20readi?=
 =?UTF-8?q?ng=20scheme=20for=20version=205=20and=20up.=20Updated=20all=20b?=
 =?UTF-8?q?asic=20blocks=20with=20correctly=20versioned=20attributes=20fro?=
 =?UTF-8?q?m=20DocSys=20database.=20Updated=20all=20data=20blocks=20except?=
 =?UTF-8?q?=20for=20NiSkinPartition=20and=20NiControllerSequence=20to=20re?=
 =?UTF-8?q?act=20correctly=20to=20known=20version=20differences=20up=20to?=
 =?UTF-8?q?=2010.1.0.0.=20All=20blocks=20but=20the=20above=20now=20have=20?=
 =?UTF-8?q?both=20a=20Read=20and=20a=20Write=20function.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 NIF_Blocks.cpp | 1136 ++++++++++++++++++++++++------------
 NIF_Blocks.h   | 1506 ++++++++++++++++++++++++------------------------
 NIF_IO.cpp     |   20 +
 NIF_IO.h       |    6 +-
 nif_attrs.h    |  194 ++++---
 niflib.cpp     |   58 +-
 niflib.h       |   43 +-
 7 files changed, 1740 insertions(+), 1223 deletions(-)

diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
index 0877874e..1852a05f 100644
--- a/NIF_Blocks.cpp
+++ b/NIF_Blocks.cpp
@@ -48,7 +48,7 @@ extern string current_file;
  * ABlock methods
  **********************************************************/
 
-ABlock::ABlock() : _ref_count(0), _block_num(-1) {
+ABlock::ABlock() : _ref_count(0), _block_num(-1), _namable(false), _first_named_ver(0) {
 		//Temporary to test reference counting
 		blocks_in_memory++;
 	}
@@ -67,55 +67,55 @@ ABlock::~ABlock() {
 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 );
+		attr = new IntAttr( name, this, first_ver, last_ver );
 	} else if ( type == "short" ) {
-		attr = new ShortAttr( name, this );
+		attr = new ShortAttr( name, this, first_ver, last_ver );
 	} else if ( type == "byte" ) {
-		attr = new ByteAttr( name, this );
+		attr = new ByteAttr( name, this, first_ver, last_ver );
 	} else if ( type == "float" ) {
-		attr = new FloatAttr( name, this );
+		attr = new FloatAttr( name, this, first_ver, last_ver );
 	} else if ( type == "float3" ) {
-		attr = new Float3Attr( name, this );
+		attr = new Float3Attr( name, this, first_ver, last_ver );
 	} else if ( type == "string" ) {
-		attr = new StringAttr( name, this );
+		attr = new StringAttr( name, this, first_ver, last_ver );
 	} else if ( type == "link" ) {
-		attr = new LinkAttr( name, this );
+		attr = new LinkAttr( name, this, first_ver, last_ver );
 	} else if ( type == "flags" ) {
-		attr = new FlagsAttr( name, this );
+		attr = new FlagsAttr( name, this, first_ver, last_ver );
 	} else if ( type == "matrix33" ) {
-		attr = new MatrixAttr( name, this );
+		attr = new MatrixAttr( name, this, first_ver, last_ver );
 	} else if ( type == "linkgroup" ) {
-		attr = new LinkGroupAttr( name, this );
+		attr = new LinkGroupAttr( name, this, first_ver, last_ver );
 	} else if ( type == "bones" ) {
-		attr = new BoneAttr( name, this );
+		attr = new BoneAttr( name, this, first_ver, last_ver );
 	} else if ( type == "bbox" ) {
-		attr = new BBoxAttr( name, this );
+		attr = new BBoxAttr( name, this, first_ver, last_ver );
 	} else if ( type == "condint" ) {
-		attr = new CIntAttr( name, this );
+		attr = new CIntAttr( name, this, first_ver, last_ver );
 	} else if ( type == "vertmode" ) {
-		attr = new VertModeAttr( name, this );
+		attr = new VertModeAttr( name, this, first_ver, last_ver );
 	} else if ( type == "lightmode" ) {
-		attr = new LightModeAttr( name, this );
+		attr = new LightModeAttr( name, this, first_ver, last_ver );
 	} else if ( type == "texture" ) {
-		attr = new TextureAttr( name, this );
+		attr = new TextureAttr( name, this, first_ver, last_ver, false );
 	} else if ( type == "bumpmap" ) {
-		attr = new TextureAttr( name, this, true );
+		attr = new TextureAttr( name, this, first_ver, last_ver, true );
 	} else if ( type == "applymode" ) {
-		attr = new ApplyModeAttr( name, this );
+		attr = new ApplyModeAttr( name, this, first_ver, last_ver );
 	} else if ( type == "texsource" ) {
-		attr = new TexSourceAttr( name, this );
+		attr = new TexSourceAttr( name, this, first_ver, last_ver );
 	} else if ( type == "pixellayout" ) {
-		attr = new PixelLayoutAttr( name, this );
+		attr = new PixelLayoutAttr( name, this, first_ver, last_ver );
 	} else if ( type == "mipmapformat" ) {
-		attr = new MipMapFormatAttr( name, this );
+		attr = new MipMapFormatAttr( name, this, first_ver, last_ver );
 	} else if ( type == "alphaformat" ) {
-		attr = new AlphaFormatAttr( name, this );
+		attr = new AlphaFormatAttr( name, this, first_ver, last_ver );
 	} else if ( type == "nodeancestor" ) {
-		attr = new NodeAncestorAttr( name, this );
+		attr = new NodeAncestorAttr( name, this, first_ver, last_ver );
 	} else if ( type == "skeletonroot" ) {
-		attr = new SkeletonRootAttr( name, this );
+		attr = new SkeletonRootAttr( name, this, first_ver, last_ver );
 	} else if ( type == "particlegroup" ) {
-		attr = new ParticleGroupAttr( name, this );
+		attr = new ParticleGroupAttr( name, this, first_ver, last_ver );
 	} else {
 		cout << type << endl;
 		throw runtime_error("Unknown attribute type requested.");
@@ -123,7 +123,6 @@ void ABlock::AddAttr( string type, string name, unsigned int first_ver, unsigned
 
 	_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) {
@@ -150,44 +149,52 @@ blk_ref ABlock::GetParent() {
 }
 
 void ABlock::Read( ifstream& in, unsigned int version ) {
+	//Read Name if there is one
+	if ( _namable == true && version >= _first_named_ver ) {
+		_name = ReadString( in );
+	}
+
+	//Read Attributes
 	for (unsigned int i = 0; i < _attr_vect.size(); ++i ) {
-		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;
-		}
+		_attr_vect[i]->Read( in, version );
+		//cout << "   " << _attr_vect[i]->GetName() << ":  " << _attr_vect[i]->asString() << endl;
 	}
 }
 
 void ABlock::Write( ofstream& out, unsigned int version ) {
+	//Write Name if there is one
+	if ( _namable == true && version >= _first_named_ver  ) {
+		WriteString( _name, out );
+	}
+
 	//Write Attributes
 	for (unsigned int i = 0; i < _attr_vect.size(); ++i ) {
 		//cout << "Writing " << blk_ref(this) << " " << _attr_vect[i]->GetName() << endl;
-		_attr_vect[i]->Write( out );
+		_attr_vect[i]->Write( out, version );
 	}
 }
 
 string ABlock::asString() {
+	// Create a stringstream and set the floating point format
+	// fixed notation with one decimal place
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
 
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
+	//Output the first parent of this block
 	out << "Parent:  " << GetParent() << endl;
+
+	//Output the name if there is one
+	if ( _namable == true ) {
+		out << "Name:  " << _name << endl;
+	}
+
+	//Output Attributes
 	for (unsigned int i = 0; i < _attr_vect.size(); ++i ) {
 		out << _attr_vect[i]->GetName() << ":  " << _attr_vect[i]->asString() << endl;
 	}
 
-	//if ( _parents.size() > 1 ) {
-	//	cout << endl  << "Parents:";
-	//	for (unsigned int i = 0; i < _parents.size(); ++i ) {
-	//		cout << "   " << _parents[i]->GetBlockType();
-	//	}
-	//	cout << endl << "Node:  " << GetBlockType() << "\a";
-	//	cin.get();
-	//}
-
+	//Return result as a string
 	return out.str();
 }
 
@@ -548,16 +555,16 @@ string NiNode::asString() {
 }
 
 /***********************************************************
- * NiTriShapeData methods
+ * AShapeData methods
  **********************************************************/
 
 /**
- * NiTriShapeData::Read - Assumes block name has already been read from in
+ * AShapeData::Read - Assumes block name has already been read from in
  */
-void NiTriShapeData::Read( ifstream& in, unsigned int version ){
+void AShapeData::Read( ifstream& in, unsigned int version ){
 
 	short vert_count = ReadUShort( in );
-	int hasVertices = ReadUInt( in );
+	bool hasVertices = ReadBool( in, version );;
 	if ( hasVertices != 0 ){
 		vertices.resize( vert_count );
 		for ( uint i = 0; i < vertices.size(); ++i ){
@@ -567,7 +574,13 @@ void NiTriShapeData::Read( ifstream& in, unsigned int version ){
 		}
 	}
 
-	int hasNormals = ReadUInt( in );
+	/// numTexSets up here up after from version 10.0.1.0 on
+	short numTexSets;
+	if ( version >= 0x0A000100 ) {
+		numTexSets = ReadUShort( in );
+	}
+
+	bool hasNormals = ReadBool( in, version );;
 	if ( hasNormals != 0 ){
 		normals.resize( vert_count );
 		for ( uint i = 0; i < normals.size(); ++i ){
@@ -577,10 +590,10 @@ void NiTriShapeData::Read( ifstream& in, unsigned int version ){
 		}
 	}
 
-	GetAttr("Center")->Read( in );
-	GetAttr("Radius")->Read( in );
+	GetAttr("Center")->Read( in, version );
+	GetAttr("Radius")->Read( in, version );
 
-	int hasVertexColors = ReadUInt( in );
+	bool hasVertexColors = ReadBool( in, version );;
 	if ( hasVertexColors != 0 ){
 		colors.resize( vert_count );
 		for ( uint i = 0; i < colors.size(); ++i ){
@@ -590,9 +603,15 @@ void NiTriShapeData::Read( ifstream& in, unsigned int version ){
 			colors[i].a = ReadFloat( in );
 		}
 	}
-
-	short numTexSets = ReadUShort( in );
-	int hasUVs = ReadUInt( in );
+	// numTexSets down here up to version 4.2.2.0
+	if ( version <= 0x04020200 ) {
+		numTexSets = ReadUShort( in );
+	}
+	// hasUVs does not exist after version 4.0.0.2
+	bool hasUVs = true;
+	if ( version <= 0x04000002 ) {
+		hasUVs = ReadBool( in, version );
+	}
 	if ( numTexSets > 0 && hasUVs != 0 ){
 		uv_sets.resize( numTexSets );
 		//UVs = new fVector2[numVertices * numTexSets];
@@ -605,30 +624,14 @@ void NiTriShapeData::Read( ifstream& in, unsigned int 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 );
-		}
-	}
-
-	short matchGroupCount = ReadUShort( in );
-	match_group_mode = ( matchGroupCount != 0 );  // Only record whether or not file prefers to have match data generated
-
-	short sub_count;
-	for ( int i = 0; i < matchGroupCount; ++i ){
-		sub_count = ReadUShort( in );
-		for (ushort j = 0; j < sub_count; ++j) {
-			ReadUShort( in );  // Read data, but don't care what it is
-		}
+	// Unknown Short here from version 10.0.1.0 on
+	// Just read it and throw it away for now
+	if ( version >= 0x0A000100 ) {
+		ReadUShort( in );
 	}
 }
 
-string NiTriShapeData::asString() {
+string AShapeData::asString() {
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
@@ -706,41 +709,20 @@ string NiTriShapeData::asString() {
 	}
 	out << endl;
 
-	out << "Triangles:  " << uint(triangles.size());
-	if (verbose) {
-		for ( uint i = 0; i < triangles.size(); ++i) {
-			if (i % 3 == 0)
-				out << endl << "   ";
-			else
-				out << "  ";
-			
-			out << "(" << setw(5) << triangles[i].v1 << ", " << setw(5) << triangles[i].v2 << ", " << setw(5) << triangles[i].v3 << " )";
-		}
-	} else {
-		out << endl << "<<Data Not Shown>>";
-	}
-	out << endl;
-
-	out << "Match Detection:  ";
-	if ( match_group_mode )
-		out << "On" << endl;
-	else
-		out << "Off" << endl;	
-
 	return out.str();
 }
 
 /**
- * NiTriShapeData::Write - Writes block name to out, in addition to data.
+ * AShapeData::Write
  */
-void NiTriShapeData::Write( ofstream& out, unsigned int version ){
+void AShapeData::Write( ofstream& out, unsigned int version ){
 
 	WriteUShort( short(vertices.size()), out );
 
 	if ( vertices.size() > 0 )
-		WriteUInt( 1, out );
+		WriteBool( true, out, version );
 	else
-		WriteUInt( 0, out );
+		WriteBool( false, out, version );
 
 	for ( uint i = 0; i < vertices.size(); ++i ){
 		WriteFloat( vertices[i].x, out );
@@ -749,9 +731,9 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 	}
 
 	if ( normals.size() > 0 )
-		WriteUInt( 1, out );
+		WriteBool( true, out, version );
 	else
-		WriteUInt( 0, out );
+		WriteBool( false, out, version );
 
 	for ( uint i = 0; i < normals.size(); ++i) {
 		WriteFloat( normals[i].x, out );
@@ -759,13 +741,13 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 		WriteFloat( normals[i].z, out );
 	}
 
-	GetAttr("Center")->Write( out );
-	GetAttr("Radius")->Write( out );
+	GetAttr("Center")->Write( out, version );
+	GetAttr("Radius")->Write( out, version );
 
 	if ( colors.size() > 0 )
-		WriteUInt( 1, out );
+		WriteBool( true, out, version );
 	else
-		WriteUInt( 0, out );
+		WriteBool( false, out, version );
 
 	for ( uint i = 0; i < colors.size(); ++i ){
 		WriteFloat( colors[i].r, out );
@@ -776,9 +758,9 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 
 	WriteUShort( ushort(uv_sets.size()), out );
 	if ( uv_sets.size() > 0 && uv_sets[0].size() > 0 ) // hasUVs
-		WriteUInt( 1, out );
+		WriteBool( true, out, version );
 	else
-		WriteUInt( 0, out );
+		WriteBool( false, out, version );
 
 	for ( uint i = 0; i < uv_sets.size(); ++i) {
 		for ( uint j = 0; j < uv_sets[i].size(); ++j) {
@@ -786,6 +768,296 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 			WriteFloat( uv_sets[i][j].v, out );
 		}
 	}
+}
+
+void * AShapeData::QueryInterface( int id ) {
+	// Contains ShapeData Interface
+	if ( id == ID_SHAPE_DATA ) {
+		return (void*)static_cast<IShapeData*>(this);
+	} else {
+		return AData::QueryInterface( id );
+	}
+}
+
+void AShapeData::SetVertexCount(int n) {
+	vertices.resize(n);
+	normals.resize(n);
+	colors.resize(n);
+	for (uint i = 0; i < uv_sets.size(); ++i) {
+		uv_sets[i].resize(n);
+	}
+}
+
+void AShapeData::SetUVSetCount(int n) {
+	uv_sets.resize(n);
+}
+
+//--Setters--//
+void AShapeData::SetVertices( const vector<Vector3> & in ) {
+	if (in.size() != vertices.size())
+		throw runtime_error("Input array size must equal Vertex Count.  Call SetVertexCount() to resize.");
+	vertices = in;
+}
+
+void AShapeData::SetNormals( const vector<Vector3> & in ) {
+	if (in.size() != vertices.size())
+		throw runtime_error("Input array size must equal Vertex Count.  Call SetVertexCount() to resize.");
+	normals = in;
+}
+
+void AShapeData::SetColors( const vector<Color> & in ) {
+	if (in.size() != vertices.size())
+		throw runtime_error("Vector size must equal Vertex Count.  Call SetVertexCount() to resize.");
+	colors = in;
+}
+
+void AShapeData::SetUVSet( int index, const vector<UVCoord> & in ) {
+	if (in.size() != vertices.size())
+		throw runtime_error("Vector size must equal Vertex Count.  Call SetVertexCount() to resize.");
+	uv_sets[index] = in;
+}
+
+/***********************************************************
+ * AParticlesData methods
+ **********************************************************/
+
+void AParticlesData::Read( ifstream& in, unsigned int version ) {
+	AShapeData::Read( in, version );
+
+	//numActive exists up to version 4.0.0.2
+	if ( version <= 0x04000002 ) {
+		numActive = ReadUShort( in );
+	}
+
+	GetAttr("Active Radius")->Read( in, version );
+
+	//numValid exists up to version 4.0.0.2
+	if ( version <= 0x04000002 ) {
+		numValid = ReadUShort( in );
+	}
+
+	GetAttr("Unknown Short")->Read( in, version );
+
+	hasSizes = ReadBool( in, version );
+
+	if ( hasSizes ) {
+		sizes.resize( vertices.size() );
+		for ( uint i = 0; i < sizes.size(); ++i ) {
+			sizes[i] = ReadFloat( in );
+		}
+	}
+}
+
+void AParticlesData::Write( ofstream& out, unsigned int version ) {
+	AShapeData::Write( out, version );
+
+	//numActive exists up to version 4.0.0.2
+	if ( version <= 0x04000002 ) {
+		WriteUShort( numActive, out );
+	}
+
+	GetAttr("Active Radius")->Write( out, version );
+
+	//numValid exists up to version 4.0.0.2
+	if ( version <= 0x04000002 ) {
+		WriteUShort( numValid, out );
+	}
+
+	
+	GetAttr("Unknown Short")->Write( out, version );
+
+	WriteBool( hasSizes, out, version );
+
+	if ( hasSizes ) {
+		for ( uint i = 0; i < sizes.size(); ++i ) {
+			WriteFloat( sizes[i], out );
+		}
+	}
+}
+
+string AParticlesData::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	out << AShapeData::asString();
+
+	out << "Num Active:  " << numActive << endl
+		<< "Active Radius:  " << GetAttr("Active Radius")->asFloat() << endl
+		<< "Num Valid:  " << numValid << endl
+		<< "Unknown Short:  " << GetAttr("Unknown Short")->asInt() << endl
+		<< "Sizes:  ";
+	
+	if ( hasSizes ) {
+		if (verbose) {
+			for ( uint i = 0; i < sizes.size(); ++i) {
+				out << i << ":  " << sizes[i] << endl;
+			}
+		} else {
+			out << endl << "<<Data Not Shown>>";
+		}
+	} else {
+		out << "None" << endl;
+	}
+
+	return out.str();
+}
+
+/***********************************************************
+ * ARotatingParticlesData methods
+ **********************************************************/
+
+void ARotatingParticlesData::Read( ifstream& in, unsigned int version ) {
+	AParticlesData::Read( in, version );
+
+	hasRotations = ReadBool( in, version );
+
+	if ( hasRotations ) {
+		rotations.resize( vertices.size() );
+		for ( uint i = 0; i < rotations.size(); ++i ) {
+			rotations[i].w = ReadFloat( in );
+			rotations[i].x = ReadFloat( in );
+			rotations[i].y = ReadFloat( in );
+			rotations[i].z = ReadFloat( in );
+		}
+	}
+}
+
+void ARotatingParticlesData::Write( ofstream& out, unsigned int version ) {
+	AParticlesData::Write( out, version );
+
+	WriteBool( hasRotations, out, version );
+
+	if ( hasRotations ) {
+		for ( uint i = 0; i < rotations.size(); ++i ) {
+			WriteFloat( rotations[i].w, out );
+			WriteFloat( rotations[i].x, out );
+			WriteFloat( rotations[i].y, out );
+			WriteFloat( rotations[i].z, out );
+		}
+	}
+}
+
+string ARotatingParticlesData::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	out << AParticlesData::asString()
+		<< "Rotations:  ";
+	
+	if ( hasRotations ) {
+		if (verbose) {
+			for ( uint i = 0; i < rotations.size(); ++i) {
+				out << i << ":  [" << rotations[i].w << " (" << rotations[i].x << ", " << rotations[i].y << ", " << rotations[1].z << ")]" << endl;
+			}
+		} else {
+			out << endl << "<<Data Not Shown>>";
+		}
+	} else {
+		out << "None" << endl;
+	}
+
+	return out.str();
+}
+
+/***********************************************************
+ * NiParticleMeshesData methods
+ **********************************************************/
+
+void NiParticleMeshesData::Read( ifstream& in, unsigned int version ) {
+	ARotatingParticlesData::Read( in, version );
+
+	GetAttr("Unknown Link")->Read( in, version );
+}
+
+void NiParticleMeshesData::Write( ofstream& out, unsigned int version ) {
+	ARotatingParticlesData::Write( out, version );
+
+	GetAttr("Unknown Link")->Write( out, version );
+}
+
+string NiParticleMeshesData::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	out << ARotatingParticlesData::asString()
+		<< "Unknown Link:  " << GetAttr("Unknown Link")->asString() << endl;
+
+	return out.str();
+}
+
+/***********************************************************
+ * NiTriShapeData methods
+ **********************************************************/
+
+/**
+ * NiTriShapeData::Read - Assumes block name has already been read from in
+ */
+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 );
+		}
+	}
+
+	short matchGroupCount = ReadUShort( in );
+	match_group_mode = ( matchGroupCount != 0 );  // Only record whether or not file prefers to have match data generated
+
+	short sub_count;
+	for ( int i = 0; i < matchGroupCount; ++i ){
+		sub_count = ReadUShort( in );
+		for (ushort j = 0; j < sub_count; ++j) {
+			ReadUShort( in );  // Read data, but don't care what it is
+		}
+	}
+}
+
+string NiTriShapeData::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	out << AShapeData::asString();
+
+	out << "Triangles:  " << uint(triangles.size());
+	if (verbose) {
+		for ( uint i = 0; i < triangles.size(); ++i) {
+			if (i % 3 == 0)
+				out << endl << "   ";
+			else
+				out << "  ";
+			
+			out << "(" << setw(5) << triangles[i].v1 << ", " << setw(5) << triangles[i].v2 << ", " << setw(5) << triangles[i].v3 << " )";
+		}
+	} else {
+		out << endl << "<<Data Not Shown>>";
+	}
+	out << endl;
+
+	out << "Match Detection:  ";
+	if ( match_group_mode )
+		out << "On" << endl;
+	else
+		out << "Off" << endl;	
+
+	return out.str();
+}
+
+/**
+ * NiTriShapeData::Write - Writes block name to out, in addition to data.
+ */
+void NiTriShapeData::Write( ofstream& out, unsigned int version ){
+
+	AShapeData::Write( out, version );
 
 	WriteUShort( ushort(triangles.size()), out );
 
@@ -800,8 +1072,6 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){
 		}
 	}
 
-	
-
 	if ( match_group_mode ) {
 		WriteUShort( ushort(vertices.size()), out ); //Match Group Count = Vertex Count
 
@@ -832,56 +1102,105 @@ void * NiTriShapeData::QueryInterface( int id ) {
 	if ( id == ID_TRI_SHAPE_DATA ) {
 		return (void*)static_cast<ITriShapeData*>(this);
 	} else {
-		return ABlock::QueryInterface( id );
-	}
-}
-
-void NiTriShapeData::SetVertexCount(int n) {
-	vertices.resize(n);
-	normals.resize(n);
-	colors.resize(n);
-	for (uint i = 0; i < uv_sets.size(); ++i) {
-		uv_sets[i].resize(n);
+		return AShapeData::QueryInterface( id );
 	}
 }
 
-void NiTriShapeData::SetUVSetCount(int n) {
-	uv_sets.resize(n);
-}
-
 void NiTriShapeData::SetTriangleCount(int n) {
 	triangles.resize(n);
 }
 
 //--Setters--//
-void NiTriShapeData::SetVertices( const vector<Vector3> & in ) {
-	if (in.size() != vertices.size())
-		throw runtime_error("Input array size must equal Vertex Count.  Call SetVertexCount() to resize.");
-	vertices = in;
-}
 
-void NiTriShapeData::SetNormals( const vector<Vector3> & in ) {
+void NiTriShapeData::SetTriangles( const vector<Triangle> & in ) {
 	if (in.size() != vertices.size())
-		throw runtime_error("Input array size must equal Vertex Count.  Call SetVertexCount() to resize.");
-	normals = in;
+		throw runtime_error("Vector size must equal Vertex Count.  Call SetVertexCount() to resize.");
+	triangles = in;
 }
 
-void NiTriShapeData::SetColors( const vector<Color> & in ) {
-	if (in.size() != vertices.size())
-		throw runtime_error("Vector size must equal Vertex Count.  Call SetVertexCount() to resize.");
-	colors = in;
+/***********************************************************
+ * NiTriStripsData methods
+ **********************************************************/
+
+void NiTriStripsData::Read( ifstream& in, unsigned int version ){
+	AShapeData::Read( in, version );
+
+	//Read number of Triangles but discard it
+	ReadUShort( in );
+
+	//Initialize vectors to number and size of strips
+	short numStrips = ReadUShort( in );
+	strips.resize( numStrips );
+	for ( uint i = 0; i < strips.size(); ++i ) {
+		short stripSize = ReadUShort( in );
+		strips[i].resize( stripSize );
+	}
+
+	//Read points
+	for ( uint i = 0; i < strips.size(); ++i ) {
+		for ( uint j = 0; j < strips[i].size(); ++j ) {
+			strips[i][j] = ReadUShort( in );
+		}
+	}
 }
 
-void NiTriShapeData::SetUVSet( int index, const vector<UVCoord> & in ) {
-	if (in.size() != vertices.size())
-		throw runtime_error("Vector size must equal Vertex Count.  Call SetVertexCount() to resize.");
-	uv_sets[index] = in;
+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
+	WriteUShort( numTriangles, out );
+	WriteUShort( ushort(strips.size()), out );
+
+	//Write Strip Sizes
+	for ( uint i = 0; i < strips.size(); ++i ) {
+		WriteUShort( ushort(strips[i].size()), out );
+	}
+
+	//Write points
+	for ( uint i = 0; i < strips.size(); ++i ) {
+		for ( uint j = 0; j < strips[i].size(); ++j ) {
+			WriteUShort( strips[i][j], out );
+		}
+	}
 }
 
-void NiTriShapeData::SetTriangles( const vector<Triangle> & in ) {
-	if (in.size() != vertices.size())
-		throw runtime_error("Vector size must equal Vertex Count.  Call SetVertexCount() to resize.");
-	triangles = in;
+string NiTriStripsData::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	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
+		<< "Strips:  " << ushort(strips.size()) << endl;
+
+	if (verbose) {
+		for ( uint i = 0; i < strips.size(); ++i ) {
+			out << "   Strip " << i + 1 << endl;
+			for ( uint j = 0; j < strips[i].size(); ++j ) {
+				out << "      " << strips[i][j] << endl;
+			}
+		}
+	} else {
+		out << endl << "   <<Data Not Shown>>";
+	}
+
+	return out.str();
 }
 
 /***********************************************************
@@ -898,7 +1217,11 @@ void NiSkinData::Read( ifstream& in, unsigned int version ) {
 	ReadFVector3( translation, in );
 	scale = ReadFloat( in );
 	int boneCount = ReadUInt( in );
-	unknown = ReadUInt( in );
+	unknownInt = ReadUInt( in );
+	//unknownByte exists from version 4.2.1.0 on
+	if ( version >= 0x04020100 ) {
+		unknownByte = ReadByte( in );
+	}
 	bones.resize(boneCount);
 	for( int i = 0; i < boneCount; i++ ) {
 		for (int c = 0; c < 3; ++c) {
@@ -931,7 +1254,11 @@ void NiSkinData::Write( ofstream& out, unsigned int version ) {
 	WriteFVector3( translation, out );
 	WriteFloat( scale, out );
 	WriteUInt(short(bone_map.size()), out);
-	WriteUInt(unknown, out);
+	WriteUInt(unknownInt, out);
+	//unknownByte exists from version 4.2.1.0 on
+	if ( version >= 0x04020100 ) {
+		WriteByte( unknownByte, out );
+	}
 
 	map<IBlock*, Bone>::iterator it;
 	for( it = bone_map.begin(); it != bone_map.end(); ++it ) {		
@@ -1011,7 +1338,8 @@ string NiSkinData::asString() {
 		<< "Translate:  " << translation << endl
 		<< "Scale:  " << scale << endl
 		<< "Bone Count:  " << uint(bone_map.size()) << endl
-		<< "Unknown Index:  " << unknown << endl
+		<< "Unknown Index:  " << unknownInt << endl
+		<< "Unknown Byte:  " << unknownByte << endl
 		<< "Bones:" << endl;
 
 	map<IBlock*, Bone>::iterator it;
@@ -1686,28 +2014,45 @@ string NiKeyframeData::asString() {
  **********************************************************/
 
 void NiColorData::Read( ifstream& in, unsigned int version ) {
-	colorCount = ReadUInt( in );
+	uint colorCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
 	if (keyType != 1) {
-		cout << "NiColorData is thought to only support keyType of 1, but this NIF has a keyType of " << keyType << ".  Please report it to NIFLA.  Aborting" << endl;
-		throw runtime_error("Abort");
+		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() );
 	}
 
-	Key<fVector4> tmp;
-	for (uint i = 0; i < colorCount; i++) {
-		tmp.time = ReadFloat( in );
-		ReadFVector4( tmp.data, in );
-		keys.push_back(tmp);
+	keys.resize( colorCount );
+	for (uint i = 0; i < keys.size(); i++) {
+		keys[i].time = ReadFloat( in );
+		ReadFVector4( keys[i].data, in );
 	}
 }
 
+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() );
+	}
+
+	for (uint i = 0; i < keys.size(); i++) {
+		WriteFloat( keys[i].time, out );
+		WriteFVector4( keys[i].data, out );
+	}
+}
+
+
 string NiColorData::asString() {
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
 
-	out << "Color Count:  " << colorCount << endl
+	out << "Color Count:  " << uint(keys.size()) << endl
 		<< "Key Type:  " << keyType << endl;
 
 	if (verbose) {
@@ -1727,21 +2072,39 @@ string NiColorData::asString() {
  **********************************************************/
 
 void NiFloatData::Read( ifstream& in, unsigned int version ) {	
-	colorCount = ReadUInt( in );
+	uint keyCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
 	if (keyType != 2) {
-		cout << "NiFloatata is thought to only support keyType of 2, but this NIF has a keyType of " << keyType << ".  Please report it to NIFLA.  Aborting" << endl;
-		throw runtime_error("Abort");
+		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() );
+	}
+
+	keys.resize( keyCount );
+	for (uint i = 0; i < keys.size(); i++) {
+		keys[i].time = ReadFloat( in );
+		keys[i].data = ReadFloat( in );
+		keys[i].forward_tangent = ReadFloat( in );
+		keys[i].backward_tangent = ReadFloat( in );
+	}
+}
+
+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() );
 	}
 
-	Key<float> tmp;
-	for (uint i = 0; i < colorCount; i++) {
-		tmp.time = ReadFloat( in );
-		tmp.data = ReadFloat( in );
-		tmp.forward_tangent = ReadFloat( in );
-		tmp.backward_tangent = ReadFloat( in );
-		keys.push_back(tmp);
+	for (uint i = 0; i < keys.size(); i++) {
+		WriteFloat( keys[i].time, out );
+		WriteFloat( keys[i].data, out );
+		WriteFloat( keys[i].forward_tangent, out );
+		WriteFloat( keys[i].backward_tangent, out );
 	}
 }
 
@@ -1750,7 +2113,7 @@ string NiFloatData::asString() {
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
 
-	out << "Vector Count:  " << colorCount << endl
+	out << "Key Count:  " << uint(keys.size()) << endl
 		<< "Key Type:  " << keyType << endl;
 
 	if (verbose) {
@@ -1770,24 +2133,38 @@ string NiFloatData::asString() {
  **********************************************************/
 
 void NiStringExtraData::Read( ifstream& in, unsigned int version ) {
-	GetAttr("Next Extra Data")->Read( in );
+	GetAttr("Next Extra Data")->Read( in, version );
+
+	//Read Name if there is one
+	if ( _namable == true && version >= _first_named_ver ) {
+		_name = ReadString( in );
+	}
 
-	//Read Bytes Remaining but don't bother to store it
-	ReadUInt( in );
+	//Up to version 4.2.2.0, read bytes remaining but don't bother to store it
+	if ( version <= 0x04020200 ) {
+		ReadUInt( in );
+	}
 
-	GetAttr("String Data")->Read( in );
+	GetAttr("String Data")->Read( in, version );
 }
 
 void NiStringExtraData::Write( ofstream& out, unsigned int version ) {
 
-	GetAttr("Next Extra Data")->Write( out );
+	//Write Name if there is one
+	if ( _namable == true && version >= _first_named_ver ) {
+		WriteString( _name, out );
+	}
+
+	GetAttr("Next Extra Data")->Write( out, version );
 
-	//Write Bytes Remaining - length of string + 4
 	attr_ref string_data = GetAttr("String Data");
-	
-	WriteUInt( uint(string_data->asString().length()) + 4, out );
 
-	string_data->Write( out );
+	//Up to version 4.2.2.0, Write Bytes Remaining - length of string + 4
+	if ( version <= 0x04020200 ) {
+		WriteUInt( uint(string_data->asString().length()) + 4, out );
+	}
+
+	string_data->Write( out, version );
 }
 
 string NiStringExtraData::asString() {
@@ -1798,7 +2175,8 @@ string NiStringExtraData::asString() {
 	attr_ref next_data = GetAttr("Next Extra Data");
 	attr_ref string_data = GetAttr("String Data");
 
-	out << next_data->GetName() << ":  " << next_data->asLink() << endl
+	out << "Name:  " << _name << endl
+		<< next_data->GetName() << ":  " << next_data->asLink() << endl
 		<< "Bytes Remaining:  " << uint(string_data->asString().length()) + 4 << endl
 		<< string_data->GetName() << ":  " << string_data->asString() << endl;
 	
@@ -1813,24 +2191,37 @@ void NiMorphData::Read( ifstream& in, unsigned int version ) {
 	uint morphCount = ReadUInt( in );
 	vertCount = ReadUInt( in );
 
-	GetAttr("Unknown Byte")->Read( in );
+	GetAttr("Unknown Byte")->Read( in, version );
 
 	morphs.resize(morphCount);
 	for ( uint i = 0; i < morphs.size() ; ++i ) {
 		uint timeCount = ReadUInt( in );
 		morphs[i].keyType = KeyType(ReadUInt( in ));
 
-		if (timeCount > 0 && morphs[i].keyType != 2) {
-			cout << "NiMorphData is thought to only support keyType of 2, but this NIF has a keyType of " << morphs[i].keyType << ".  Please report it to NifTools.  Aborting." << endl;
-			throw runtime_error("Abort");
+		if (timeCount > 0 && (morphs[i].keyType < 1 || morphs[i].keyType > 3 ) ) {
+			stringstream s;
+			s << "NiMorphData is thought to only support keyType of 1, 2, or 3, but this NIF has a keyType of " << morphs[i].keyType << ".";
+			throw runtime_error(s.str());
 		}
 
 		morphs[i].keys.resize( timeCount );
 		for (uint j = 0; j < morphs[i].keys.size(); ++j ) {
+
+			//Always read the time and data
 			morphs[i].keys[j].time = ReadFloat( in );
 			morphs[i].keys[j].data = ReadFloat( in );
-			morphs[i].keys[j].forward_tangent = ReadFloat( in );
-			morphs[i].keys[j].backward_tangent = ReadFloat( in );
+
+			if ( morphs[i].keyType == 2 ) {
+				//Uses Quadratic interpolation
+				morphs[i].keys[j].forward_tangent = ReadFloat( in );
+				morphs[i].keys[j].backward_tangent = ReadFloat( in );
+			} else if ( morphs[i].keyType == 3 ) {
+				//Uses TBC interpolation
+				morphs[i].keys[j].tension = ReadFloat( in );
+				morphs[i].keys[j].bias = ReadFloat( in );
+				morphs[i].keys[j].continuity = ReadFloat( in );
+
+			}
 		}
 		
 		morphs[i].morph.resize( vertCount );
@@ -1846,22 +2237,35 @@ void NiMorphData::Write( ofstream& out, unsigned int version ) {
 	WriteUInt( uint(morphs.size()), out );
 	WriteUInt( vertCount, out );
 
-	GetAttr("Unknown Byte")->Write( out );
+	GetAttr("Unknown Byte")->Write( out, version );
 
 	for ( uint i = 0; i < morphs.size() ; ++i ) {
 		WriteUInt( uint(morphs[i].keys.size()), out );
 		WriteUInt( KeyType(morphs[i].keyType), out );
 
-		if (morphs[i].keys.size() > 0 && morphs[i].keyType != 2) {
-			cout << "NiMorphData is thought to only support keyType of 2, but this NIF has a keyType of " << morphs[i].keyType << ".  Please report it to NifTools.  Aborting." << endl;
-			throw runtime_error("Abort");
+		if ( morphs[i].keyType < 1 || morphs[i].keyType > 3 ) {
+			stringstream s;
+			s << "NiMorphData is thought to only support keyType of 1, 2, or 3, but this NIF has a keyType of " << morphs[i].keyType << ".";
+			throw runtime_error(s.str());
 		}
 
 		for (uint j = 0; j < morphs[i].keys.size(); ++j ) {
+
+			//Always write the time and data
 			WriteFloat( morphs[i].keys[j].time, out );
 			WriteFloat( morphs[i].keys[j].data, out );
-			WriteFloat( morphs[i].keys[j].forward_tangent, out );
-			WriteFloat( morphs[i].keys[j].backward_tangent, out );
+
+			if ( morphs[i].keyType == 2 ) {
+				//Uses Quadratic interpolation
+				WriteFloat( morphs[i].keys[j].forward_tangent, out );
+				WriteFloat( morphs[i].keys[j].backward_tangent, out );
+			} else if ( morphs[i].keyType == 3 ) {
+				//Uses TBC interpolation
+				WriteFloat ( morphs[i].keys[j].tension, out );
+				WriteFloat ( morphs[i].keys[j].bias, out );
+				WriteFloat ( morphs[i].keys[j].continuity, out );
+
+			}
 		}
 		
 		for (uint j = 0; j < vertCount ; ++j ) {
@@ -1889,7 +2293,15 @@ string NiMorphData::asString() {
 		
 		if (verbose) {
 			for (uint j = 0; j < morphs[i].keys.size(); ++j ) {
-				out << "Key Time:  " << morphs[i].keys[j].time << " Influence?: " << morphs[i].keys[j].data << " F: " << morphs[i].keys[j].forward_tangent << " B: " << morphs[i].keys[j].backward_tangent << endl;
+				//Always show time and data
+				out << "Key Time:  " << morphs[i].keys[j].time << ", Influence?: " << morphs[i].keys[j].data;
+				if ( morphs[i].keyType == 2 ) {
+					//Uses Quadratic interpolation
+					out << ", FT(" << morphs[i].keys[j].forward_tangent << ") BT(" << morphs[i].keys[j].backward_tangent << ")";
+				} else if ( morphs[i].keyType == 3 ) {
+					out << ", T " << morphs[i].keys[j].tension << ", B " << morphs[i].keys[j].bias << ", C " << morphs[i].keys[j].continuity;
+				}
+				out << endl;
 			}
 			
 			for (uint j = 0; j < vertCount ; ++j ) {
@@ -1921,6 +2333,63 @@ void NiMorphData::SetMorphVerts( int n, const vector<Vector3> & in ) {
 	morphs[n].morph = in;
 }
 
+/***********************************************************
+ * NiPalette methods
+ **********************************************************/
+
+void NiPalette::Read( ifstream& in, unsigned int version ) {
+	//Read 5 unknown bytes
+	for (int i = 0; i < 5; ++i) {
+		unknownBytes[i] = ReadByte( in );
+	}
+
+	//Read Palette
+	for (int i = 0; i < 256; ++i) {
+		for (int j = 0; j < 4; ++j) {
+			palette[i][j] = ReadByte( in );
+		}
+	}
+}
+
+void NiPalette::Write( ofstream& out, unsigned int version ) {
+	//Write 5 unknown bytes
+	for (int i = 0; i < 5; ++i) {
+		WriteByte( unknownBytes[i], out );
+	}
+
+	//Read Palette
+	for (int i = 0; i < 256; ++i) {
+		for (int j = 0; j < 4; ++j) {
+			WriteByte( palette[i][j], out );
+		}
+	}
+}
+
+
+string NiPalette::asString() {
+	stringstream out;
+	out.setf(ios::fixed, ios::floatfield);
+	out << setprecision(1);
+
+	//Print 5 unknown bytes
+	for (int i = 0; i < 5; ++i) {
+		out << "Unknown Byte " << i + 1 << ":  " << unknownBytes[i] << endl;
+	}
+
+	//Print Palette
+	out << "Palette:" << endl;
+	if (verbose) {
+		for (int i = 0; i < 256; ++i) {
+			out << i + 1 << ":  " << int(palette[i][0]) << ", " << int(palette[i][1]) << ", " << int(palette[i][2]) << ", " << int(palette[i][3]) << endl;
+		}
+	} else {
+		out << "  <<Data Not Shown>>" << endl;
+	}
+
+	return out.str();
+}
+
+
 /***********************************************************
  * NiPixelData methods
  **********************************************************/
@@ -1937,17 +2406,16 @@ void NiPixelData::Read( ifstream& in, unsigned int version ) {
 		unknown8Bytes[i] = ReadByte( in );
 	}
 
-	GetAttr("Unknown Index")->Read( in );
+	GetAttr("Unknown Index")->Read( in, version );
 
-	mipCount = ReadUInt( in );
+	uint mipCount = ReadUInt( in );
 	bytesPerPixel = ReadUInt( in );
 
-	MipMap temp;
+	mipmaps.resize( mipCount );
 	for ( uint i = 0; i < mipCount; ++i ) {
-		temp.width = ReadUInt( in );
-		temp.height = ReadUInt( in );
-		temp.offset = ReadUInt( in );
-		mipmaps.push_back(temp);
+		mipmaps[i].width = ReadUInt( in );
+		mipmaps[i].height = ReadUInt( in );
+		mipmaps[i].offset = ReadUInt( in );
 	}
 
 	dataSize = ReadUInt( in );
@@ -1955,6 +2423,33 @@ void NiPixelData::Read( ifstream& in, unsigned int version ) {
 	in.read( (char *)data, dataSize);
 }
 
+void NiPixelData::Write( ofstream& out, unsigned int version ) {
+	WriteUInt( unknownInt, out );
+	WriteUInt( rMask, out );
+	WriteUInt( gMask, out );
+	WriteUInt( bMask, out );
+	WriteUInt( aMask, out );
+	WriteUInt( bpp, out );
+		
+	for (int i = 0; i < 8; ++i) {
+		WriteByte( unknown8Bytes[i], out );
+	}
+
+	GetAttr("Unknown Index")->Write( out, version );
+
+	WriteUInt( uint(mipmaps.size()), out );
+	WriteUInt( bytesPerPixel, out );
+
+	for ( uint i = 0; i < mipmaps.size(); ++i ) {
+		WriteUInt( mipmaps[i].width, out );
+		WriteUInt( mipmaps[i].height, out );
+		WriteUInt( mipmaps[i].offset, out );
+	}
+
+	WriteUInt( dataSize, out );
+	out.write( (char *)data, dataSize);
+}
+
 string NiPixelData::asString() {
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
@@ -1977,7 +2472,7 @@ string NiPixelData::asString() {
 	out << endl;
 
 	out << "Unknown Index:  " <<  GetAttr("Unknown Index")->asLink() << endl
-		<< "Mipmap Count:  " << mipCount << endl
+		<< "Mipmap Count:  " << uint(mipmaps.size()) << endl
 		<< "Bytes Per Pixel:  " << bytesPerPixel << endl;
 
 	for ( uint i = 0; i < mipmaps.size(); ++i ) {
@@ -1997,10 +2492,10 @@ string NiPixelData::asString() {
  **********************************************************/
 
 void NiPosData::Read( ifstream& in, unsigned int version ) {
-	posCount = ReadUInt( in );
+	uint keyCount = ReadUInt( in );
 	keyType = ReadUInt( in );
 
-	keys.resize(posCount);
+	keys.resize(keyCount);
 	for (uint i = 0; i < keys.size(); i++) {
 		keys[i].time = ReadFloat( in );
 		ReadFVector3( keys[i].data, in );
@@ -2011,8 +2506,30 @@ void NiPosData::Read( ifstream& in, unsigned int version ) {
 		}
 
 		if (keyType != 1 && keyType != 2) {
-			cout << "NiPosData is thought to only support keyTypes of 1 and 2, but this NIF has a keyType of " << keyType << ".  Please report it to NIFLA.  Aborting" << endl;
-			throw runtime_error("Abort");
+			stringstream str;
+            str << "NiPosData is thought to only support keyTypes of 1 and 2, but this NIF has a keyType of " << keyType << ".";
+			throw runtime_error( str.str() );
+		}
+	}
+}
+
+void NiPosData::Write( ofstream& out, unsigned int version ) {
+	WriteUInt( uint(keys.size()), out );
+	WriteUInt( keyType, out );
+
+	for (uint i = 0; i < keys.size(); i++) {
+		WriteFloat( keys[i].time, out );
+		WriteFVector3( keys[i].data, out );
+
+		if (keyType == 2) {
+			WriteFVector3( keys[i].forward_tangent, out );
+			WriteFVector3( keys[i].backward_tangent, out );
+		}
+
+		if (keyType != 1 && keyType != 2) {
+			stringstream str;
+            str << "NiPosData is thought to only support keyTypes of 1 and 2, but this NIF has a keyType of " << keyType << ".";
+			throw runtime_error( str.str() );
 		}
 	}
 }
@@ -2022,7 +2539,7 @@ string NiPosData::asString() {
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
 
-	out << "Pos Count:  " << posCount << endl
+	out << "Key Count:  " << uint(keys.size()) << endl
 		<< "Key Type:  " << keyType << endl;
 
 	if (verbose) {
@@ -2041,147 +2558,13 @@ string NiPosData::asString() {
 	return out.str();
 }
 
-/***********************************************************
- * NiRotatingParticlesData methods
- **********************************************************/
-
-void NiRotatingParticlesData::Read( ifstream& in, unsigned int version ) {
-
- //   short count1
-
- //   int b1
- //   if(b1 != 0)
- //       foreach count1
- //           float[3] unknown
-
- //   int or float = 0
- //   float[4] unknown
-
- //   int b2
- //   if(b2 != 0)
- //       foreach count1
- //           float[4] unknown
-
- //   int = 0
- //   short unknown
- //   short count2             - always equal to count1 ?
- //   float unknown
- //   short unknown
-
- //   int b3
- //   if(b3 != 0)
- //       foreach count2 (or count1)
- //           float unknown
-
- //   int b4
- //   if(b4 != 0)
- //       foreach count2 (or count1)
- //           float[4] unknown
-
-	ushort count1 = ReadUShort( in );
-	uint b1 = ReadUInt( in );
-
-	cout << "Count 1:  " << count1 << endl
-		 << "Bool 1:  " << b1 << endl;
-
-	if (b1) {
-		fVector3 vect;
-		for (uint i = 0; i < count1; ++i) {
-			ReadFVector3( vect, in );
-			cout << "Unknown 3 Floats:  " << vect << endl;
-		}
-	}
-
-	uint unknownInt = ReadUInt( in );
-
-	fVector4 unknown4Floats;
-	ReadFVector4( unknown4Floats, in );
-	uint b2 = ReadUInt( in );
-
-	cout << "Unknown Int:  " << unknownInt << endl
-		 << "Unknown 4 Floats:  " << unknown4Floats << endl
-		 << "Bool 2:  " << b2 << endl;
-
-	if (b2) {
-		fVector4 unknown4Floats2;
-		for (uint i = 0; i < count1; ++i) {
-			ReadFVector4( unknown4Floats2, in );
-			cout << "Unkown 4 Floats:  " << unknown4Floats2 << endl;
-		}
-	}
-
-	uint unknownInt2 = ReadUInt( in );
-	ushort unknownShort = ReadUShort( in );
-	ushort count2 = ReadUShort( in );
-	float unknownFloat = ReadFloat( in );
-	ushort unknownShort2 = ReadUShort( in );
-	uint b3 = ReadUInt( in );
-
-	cout << "Unknown Int 2:  " << unknownInt2 << endl
-		 << "Unknown Short:  " << unknownShort << endl
-		 << "Count 2:  " << count2 << endl
-		 << "Unknown Float:  " << unknownFloat << endl
-		 << "Unknown Short 2:  " << unknownShort2 << endl
-		 << "Bool 3:  " << b3 << endl;
-
-	if (b3) {
-		float temp;
-		for (uint i = 0; i < count1; ++i) {
-			temp = ReadFloat( in );
-			cout << "Unkown Float:  " << temp << endl;
-		}
-	}
-
-	uint b4 = ReadUInt( in );
-
-	cout << "Bool 4:  " << b4 << endl;
-
-	if (b4) {
-		fVector4 unknown4Floats3;
-		for (uint i = 0; i < count1; ++i) {
-			ReadFVector4( unknown4Floats3, in );
-			cout << "Unkown 4 Floats:  " << unknown4Floats3 << endl;
-		}
-	}
-
-	//int len = BlockSearch(in);
-
-	////Create byte array and read in unknown block
-	//byte * data = new byte [len];
-	//in.read((char*)data, len);
-
-	//Display Data in Hex form
-	//cout << hex << setfill('0');
-	//for (int j = 0; j < len; j++) {
-	//	cout << uppercase << setw(2) << uint(data[j]);
-	//	if (j % 16 == 15 || j == len - 1)
-	//		cout << endl;
-	//	else if (j % 16 == 7)
-	//		cout << "   ";
-	//	else if (j % 8 == 3)
-	//		cout << "  ";
-	//	else
-	//		cout << " ";
-	//}
-	//cout << dec << setfill(' ');
-
-	/*delete [] data;*/
-
-	//if (count1 != count2) {
-	//	cout << "\a";
-	//	cin.get();
-	//}
-}
-
 /***********************************************************
  * NiTextKeyExtraData methods
  **********************************************************/
 
 void NiTextKeyExtraData::Read( ifstream& in, unsigned int version ) {
-
-	GetAttr("Next Extra Data")->Read( in );
-	GetAttr("Unknown Int")->Read( in );
-
+	ABlock::Read( in, version );
+	
 	uint keyCount = ReadUInt( in );
 
 	_keys.resize(keyCount);
@@ -2193,8 +2576,7 @@ void NiTextKeyExtraData::Read( ifstream& in, unsigned int version ) {
 
 void NiTextKeyExtraData::Write( ofstream& out, unsigned int version ) {
 
-	GetAttr("Next Extra Data")->Write( out );
-	GetAttr("Unknown Int")->Write( out );
+	ABlock::Write( out, version );
 
 	WriteUInt( uint(_keys.size()), out );
 
@@ -2231,12 +2613,12 @@ string NiTextKeyExtraData::asString() {
 
 void NiUVData::Read( ifstream& in, unsigned int version ) {	
 	for (uint i = 0; i < 4; ++i) {
-		groups[i].count = ReadUInt( in );
+		uint count = ReadUInt( in );
 
-		if ( groups[i].count > 0 ) {
+		if ( count > 0 ) {
 			groups[i].keyType = ReadUInt( in );
 
-			groups[i].keys.resize(groups[i].count);
+			groups[i].keys.resize(count);
 			for (uint j = 0; j < groups[i].keys.size(); ++j) {
 				groups[i].keys[j].time = ReadFloat( in );
 				groups[i].keys[j].data = ReadFloat( in );
@@ -2250,6 +2632,26 @@ void NiUVData::Read( ifstream& in, unsigned int version ) {
 	}
 }
 
+void NiUVData::Write( ofstream& out, unsigned int version ) {	
+	for (uint i = 0; i < 4; ++i) {
+		WriteUInt( uint(groups[i].keys.size()), out );
+
+		if ( groups[i].keys.size() > 0 ) {
+			WriteUInt( groups[i].keyType, out );
+
+			for (uint j = 0; j < groups[i].keys.size(); ++j) {
+				WriteFloat( groups[i].keys[j].time, out );
+				WriteFloat( groups[i].keys[j].data, out );
+
+				if ( groups[i].keyType == 2) {
+					WriteFloat( groups[i].keys[j].forward_tangent, out );
+					WriteFloat( groups[i].keys[j].backward_tangent, out );
+				}
+			}
+		}
+	}
+}
+
 string NiUVData::asString() {
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
@@ -2257,9 +2659,9 @@ string NiUVData::asString() {
 
 	for (uint i = 0; i < 4; ++i) {
 		out << "UV Group " << i + 1 << ":" << endl
-			<< "   Count:  " << groups[i].count << endl;
+			<< "   Key Count:  " << uint(groups[i].keys.size()) << endl;
 
-		if ( groups[i].count > 0 ) {
+		if ( groups[i].keys.size() > 0 ) {
 			out << "   Key Type:  " << groups[i].keyType << endl;
 
 			if (verbose) {
@@ -2284,10 +2686,11 @@ string NiUVData::asString() {
  * NiVertWeightsExtraData methods
  **********************************************************/
  
-void NiVertWeightsExtraData ::Read( ifstream& in, unsigned int version ) {
-	GetAttr("Next Extra Data")->Read( in );
+void NiVertWeightsExtraData::Read( ifstream& in, unsigned int version ) {
+	ABlock::Read( in, version );
+
 	bytes = ReadUInt( in );
-	verts = ReadUShort( in );
+	ushort verts = ReadUShort( in );
 
 	weights.resize( verts );
 	for (uint i = 0; i < weights.size(); ++i) {
@@ -2295,14 +2698,26 @@ void NiVertWeightsExtraData ::Read( ifstream& in, unsigned int version ) {
 	}
 }
 
+void NiVertWeightsExtraData::Write( ofstream& out, unsigned int version ) {
+	ABlock::Write( out, version );
+
+	WriteUInt( bytes, out );
+	WriteUShort( ushort(weights.size()), out );
+
+	for (uint i = 0; i < weights.size(); ++i) {
+		 WriteFloat( weights[i], out );
+	}
+}
+
 string NiVertWeightsExtraData::asString() {
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
 
-	out << "Next Extra Data:  " <<  GetAttr("Next Extra Data")->asLink() << endl
-		<< "Bytes:  " << bytes << endl
-		<< "Verts:  " << verts << endl;
+	out << ABlock::asString();
+
+	out << "Bytes:  " << bytes << endl
+		<< "Verts:  " << uint(weights.size()) << endl;
 
 	if (verbose) {
 		for (uint i = 0; i < weights.size(); ++i) {
@@ -2320,21 +2735,30 @@ string NiVertWeightsExtraData::asString() {
  **********************************************************/
 
 void NiVisData ::Read( ifstream& in, unsigned int version ) {
-	visCount = ReadUInt( in );
+	uint keyCount = ReadUInt( in );
 
-	keys.resize( visCount );
+	keys.resize( keyCount );
 	for (uint i = 0; i < keys.size(); ++i) {
 		keys[i].time = ReadFloat( in );
 		keys[i].data = ReadByte( in ); // Is Visible? True/False
 	}
 }
 
+void NiVisData ::Write( ofstream& out, unsigned int version ) {
+	WriteUInt( uint(keys.size()), out );
+
+	for (uint i = 0; i < keys.size(); ++i) {
+		WriteFloat( keys[i].time, out );
+		WriteByte( keys[i].data, out ); // Is Visible? True/False
+	}
+}
+
 string NiVisData::asString() {
 	stringstream out;
 	out.setf(ios::fixed, ios::floatfield);
 	out << setprecision(1);
 
-	out << "Visibility Count:  "  << visCount << endl;
+	out << "Key Count:  "  << uint(keys.size()) << endl;
 
 	if (verbose) {
 		for (uint i = 0; i < keys.size(); ++i) {
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
index 50e0b153..739dfdaa 100644
--- a/NIF_Blocks.h
+++ b/NIF_Blocks.h
@@ -102,6 +102,11 @@ public:
 		}
 	}
 
+	//Name Functions
+	virtual bool Namable() { return _namable; }
+	virtual void SetName( string & name ) { _name = name; }
+	virtual string GetName() { return _name; }
+
 	//--Internal Functions--//
 	void AddParent( blk_ref parent);
 	void RemoveParent( IBlock * match );
@@ -110,31 +115,29 @@ public:
 
 	void Read( ifstream& in, unsigned int version );
 	void Write( ofstream& out, unsigned int version );
-private:
+protected:
 	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;
+	bool _namable;
+	unsigned int _first_named_ver;
+	string _name;
 };
 
-class AExtraData : public ABlock {
+class AControllable : public ABlock {
 public:
-	AExtraData() {
-		AddAttr( "link", "Next Extra Data" );
-	}
-	~AExtraData() {};
+	AControllable();
+	void Init() {}
+	~AControllable() {}
 };
 
-class ANamed : public ABlock {
+class ANamed : public AControllable {
 public:
-	ANamed(){
-		AddAttr( "string", "Name" );
-		AddAttr( "link", "Extra Data" );
-		AddAttr( "link", "Controller" );
-	}
-	~ANamed(){}
+	ANamed();
+	void Init() { _namable = true; }
+	~ANamed() {}
 };
 
 class INodeInternal {
@@ -145,19 +148,10 @@ public:
 
 class ANode : public ANamed, public INode, public INodeInternal {
 public:
-	ANode(){
-		AddAttr( "flags", "Flags" );
-		AddAttr( "float3", "Translation" );
-		AddAttr( "matrix33", "Rotation" );
-		AddAttr( "float", "Scale" );
-		AddAttr( "float3", "Velocity" );
-		AddAttr( "linkgroup", "Properties" );
-		AddAttr( "bbox", "Bounding Box", 0, VER_4_0_0_2 );
-		AddAttr( "byte" , "Unknown Byte", VER_4_2_0_2 );
-
-		SetIdentity44(bindPosition);
-	}
+	ANode();
+	void Init() { SetIdentity44(bindPosition); };
 	~ANode();
+	void InitAttrs();
 	void * QueryInterface( int id );
 	void Read( ifstream& in, unsigned int version ) {
 		ABlock::Read( in, version );
@@ -184,53 +178,63 @@ protected:
 
 class AParentNode : public ANode {
 public:
-	AParentNode(){
-		AddAttr( "linkgroup", "Children" );
-		AddAttr( "linkgroup", "Effects" );
-	}
-	~AParentNode(){}
+	AParentNode();
+	void Init() {}
+	~AParentNode() {}
+};
+
+class AShape : public ANode {
+public:
+	AShape();
+	void Init() {}
+	~AShape() {}
 };
 
 class AProperty : public ANamed {
 public:
-	AProperty() {
-		AddAttr( "flags", "Flags" );
-	}
+	AProperty();
+	void Init() {}
 	~AProperty() {}
 };
 
 class AController : public ABlock {
 public:
-	AController() {
-		AddAttr( "link", "Next Controller" );
-		AddAttr( "flags", "Flags" );
-		AddAttr( "float", "Frequency" );
-		AddAttr( "float", "Phase" );
-		AddAttr( "float", "Start Time" );
-		AddAttr( "float", "Stop Time" );
-		AddAttr( "nodeancestor", "Target Node" );
-	}
+	AController();
+	void Init() {}
 	~AController() {}
 };
 
-class ALight   : public ANode {
+class AData : public ABlock {
 public:
-	ALight  (){
-		//AddAttr( "int", "Unknown Int 1" );
-		//AddAttr( "int", "Unknown Int 2" );
-		AddAttr( "float", "Dimmer" );
-		AddAttr( "float3", "Ambient Color" );
-		AddAttr( "float3", "Diffuse Color" );
-		AddAttr( "float3", "Specular Color" );
+	AData() {}
+	void Init() {}
+	~AData() {}
+};
+
+class AExtraData : public AData {
+public:
+	AExtraData() {
+		_namable = true;
+		_first_named_ver = 0x10000100;
+		AddAttr( "link", "Next Extra Data", 0, 0x04020200 );
 	}
-	~ALight  (){}
+	~AExtraData() {};
 };
 
-// Used to move temporarily stored bone indicies into SkinInstanceData block to facilitate
-// the illusion that these are stored together with the bone data
-class ISkinInstance { 
+
+class AParticleSystemController : public AController {
+public:
+	AParticleSystemController();
+	void Init() {}
+	~AParticleSystemController() {}
 };
 
+class ALight   : public ANode {
+public:
+	ALight();
+	void Init() {}
+	~ALight() {}
+};
 
 /**
  * NiNode - Root of each model component.
@@ -238,8 +242,9 @@ class ISkinInstance {
 class NiNode : public AParentNode {
 public:
 
-	NiNode(){}
-	~NiNode(){}
+	NiNode();
+	void Init() {}
+	~NiNode() {}
 	string GetBlockType() { return "NiNode"; }
 	string asString();
 };
@@ -248,148 +253,151 @@ public:
  * RootCollisionNode
  */
 class RootCollisionNode : public AParentNode {
+public:
 
-	public:
-
-		RootCollisionNode(){}
-		~RootCollisionNode(){}
+	RootCollisionNode();
+	void Init() {}
+	~RootCollisionNode() {}
 
-		string GetBlockType() { return "RootCollisionNode"; }
+	string GetBlockType() { return "RootCollisionNode"; }
 };
 
 /**
  * AvoidNode
  */
 class AvoidNode : public AParentNode {
+public:
 
-	public:
-
-		AvoidNode(){}
-		~AvoidNode(){}
+	AvoidNode();
+	void Init() {}
+	~AvoidNode() {}
 
-		string GetBlockType() { return "AvoidNode"; }
+	string GetBlockType() { return "AvoidNode"; }
 };
 
 /**
  * NiBillboardNode
  */
 class NiBillboardNode : public AParentNode {
+public:
+	NiBillboardNode();
+	void Init() {}
+	~NiBillboardNode() {}
 
-	public:
+	string GetBlockType() { return "NiBillboardNode"; }
+};
 
-		NiBillboardNode(){}
-		~NiBillboardNode(){}
+/**
+ * NiBSAnimationNode
+ */
+class NiBSAnimationNode : public AParentNode {
+public:
+	NiBSAnimationNode();
+	void Init() {}
+	~NiBSAnimationNode() {}
 
-		string GetBlockType() { return "NiBillboardNode"; }
+	string GetBlockType() { return "NiBSAnimationNode"; }
 };
 
 /**
  * NiBSParticleNode
  */
 class NiBSParticleNode : public AParentNode {
+public:
+	NiBSParticleNode();
+	void Init() {}
+	~NiBSParticleNode() {}
 
-	public:
-
-		NiBSParticleNode(){}
-		~NiBSParticleNode(){}
-
-		string GetBlockType() { return "NiBSParticleNode"; }
+	string GetBlockType() { return "NiBSParticleNode"; }
 };
 
 /**
- * NiBSAnimationNode
+ * NiLODNode
  */
-class NiBSAnimationNode : public AParentNode {
-
-	public:
-
-		NiBSAnimationNode(){}
-		~NiBSAnimationNode(){}
+class NiLODNode : public AParentNode {
+public:
+	NiLODNode();
+	void Init() {}
+	~NiLODNode() {}
 
-		string GetBlockType() { return "NiBSAnimationNode"; }
+	string GetBlockType() { return "NiLODNode"; }
 };
 
-
 /**
  * ZBuffer data.
  */
 class NiZBufferProperty : public AProperty {
+public:
+	NiZBufferProperty();
+	void Init() {}
+	~NiZBufferProperty() {}
 
-	public:
-
-		NiZBufferProperty(){
-			AddAttr( "int", "Unknown Int", VER_4_2_0_2 );
-		}
-		~NiZBufferProperty(){}
-
-		string GetBlockType() { return "NiZBufferProperty"; }
+	string GetBlockType() { return "NiZBufferProperty"; }
 };
 
 /**
  * NiShadeProperty
  */
 class NiShadeProperty : public AProperty {
+public:
 
-	public:
-
-		NiShadeProperty(){}
-		~NiShadeProperty(){}
+	NiShadeProperty();
+	void Init() {}
+	~NiShadeProperty() {}
 
-		string GetBlockType() { return "NiShadeProperty"; }
+	string GetBlockType() { return "NiShadeProperty"; }
 };
 
 /**
  * ZBuffer data.NiWireframeProperty
  */
 class NiWireframeProperty : public AProperty {
+public:
 
-	public:
-
-		NiWireframeProperty(){}
-		~NiWireframeProperty(){}
+	NiWireframeProperty();
+	void Init() {}
+	~NiWireframeProperty() {}
 
-		string GetBlockType() { return "NiWireframeProperty"; }
+	string GetBlockType() { return "NiWireframeProperty"; }
 };
 
 /**
  * NiDitherProperty
  */
 class NiDitherProperty : public AProperty {
+public:
 
-	public:
-
-		NiDitherProperty(){}
-		~NiDitherProperty(){}
+	NiDitherProperty();
+	void Init() {}
+	~NiDitherProperty() {}
 
-		string GetBlockType() { return "NiDitherProperty"; }
+	string GetBlockType() { return "NiDitherProperty"; }
 };
 
 /**
  * NiSequenceStreamHelper 
  */
 class NiSequenceStreamHelper  : public ANamed {
+public:
 
-	public:
-
-		NiSequenceStreamHelper (){}
-		~NiSequenceStreamHelper (){}
+	NiSequenceStreamHelper ();
+	void Init() {}
+	~NiSequenceStreamHelper () {}
 
-		string GetBlockType() { return "NiSequenceStreamHelper"; }
+	string GetBlockType() { return "NiSequenceStreamHelper"; }
 };
 
 /**
  * NiVertexColorProperty - Vertex colour data.
  */
 class NiVertexColorProperty : public AProperty{
-	public:
+public:
 
-		NiVertexColorProperty(){
-			AddAttr( "vertmode", "Vertex Mode" );
-			AddAttr( "lightmode", "Lighting Mode" );
-		}
-		~NiVertexColorProperty(){}
+	NiVertexColorProperty();
+	void Init() {}
+	~NiVertexColorProperty() {}
 
-		string GetBlockType() { return "NiVertexColorProperty"; }
+	string GetBlockType() { return "NiVertexColorProperty"; }
 };
 
 
@@ -397,66 +405,47 @@ class NiVertexColorProperty : public AProperty{
 /**
  * NiTriShape - 
  */
-class NiTriShape : public ANode {
-	public:
-		NiTriShape() {
-			AddAttr( "link", "Data" );
-			AddAttr( "link", "Skin Instance" );
-		}
-		~NiTriShape(){}
-		string GetBlockType() { return "NiTriShape"; }
+class NiTriShape : public AShape {
+public:
+	NiTriShape();
+	void Init() {}
+	~NiTriShape() {}
+
+	string GetBlockType() { return "NiTriShape"; }
 };
 
 /**
- * NiTexturingProperty -
+ * NiTriStrips - 
  */
-class NiTexturingProperty : public AProperty {
+class NiTriStrips : public AShape {
+public:
+	NiTriStrips();
+	void Init() {}
+	~NiTriStrips() {}
 
-	public:
-		NiTexturingProperty( ) {
-			AddAttr( "applymode", "Apply Mode" );
-			AddAttr( "int", "Texture Count?" );
-			AddAttr( "texture", "Base Texture" );
-			AddAttr( "texture", "Dark Texture" );
-			AddAttr( "texture", "Detail Texture" );
-			AddAttr( "texture", "Gloss Texture" );
-			AddAttr( "texture", "Glow Texture" );
-			AddAttr( "bumpmap", "Bump Map Texture" );
-			AddAttr( "texture", "Decal 0 Texture" );
-		}
-		~NiTexturingProperty(){}
-		string GetBlockType() { return "NiTexturingProperty"; }
+	string GetBlockType() { return "NiTriStrips"; }
 };
 
-    //byte useExternal      - set to 0 or 1
-
-    //if(useExternal == 0)
-    //    byte unknown      = 1
-    //    int pixelIndex    - index of NiPixelData record
-    //else
-    //    string file       - texture file name
-
-    //int pixelLayout
-    //int mipmap
-    //int alpha             - see the NIFLA spec
-
-    //byte unknown          = 1
+/**
+ * NiTexturingProperty -
+ */
+class NiTexturingProperty : public AProperty {
+public:
+	NiTexturingProperty( );
+	void Init() {}
+	~NiTexturingProperty() {}
+	string GetBlockType() { return "NiTexturingProperty"; }
+};
 
 /**
  * NiSourceTexture - Data for the associated texture, included in nif or external.
  */
 class NiSourceTexture : public ANamed{
-
-	public:
-		NiSourceTexture(){
-			AddAttr( "texsource", "Texture Source" );
-			AddAttr( "pixellayout", "Pixel Layout" );
-			AddAttr( "mipmapformat", "Use Mipmaps" );
-			AddAttr( "alphaformat", "Alpha Format" );
-			AddAttr( "byte", "Unknown Byte" );
-		}
-		~NiSourceTexture(){}
-		string GetBlockType() { return "NiSourceTexture"; }
+public:
+	NiSourceTexture();
+	void Init() {}
+	~NiSourceTexture() {}
+	string GetBlockType() { return "NiSourceTexture"; }
 };
 
 
@@ -464,167 +453,299 @@ class NiSourceTexture : public ANamed{
 /**
  * NiPixelData - Texture data for an included texture.
  */
-class NiPixelData : public ABlock{
-
-	public:
-		NiPixelData(){
-			AddAttr( "link", "Unknown Index" );
-			data = NULL; }
-		~NiPixelData(){ if (data != NULL) delete [] data; }
-
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ){}
-		string asString();
-		string GetBlockType() { return "NiPixelData"; }
+class NiPixelData : public AData {
+public:
+	NiPixelData() {
+		AddAttr( "link", "Unknown Index" );
+		data = NULL; }
+	~NiPixelData() { if (data != NULL) delete [] data; }
 
- 	private:
-		struct MipMap {
-			uint width, height, offset;
-		};
-		
-		uint unknownInt, rMask, gMask, bMask, aMask, bpp;
-		byte unknown8Bytes[8];
-		uint mipCount, bytesPerPixel;
-		vector<MipMap> mipmaps;
-		uint dataSize;
-		byte * data;
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiPixelData"; }
 
+private:
+	struct MipMap {
+		uint width, height, offset;
+	};
+	
+	uint unknownInt, rMask, gMask, bMask, aMask, bpp;
+	byte unknown8Bytes[8];
+	uint bytesPerPixel;
+	vector<MipMap> mipmaps;
+	uint dataSize;
+	byte * data;
 };
 
-
-
 /**
  * NiMaterialProperty - material properties
  */
 class NiMaterialProperty : public AProperty{
-	public:
-		NiMaterialProperty(){
-			AddAttr( "float3", "Ambient Color" );
-			AddAttr( "float3", "Diffuse Color" );
-			AddAttr( "float3", "Specular Color" );
-			AddAttr( "float3", "Emissive Color" );
-			AddAttr( "float", "Glossiness" );
-			AddAttr( "float", "Alpha" );
-		}
-		~NiMaterialProperty(){}
-		string GetBlockType() { return "NiMaterialProperty"; };
+public:
+	NiMaterialProperty();
+	void Init() {}
+	~NiMaterialProperty() {}
+	string GetBlockType() { return "NiMaterialProperty"; };
 };
 
 /**
  * NiSpecularProperty -
  */
 class NiSpecularProperty : public AProperty {
-
-	public:
-		NiSpecularProperty() {}
-		~NiSpecularProperty(){}
-		string GetBlockType() { return "NiSpecularProperty"; };
+public:
+	NiSpecularProperty();
+	void Init() {}
+	~NiSpecularProperty() {}
+	string GetBlockType() { return "NiSpecularProperty"; };
 };
 
-
+/**
+ * NiStencilProperty -
+ */
+class NiStencilProperty : public AProperty {
+public:
+	NiStencilProperty();
+	void Init() {}
+	~NiStencilProperty() {}
+	string GetBlockType() { return "NiStencilProperty"; };
+};
 
 /**
  * NiAlphaProperty - Does the mesh have alpha-blending enabled?
  */
 class NiAlphaProperty : public AProperty {
+public:
+	NiAlphaProperty();
+	void Init() {}
+	~NiAlphaProperty() {}
+	string GetBlockType() { return "NiAlphaProperty"; }
+};
 
-	public:
+/**
+ * AShapeData - Mesh data: vertices, vertex normals, etc.
+ */
+class AShapeData : public AData, public IShapeData {
+public:
+	AShapeData() {
+		AddAttr( "float3", "Center" );
+		AddAttr( "float", "Radius" );
+	}
+	~AShapeData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
 
-		NiAlphaProperty(){ 
-			AddAttr( "byte", "Unknown Byte" );
-		}
-		~NiAlphaProperty(){}
-		string GetBlockType() { return "NiAlphaProperty"; }
+	void * QueryInterface( int id );
+
+	//--IShapeData--//
+	//Counts
+	short GetVertexCount() { return short(vertices.size()); }
+	short GetUVSetCount() { return short(uv_sets.size()); }
+	void SetVertexCount(int n);
+	void SetUVSetCount(int n);
+	//Getters
+	vector<Vector3> GetVertices() { return vertices; }
+	vector<Vector3> GetNormals() { return normals; }
+	vector<Color> GetColors() { return colors; }
+	vector<UVCoord> GetUVSet( int index ) { return uv_sets[index]; }
+	//Setters
+	void SetVertices( const vector<Vector3> & in );
+	void SetNormals( const vector<Vector3> & in );
+	void SetColors( const vector<Color> & in );
+	void SetUVSet( int index, const vector<UVCoord> & in );
+protected:
+	vector<Vector3> vertices;
+	vector<Vector3> normals;
+	vector<Color> colors;
+	vector< vector<UVCoord> > uv_sets;
 };
 
+/**
+ * AParticlesData - Generic particle system data block.
+ */
 
+class AParticlesData : public AShapeData {
+public:
+	AParticlesData() {
+		AddAttr( "float", "Active Radius" );
+		AddAttr( "short", "Unknown Short", 0x0401000C );
+	}
+	~AParticlesData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+protected:
+	bool hasSizes;
+	short numActive, numValid;
+	vector<float> sizes;
+};
 
 /**
- * Contains the mesh data of a group.
+ * ARotatingParticlesData - Generic rotating particles data block. 
  */
-class NiTriShapeData : public ABlock, public ITriShapeData {
 
-	public:
+class ARotatingParticlesData : public AParticlesData {
+public:
+	ARotatingParticlesData() {}
+	~ARotatingParticlesData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+protected:
+	bool hasRotations;
+	vector<Quaternion> rotations;
+};
 
-		NiTriShapeData() : match_group_mode(false) {
-			AddAttr( "float3", "Center" );
-			AddAttr( "float", "Radius" );
-		}
-		~NiTriShapeData(){}
+/**
+ * NiParticleMeshesData - Particle meshes data. 
+ */
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version );
-		string asString();
-		string GetBlockType() { return "NiTriShapeData"; }
-		void * QueryInterface( int id );
+class NiParticleMeshesData : public ARotatingParticlesData {
+public:
+	NiParticleMeshesData() {
+		AddAttr( "link", "Unknown Link" );
+	}
+	~NiParticleMeshesData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
 
-		//--ITriShapeData--//
-		//Counts
-		short GetVertexCount() { return short(vertices.size()); }
-		short GetUVSetCount() { return short(uv_sets.size()); }
-		short GetTriangleCount() { return short(triangles.size()); }
-		void SetVertexCount(int n);
-		void SetUVSetCount(int n);
-		void SetTriangleCount(int n);
-		//Match Detection
-		void SetMatchDetectionMode(bool choice) { match_group_mode = choice; }
-		bool GetMatchDetectionMode() { return match_group_mode; }
-		//Getters
-		vector<Vector3> GetVertices() { return vertices; }
-		vector<Vector3> GetNormals() { return normals; }
-		vector<Color> GetColors() { return colors; }
-		vector<UVCoord> GetUVSet( int index ) { return uv_sets[index]; }
-		vector<Triangle> GetTriangles() { return triangles; }
-		//Setters
-		void SetVertices( const vector<Vector3> & in );
-		void SetNormals( const vector<Vector3> & in );
-		void SetColors( const vector<Color> & in );
-		void SetUVSet( int index, const vector<UVCoord> & in );
-		void SetTriangles( const vector<Triangle> & in );
+	string GetBlockType() { return "NiParticleMeshesData"; }
+protected:
+	
+};
 
-	private:
-		vector<Vector3> vertices;
-		vector<Vector3> normals;
-		vector<Color> colors;
-		vector< vector<UVCoord> > uv_sets;
-		vector<Triangle> triangles;
-		bool match_group_mode;
-		//short vert_count;
+/**
+ * NiAutoNormalParticlesData - Particle system data block (emits particles along vertex normals?).
+ */
+
+class NiAutoNormalParticlesData : public AParticlesData {
+public:
+	NiAutoNormalParticlesData() {}
+	~NiAutoNormalParticlesData() {}
+	string GetBlockType() { return "NiAutoNormalParticlesData"; }
+};
+
+/**
+ * NiTriShapeData - Holds mesh data using a list of singular triangles.
+ */
+class NiTriShapeData : public AShapeData, public ITriShapeData {
+public:
+	NiTriShapeData() : match_group_mode(false) {}
+	~NiTriShapeData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiTriShapeData"; }
+	void * QueryInterface( int id );
+
+	//--ITriShapeData--//
+	//Counts
+	short GetTriangleCount() { return short(triangles.size()); }
+	void SetTriangleCount(int n);
+	//Match Detection
+	void SetMatchDetectionMode(bool choice) { match_group_mode = choice; }
+	bool GetMatchDetectionMode() { return match_group_mode; }
+	//Getters
+	vector<Triangle> GetTriangles() { return triangles; }
+	//Setters
+	void SetTriangles( const vector<Triangle> & in );
+
+private:
+	vector<Triangle> triangles;
+	bool match_group_mode;
 };
 
+/**
+ * NiTriStripsData - Holds mesh data using strips of triangles.
+ */
+class NiTriStripsData : public AShapeData {
+public:
+	NiTriStripsData() {}
+	~NiTriStripsData() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+
+	string GetBlockType() { return "NiTriStripsData"; }
+
+private:
+	vector< vector<short> > strips;
+};
+
+/**
+ * NiCollisionData - Collision box.
+ */
+class NiCollisionData : public AData {
+public:
+	NiCollisionData() {
+		AddAttr( "int", "Unknown Int 1" );
+		AddAttr( "int", "Unknown Int 2" );
+		AddAttr( "byte", "Unknown Byte" );
+		AddAttr( "int", "Unknown Int 3" );
+		AddAttr( "int", "Unknown Int 4" );
+		AddAttr( "float3", "Radius" );
+	}
+	~NiCollisionData() {}
+
+	string GetBlockType() { return "NiCollisionData"; }
+};
+
+
+
 /**
  * NiKeyframeController
  */
 class NiKeyframeController : public AController {
 public:
-	NiKeyframeController(){
-		AddAttr( "link", "Data" );
-	}
-	~NiKeyframeController(){}
+	NiKeyframeController();
+	void Init() {}
+	~NiKeyframeController() {}
 	string GetBlockType() { return "NiKeyframeController"; }
 };
 
+/**
+ * NiKeyframeController
+ */
+class NiLookAtController : public AController {
+public:
+	NiLookAtController();
+	void Init() {}
+	~NiLookAtController() {}
+	string GetBlockType() { return "NiLookAtController"; }
+};
+
 /**
  * NiAlphaController
  */
 class NiAlphaController : public AController {
 public:
-	NiAlphaController(){
-		AddAttr( "link", "Data" );
-	}
-	~NiAlphaController(){}
+	NiAlphaController();
+	void Init() {}
+	~NiAlphaController() {}
 	string GetBlockType() { return "NiAlphaController"; }
 };
 
+/**
+ * NiFlipController
+ */
+class NiFlipController : public AController {
+public:
+	NiFlipController();
+	void Init() {}
+	~NiFlipController() {}
+	string GetBlockType() { return "NiFlipController"; }
+};
+
 /**
  * NiVisController
  */
 class NiVisController : public AController {
 public:
-	NiVisController(){
-		AddAttr( "link", "Data" );
-	}
-	~NiVisController(){}
+	NiVisController();
+	void Init() {}
+	~NiVisController() {}
 	string GetBlockType() { return "NiVisController"; }
 };
 
@@ -633,11 +754,9 @@ public:
  */
 class NiMaterialColorController : public AController {
 public:
-
-	NiMaterialColorController(){
-		AddAttr( "link", "Data" );
-	}
-	~NiMaterialColorController(){}
+	NiMaterialColorController();
+	void Init() {}
+	~NiMaterialColorController() {}
 	string GetBlockType() { return "NiMaterialColorController"; }
 };
 
@@ -646,11 +765,9 @@ public:
  */
 class NiUVController : public AController {
 public:
-	NiUVController (){
-		AddAttr( "link", "Data" );
-		AddAttr( "short", "Unknown Short" );
-	}
-	~NiUVController (){}
+	NiUVController();
+	void Init() {}
+	~NiUVController() {}
 	string GetBlockType() { return "NiUVController"; }
 };
 
@@ -660,15 +777,9 @@ public:
 
 class NiPathController : public AController {
 public:
-	NiPathController  (){
-		AddAttr( "int", "Unknown Int 1" );
-		AddAttr( "int", "Unknown Int 2" );
-		AddAttr( "int", "Unknown Int 3" );
-		AddAttr( "short", "Unknown Short" );
-		AddAttr( "link", "Pos Data" );
-		AddAttr( "link", "Float Data" );
-	}
-	~NiPathController  (){}
+	NiPathController();
+	void Init() {}
+	~NiPathController() {}
 	string GetBlockType() { return "NiPathController"; }
 };
 
@@ -678,8 +789,9 @@ public:
 
 class NiAmbientLight : public ALight {
 public:
-	NiAmbientLight  (){}
-	~NiAmbientLight  (){}
+	NiAmbientLight();
+	void Init() {}
+	~NiAmbientLight() {}
 	string GetBlockType() { return "NiAmbientLight"; }
 };
 
@@ -689,8 +801,9 @@ public:
 
 class NiDirectionalLight : public ALight {
 public:
-	NiDirectionalLight  (){}
-	~NiDirectionalLight  (){}
+	NiDirectionalLight();
+	void Init() {}
+	~NiDirectionalLight() {}
 	string GetBlockType() { return "NiDirectionalLight"; }
 };
 
@@ -700,11 +813,9 @@ public:
 
 class NiAutoNormalParticles : public ANode {
 public:
-	NiAutoNormalParticles  (){
-		AddAttr( "link", "Data" );
-		AddAttr( "link", "Unknown Index?" ); // Always -1
-	}
-	~NiAutoNormalParticles  (){}
+	NiAutoNormalParticles();
+	void Init() {}
+	~NiAutoNormalParticles() {}
 	string GetBlockType() { return "NiAutoNormalParticles"; }
 };
 
@@ -714,11 +825,9 @@ public:
 
 class NiRotatingParticles : public ANode {
 public:
-	NiRotatingParticles  (){
-		AddAttr( "link", "Data" );
-		AddAttr( "link", "Unknown Index?" ); // Always -1
-	}
-	~NiRotatingParticles  (){}
+	NiRotatingParticles();
+	void Init() {}
+	~NiRotatingParticles() {}
 	string GetBlockType() { return "NiRotatingParticles"; }
 }; 
 
@@ -728,35 +837,9 @@ public:
 
 class NiTextureEffect : public ANode {
 public:
-	NiTextureEffect  (){
-		AddAttr( "condint", "Conditional Int" );
-		AddAttr( "float", "Unknown Float 1" );
-		AddAttr( "float", "Unknown Float 2" );
-		AddAttr( "float", "Unknown Float 3" );
-		AddAttr( "float", "Unknown Float 4" );
-		AddAttr( "float", "Unknown Float 5" );
-		AddAttr( "float", "Unknown Float 6" );
-		AddAttr( "float", "Unknown Float 7" );
-		AddAttr( "float", "Unknown Float 8" );
-		AddAttr( "float", "Unknown Float 9" );
-		AddAttr( "float", "Unknown Float 10" );
-		AddAttr( "float", "Unknown Float 11" );
-		AddAttr( "float", "Unknown Float 12" );
-		AddAttr( "int", "Unknown Int 1" );
-		AddAttr( "int", "Unknown Int 2" );
-		AddAttr( "int", "Unknown Int 3" );
-		AddAttr( "int", "Unknown Int 4" );
-		AddAttr( "link", "Source Texture" );
-		AddAttr( "byte", "Unknown Byte" );
-		AddAttr( "float", "Unknown Float 13" );
-		AddAttr( "float", "Unknown Float 14" );
-		AddAttr( "float", "Unknown Float 15" );
-		AddAttr( "float", "Unknown Float 16" );
-		AddAttr( "short", "PS2 L?" );
-		AddAttr( "short", "PS2 K?" );
-		AddAttr( "short", "Unknown Short" );
-	}
-	~NiTextureEffect  (){}
+	NiTextureEffect();
+	void Init() {}
+	~NiTextureEffect() {}
 	string GetBlockType() { return "NiTextureEffect"; }
 }; 
 
@@ -766,106 +849,105 @@ public:
 
 class NiCamera : public ANode {
 public:
-	NiCamera  (){
-		AddAttr( "float", "Frustum Left" );
-		AddAttr( "float", "Frustum Right" );
-		AddAttr( "float", "Frustum Top" );
-		AddAttr( "float", "Frustum Bottom" );
-		AddAttr( "float", "Frustum Near" );
-		AddAttr( "float", "Frustum Far" );
-		AddAttr( "float", "Viewport Left" );
-		AddAttr( "float", "Viewport Right" );
-		AddAttr( "float", "Viewport Top" );
-		AddAttr( "float", "Viewport Bottom" );
-		AddAttr( "float", "LOLAdjust" );
-		AddAttr( "link", "Unknown Index?" ); // Always -1
-		AddAttr( "int", "Unknown Int" ); // Always 0
-	}
-	~NiCamera  (){}
+	NiCamera();
+	void Init() {}
+	~NiCamera() {}
 	string GetBlockType() { return "NiCamera"; }
 }; 
 
+/**
+ * NiParticleMeshes
+ */
+
+class NiParticleMeshes : public ANode {
+public:
+	NiParticleMeshes();
+	void Init() {}
+	~NiParticleMeshes() {}
+	string GetBlockType() { return "NiParticleMeshes"; }
+}; 
+
 /**
  * NiGravity
  */
 
-class NiGravity : public ABlock {
+class NiGravity : public AControllable {
 public:
-	NiGravity  (){
-		AddAttr( "link", "Extra Data" );
-		AddAttr( "link", "Controller" );
-		AddAttr( "float", "Unknown Float 1" );
-		AddAttr( "float", "Unknown Float 2" );
-		AddAttr( "int", "Unknown Int 1" );
-		AddAttr( "float", "Unknown Float 3" );
-		AddAttr( "float", "Unknown Float 4" );
-		AddAttr( "float", "Unknown Float 5" );
-		AddAttr( "float", "Unknown Float 6" );
-		AddAttr( "float", "Unknown Float 7" );
-		AddAttr( "float", "Unknown Float 8" );
-	}
-	~NiGravity  (){}
+	NiGravity();
+	void Init() {}
+	~NiGravity() {}
 	string GetBlockType() { return "NiGravity"; }
 }; 
 
+/**
+ * NiParticleBomb
+ */
+
+class NiParticleBomb : public AControllable {
+public:
+	NiParticleBomb();
+	void Init() {}
+	~NiParticleBomb() {}
+	string GetBlockType() { return "NiParticleBomb"; }
+}; 
+
 /**
  * NiPlanarCollider
  */
 
-class NiPlanarCollider : public ABlock {
-public:
-	NiPlanarCollider  (){
-		AddAttr( "link", "Extra Data" );
-		AddAttr( "link", "Controller" );
-		AddAttr( "float", "Unknown Float 1" );
-		AddAttr( "float", "Unknown Float 2" );
-		AddAttr( "float", "Unknown Float 3" );
-		AddAttr( "float", "Unknown Float 4" );
-		AddAttr( "float", "Unknown Float 5" );
-		AddAttr( "float", "Unknown Float 6" );
-		AddAttr( "float", "Unknown Float 7" );
-		AddAttr( "float", "Unknown Float 8" );
-		AddAttr( "float", "Unknown Float 9" );
-		AddAttr( "float", "Unknown Float 10" );
-		AddAttr( "float", "Unknown Float 11" );
-		AddAttr( "float", "Unknown Float 12" );
-		AddAttr( "float", "Unknown Float 13" );
-		AddAttr( "float", "Unknown Float 14" );
-		AddAttr( "float", "Unknown Float 15" );
-		AddAttr( "float", "Unknown Float 16" );
-	}
-	~NiPlanarCollider  (){}
+class NiPlanarCollider : public AControllable {
+public:
+	NiPlanarCollider();
+	void Init() {}
+	~NiPlanarCollider() {}
 	string GetBlockType() { return "NiPlanarCollider"; }
 }; 
 
+/**
+ * NiPlanarCollider
+ */
+
+class NiSphericalCollider : public AControllable {
+public:
+	NiSphericalCollider();
+	void Init() {}
+	~NiSphericalCollider() {}
+	string GetBlockType() { return "NiSphericalCollider"; }
+}; 
+
 /**
  * NiParticleGrowFade
  */
 
-class NiParticleGrowFade : public ABlock {
+class NiParticleGrowFade : public AControllable {
 public:
-	NiParticleGrowFade  (){
-		AddAttr( "link", "Extra Data" );
-		AddAttr( "link", "Controller" );
-		AddAttr( "float", "Unknown Float 1" );
-		AddAttr( "float", "Unknown Float 2" );
-	}
-	~NiParticleGrowFade  (){}
+	NiParticleGrowFade();
+	void Init() {}
+	~NiParticleGrowFade() {}
 	string GetBlockType() { return "NiParticleGrowFade"; }
 }; 
 
 /**
- * NiParticleGrowFade
+ * NiParticleMeshModifier
  */
 
-class NiParticleColorModifier : public ABlock {
+class NiParticleMeshModifier : public AControllable {
 public:
-	NiParticleColorModifier  (){
-		AddAttr( "link", "Extra Data" );
-		AddAttr( "link", "Controller" );
-		AddAttr( "link", "Color Data" );
-	}
-	~NiParticleColorModifier  (){}
+	NiParticleMeshModifier();
+	void Init() {}
+	~NiParticleMeshModifier() {}
+	string GetBlockType() { return "NiParticleMeshModifier"; }
+}; 
+
+/**
+ * NiParticleColorModifier
+ */
+
+class NiParticleColorModifier : public AControllable {
+public:
+	NiParticleColorModifier();
+	void Init() {}
+	~NiParticleColorModifier() {}
 	string GetBlockType() { return "NiParticleColorModifier"; }
 }; 
 
@@ -873,54 +955,23 @@ public:
  * NiGravity
  */
 
-class NiParticleRotation : public ABlock {
+class NiParticleRotation : public AControllable {
 public:
-	NiParticleRotation  (){
-		AddAttr( "link", "Extra Data" );
-		AddAttr( "link", "Controller" );
-		AddAttr( "byte", "Unknown Byte" );
-		AddAttr( "float", "Unknown Float 1" );
-		AddAttr( "float", "Unknown Float 2" );
-		AddAttr( "float", "Unknown Float 3" );
-		AddAttr( "float", "Unknown Float 4" );
-	}
-	~NiParticleRotation  (){}
+	NiParticleRotation();
+	void Init() {}
+	~NiParticleRotation() {}
 	string GetBlockType() { return "NiParticleRotation"; }
 }; 
 
-//NiGravity : Controlled
-//
-//    float[2] unknown
-//    int unknown
-//    float[6] unknown
-//
-//NiPlanarCollider : Controlled
-//
-//    float unknown[16]
-//
-//NiParticleGrowFade : Controlled
-//
-//    float unknown[2]
-//
-//NiParticleColorModifier : Controlled
-//
-//    int colorIndex    - index to NiColorData record
-//
-//NiParticleRotation : Controlled
-//
-//    byte unknown
-//    float unknown     = 1
-//    float unknown[3]
-
 /**
  * NiKeyframeData -
  */
-class NiKeyframeData : public ABlock, public IKeyframeData {
+class NiKeyframeData : public AData, public IKeyframeData {
 
 	public:
 
-		NiKeyframeData(){}
-		~NiKeyframeData(){}
+		NiKeyframeData() {}
+		~NiKeyframeData() {}
 
 		void Read( ifstream& in, unsigned int version );
 		void Write( ofstream& out, unsigned int version );
@@ -962,13 +1013,37 @@ class NiKeyframeData : public ABlock, public IKeyframeData {
 		vector< Key<float> > scaleKeys;
 };
 
+/**
+ * NiPalette
+ */
+
+class NiPalette : public AData {
+public:
+	NiPalette() {}
+	void Init() {}
+	~NiPalette() {}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+
+	string GetBlockType() { return "NiPalette"; }
+private:
+	byte unknownBytes[5];
+	byte palette[256][4];
+};
+
+/**
+ * NiSkinPartition
+ */
 
-//-- Skin Stuff --//
+class NiSkinPartition : public AData {
+public:
+	NiSkinPartition();
+	void Init() {}
+	~NiSkinPartition() {}
+	string GetBlockType() { return "NiSkinPartition"; }
+};
 
-    //int data              - NiSkinData index
-    //int root              - Skeleton root NiNode index (?)
-    //int boneCount
-    //int bones[boneCount]  - Index of bones (NiNode records)
 
 /**
  * NiSkinInstance
@@ -981,37 +1056,35 @@ public:
 	virtual void ReadBoneList( ifstream& in ) = 0;
 };
 
-class NiSkinInstance : public ABlock, public ISkinInstInternal {
-
-	public:
-
-		NiSkinInstance(){
-			AddAttr( "link", "Data" );
-			AddAttr( "skeletonroot", "Skeleton Root" );
-			AddAttr( "bones", "Bones" );
-		}
-		~NiSkinInstance(){}
-		string GetBlockType() { return "NiSkinInstance"; }
+class NiSkinInstance : public AData, public ISkinInstInternal {
+public:
+	NiSkinInstance(){
+		AddAttr( "link", "Data" );
+		AddAttr( "skeletonroot", "Skeleton Root" );
+		AddAttr( "bones", "Bones" );
+	}
+	~NiSkinInstance() {}
+	string GetBlockType() { return "NiSkinInstance"; }
 
-		void * QueryInterface( int id ) {
-			if ( id == SkinInstInternal ) {
-				return (void*)static_cast<ISkinInstInternal*>(this);;
-			} else {
-				return ABlock::QueryInterface( id );
-			}
+	void * QueryInterface( int id ) {
+		if ( id == SkinInstInternal ) {
+			return (void*)static_cast<ISkinInstInternal*>(this);;
+		} else {
+			return ABlock::QueryInterface( id );
 		}
+	}
 
-		//ISkinInstInternal
+	//ISkinInstInternal
 
-		vector<int> GetBoneList() { return bones; }
+	vector<int> GetBoneList() { return bones; }
 
-		void ReadBoneList( ifstream& in ) {
-			int len = ReadUInt( in );
-			bones.resize( len );
-			for (int i = 0; i < len; ++i ) {
-				bones[i] = ReadUInt( in );
-			}
+	void ReadBoneList( ifstream& in ) {
+		int len = ReadUInt( in );
+		bones.resize( len );
+		for (int i = 0; i < len; ++i ) {
+			bones[i] = ReadUInt( in );
 		}
+	}
 private:
 	vector<int> bones;
 };
@@ -1024,17 +1097,18 @@ public:
 	virtual void RemoveBoneByPtr( IBlock * bone ) = 0;
 };
 
-class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
+class NiSkinData : public AData, public ISkinData, public ISkinDataInternal {
 
 	public:
 
-		NiSkinData(){ 
+		NiSkinData() { 
 			SetIdentity33(rotation);
 			translation[0] = 0.0f;
 			translation[1] = 0.0f;
 			translation[2] = 0.0f;
 			scale = 1.0f;
-			unknown = -1;
+			unknownInt = -1;
+			unknownByte = 1;
 		}
 		~NiSkinData();
 
@@ -1068,7 +1142,8 @@ class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
 		Matrix33 rotation;
 		fVector3 translation;
 		float  scale;
-		int unknown;
+		int unknownInt;
+		byte unknownByte;
 		map<IBlock*, Bone> bone_map;
 		vector<Bone> bones;		
 };
@@ -1076,233 +1151,232 @@ class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
 //-- New Nodes--//
 
 class NiGeomMorpherController : public AController{
+public:
+	NiGeomMorpherController();
+	void Init() {}
+	~NiGeomMorpherController() {}
 
-	public:
-
-		NiGeomMorpherController(){
-			AddAttr( "link", "Morph Data" );
-			AddAttr( "byte", "Unknown Byte" );
-		}
-		~NiGeomMorpherController(){}
-
-		string asString();
-		string GetBlockType() { return "NiGeomMorpherController"; }
+	string asString();
+	string GetBlockType() { return "NiGeomMorpherController"; }
 };
 
-class NiColorData : public ABlock{
-
-	public:
-
-		NiColorData(){}
-		~NiColorData(){}
+class NiColorData : public AData {
+public:
+	NiColorData() {}
+	~NiColorData() {}
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString();
-		string GetBlockType() { return "NiColorData"; };
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiColorData"; };
 
-	private:
-		uint colorCount, keyType;
-		vector<Key<fVector4> > keys;
+private:
+	uint keyType;
+	vector< Key<fVector4> > keys;
 };
 
-class NiFloatData : public ABlock{
+/**
+ * NiControllerSequence - Root node in .kf files (version 10.0.1.0 and up).
+ */
+class NiControllerSequence : public AData {
+public:
+	NiControllerSequence() {
+		_namable = true;
+	}
+	~NiControllerSequence() {}
 
-	public:
+	string GetBlockType() { return "NiControllerSequence"; }
+private:
+	vector< pair< string, blk_ref> > controllers;
+};
 
-		NiFloatData(){}
-		~NiFloatData(){}
+class NiFloatData : public AData {
+public:
+	NiFloatData() {}
+	~NiFloatData() {}
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString();
-		string GetBlockType() { return "NiFloatData"; };
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiFloatData"; };
 
-	private:
-		uint colorCount, keyType;
-		vector<Key<float> > keys;
+private:
+	uint keyType;
+	vector<Key<float> > keys;
 };
 
-class NiStringExtraData : public AExtraData{
-
-	public:
+class NiStringExtraData : public AExtraData {
+public:
+	NiStringExtraData() {
+		AddAttr( "string", "String Data" );
+	}
+	~NiStringExtraData() {}
 
-		NiStringExtraData(){
-			AddAttr( "string", "String Data" );
-		}
-		~NiStringExtraData(){}
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiStringExtraData"; }
+};
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version );
-		string asString();
-		string GetBlockType() { return "NiStringExtraData"; };
+class NiBooleanExtraData : public AExtraData {
+public:
+	NiBooleanExtraData() {
+		AddAttr( "byte", "Boolean Data" );
+	}
+	~NiBooleanExtraData() {}
 
-	private:
-		string strData;
+	string GetBlockType() { return "NiBooleanExtraData"; };
 };
 
-class NiMorphData : public ABlock, public IMorphData {
+class NiIntegerExtraData : public AExtraData {
+public:
+	NiIntegerExtraData() {
+		AddAttr( "int", "Integer Data" );
+	}
+	~NiIntegerExtraData() {}
 
-	public:
+	string GetBlockType() { return "NiIntegerExtraData"; };
+};
 
-		NiMorphData(){
-			AddAttr( "byte", "Unknown Byte" );
-		}
-		~NiMorphData(){}
+class NiMorphData : public AData, public IMorphData {
+public:
+	NiMorphData() {
+		AddAttr( "byte", "Unknown Byte" );
+	}
+	~NiMorphData() {}
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version );
-		string asString();
-		string GetBlockType() { return "NiMorphData"; };
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiMorphData"; };
 
-		void * QueryInterface( int id ) {
-			if ( id == ID_MORPH_DATA ) {
-				return (void*)static_cast<IMorphData*>(this);;
-			} else {
-				return ABlock::QueryInterface( id );
-			}
+	void * QueryInterface( int id ) {
+		if ( id == ID_MORPH_DATA ) {
+			return (void*)static_cast<IMorphData*>(this);;
+		} else {
+			return ABlock::QueryInterface( id );
 		}
+	}
 
-		//--IMorphData Functions --//
-		int GetVertexCount() { return vertCount; }
-		void SetVertexCount( int n );
-		int GetMorphCount() { return int(morphs.size()); }
-		void SetMorphCount( int n ) { morphs.resize( n ); }
-		vector< Key<float> > GetMorphKeys( int n ) { return morphs[n].keys; }
-		void SetMorphKeys( int n, vector< Key<float> > & keys ) { morphs[n].keys = keys; }
-		vector<Vector3> GetMorphVerts( int n) { return morphs[n].morph; }
-		void SetMorphVerts( int n, const vector<Vector3> & in );
+	//--IMorphData Functions --//
+	int GetVertexCount() { return vertCount; }
+	void SetVertexCount( int n );
+	int GetMorphCount() { return int(morphs.size()); }
+	void SetMorphCount( int n ) { morphs.resize( n ); }
+	vector< Key<float> > GetMorphKeys( int n ) { return morphs[n].keys; }
+	void SetMorphKeys( int n, vector< Key<float> > & keys ) { morphs[n].keys = keys; }
+	vector<Vector3> GetMorphVerts( int n) { return morphs[n].morph; }
+	void SetMorphVerts( int n, const vector<Vector3> & in );
 
-	private:
-		struct Morph {
-			Morph() :  keyType(QUADRATIC_KEY) {}
-			~Morph() {}
-			KeyType keyType;
-			vector< Key<float> > keys;
-			vector< Vector3 > morph;
-		};
-		
-		uint vertCount;
-		vector<Morph> morphs;
+private:
+	struct Morph {
+		Morph() :  keyType(QUADRATIC_KEY) {}
+		~Morph() {}
+		KeyType keyType;
+		vector< Key<float> > keys;
+		vector< Vector3 > morph;
+	};
+	
+	uint vertCount;
+	vector<Morph> morphs;
 };
 
-class NiPosData : public ABlock{
-
-	public:
-
-		NiPosData(){}
-		~NiPosData(){}
+class NiPosData : public AData {
+public:
+	NiPosData() {}
+	~NiPosData() {}
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString();
-		string GetBlockType() { return "NiPosData"; }
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiPosData"; }
 
-	private:
-		uint posCount, keyType;
-		vector<Key<fVector3> > keys;
+private:
+	uint keyType;
+	vector<Key<fVector3> > keys;
 };
 
-class NiRotatingParticlesData : public ABlock{
-
-	public:
-
-		NiRotatingParticlesData(){}
-		~NiRotatingParticlesData(){}
-
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString() { return string(""); };
-		string GetBlockType() { return "NiRotationparticlesData"; }
-
-	private:
+class NiRotatingParticlesData : public ARotatingParticlesData {
+public:
+	NiRotatingParticlesData() {}
+	~NiRotatingParticlesData() {}
 
+	string GetBlockType() { return "NiRotationparticlesData"; }
 };
 
 class NiTextKeyExtraData : public AExtraData, public ITextKeyExtraData {
+public:
+	NiTextKeyExtraData() {
+		AddAttr( "int", "Unknown Int", 0, 0x04020200 );
+	}
+	~NiTextKeyExtraData() {}
 
-	public:
-
-		NiTextKeyExtraData(){
-			AddAttr( "int", "Unknown Int" );
-		}
-		~NiTextKeyExtraData(){}
-
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version );
-		string asString();
-		string GetBlockType() { return "NiTextKeyExtraData"; }
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiTextKeyExtraData"; }
 
-		void * QueryInterface( int id ) {
-			if ( id == ID_TEXT_KEY_EXTRA_DATA ) {
-				return (void*)static_cast<ITextKeyExtraData*>(this);;
-			} else {
-				return AExtraData::QueryInterface( id );
-			}
+	void * QueryInterface( int id ) {
+		if ( id == ID_TEXT_KEY_EXTRA_DATA ) {
+			return (void*)static_cast<ITextKeyExtraData*>(this);;
+		} else {
+			return AExtraData::QueryInterface( id );
 		}
+	}
 
-		//--ITextKeyExtraData Functions--//
-		virtual vector< Key<string> > GetRotateKeys() { return _keys; }
-		virtual void SetRotateKeys( vector< Key<string> > & keys ) { _keys = keys; }
+	//--ITextKeyExtraData Functions--//
+	virtual vector< Key<string> > GetRotateKeys() { return _keys; }
+	virtual void SetRotateKeys( vector< Key<string> > & keys ) { _keys = keys; }
 
-	private:
-		vector< Key<string> > _keys;
+private:
+	vector< Key<string> > _keys;
 };
 
-class NiUVData : public ABlock{
-
-	public:
-
-		NiUVData(){}
-		~NiUVData(){}
+class NiUVData : public AData {
+public:
+	NiUVData() {}
+	~NiUVData() {}
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString();
-		string GetBlockType() { return "NiUVData"; }
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiUVData"; }
 
-	private:
-		struct UVGroup {
-			uint count;
-			uint keyType;
-			vector<Key<float> > keys;
-		};
-		UVGroup groups[4];
+private:
+	struct UVGroup {
+		uint keyType;
+		vector<Key<float> > keys;
+	};
+	UVGroup groups[4];
 };
 
 class NiVertWeightsExtraData : public AExtraData{
+public:
+	NiVertWeightsExtraData() {}
+	~NiVertWeightsExtraData() {}
 
-	public:
-
-		NiVertWeightsExtraData(){}
-		~NiVertWeightsExtraData(){}
-
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString();
-		string GetBlockType() { return "NiVertWeightsExtraData"; }
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiVertWeightsExtraData"; }
 
-	private:
-		uint bytes;
-		ushort verts;
-		vector<float> weights;
+private:
+	uint bytes;
+	vector<float> weights;
 };
 
-class NiVisData : public ABlock{
-
-	public:
-
-		NiVisData(){}
-		~NiVisData(){}
+class NiVisData : public AData {
+public:
+	NiVisData() {}
+	~NiVisData() {}
 
-		void Read( ifstream& in, unsigned int version );
-		void Write( ofstream& out, unsigned int version ) {}
-		string asString();
-		string GetBlockType() { return "NiVisData"; }
+	void Read( ifstream& in, unsigned int version );
+	void Write( ofstream& out, unsigned int version );
+	string asString();
+	string GetBlockType() { return "NiVisData"; }
 
-	private:
-		uint visCount;
-		vector<Key<byte> > keys;
+private:
+	vector<Key<byte> > keys;
 };
 
 class UnknownMixIn {
@@ -1311,7 +1385,7 @@ public:
 		data = NULL;
 		_block_type = block_type;
 	}
-	~UnknownMixIn(){ if (data != NULL) delete [] data; }
+	~UnknownMixIn() { if (data != NULL) delete [] data; }
 	void Read( ifstream& in, unsigned int version );
 	void Write( ofstream& out, unsigned int version );
 	string asString();
@@ -1326,7 +1400,7 @@ private:
 class UnknownBlock : public ABlock, public UnknownMixIn {
 public:
 	UnknownBlock( string block_type ) : UnknownMixIn(block_type) {}
-	~UnknownBlock(){}
+	~UnknownBlock() {}
 	void Read( ifstream& in, unsigned int version ) {
 		//cout << endl << "Unknown Block Type found:  " << GetBlockType() << "\a" << endl;
 		ABlock::Read( in, version );
@@ -1346,7 +1420,7 @@ public:
 class UnknownControllerBlock : public AController, public UnknownMixIn {
 public:
 	UnknownControllerBlock( string block_type ) : UnknownMixIn(block_type) {}
-	~UnknownControllerBlock(){}
+	~UnknownControllerBlock() {}
 	void Read( ifstream& in, unsigned int version ) {
 		ABlock::Read( in, version );
 		UnknownMixIn::Read( in, version );
@@ -1371,7 +1445,7 @@ public:
 class UnknownPropertyBlock : public AProperty, public UnknownMixIn {
 public:
 	UnknownPropertyBlock( string block_type ) : UnknownMixIn(block_type) {}
-	~UnknownPropertyBlock(){}
+	~UnknownPropertyBlock() {}
 	void Read( ifstream& in, unsigned int version ) {
 		ABlock::Read( in, version );
 		UnknownMixIn::Read( in, version );
@@ -1396,111 +1470,23 @@ public:
 /**
  * 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(){}
+class NiParticleSystemController : public AParticleSystemController {
+public:
+	NiParticleSystemController();
+	void Init() {}
+	~NiParticleSystemController() {}
 	string GetBlockType() { return "NiParticleSystemController"; }
 };
 
-#endif // TAH_NIF_LIB_NIF_BLOCKS_H
+/**
+ * NiBSPArrayController
+ */
+class NiBSPArrayController : public AParticleSystemController {
+public:
+	NiBSPArrayController();
+	void Init() {}
+	~NiBSPArrayController() {}
+	string GetBlockType() { return "NiBSPArrayController"; }
+};
+
+#endif
diff --git a/NIF_IO.cpp b/NIF_IO.cpp
index 974048d3..1a15b0a4 100644
--- a/NIF_IO.cpp
+++ b/NIF_IO.cpp
@@ -231,6 +231,16 @@ string ReadString( ifstream &in ) {
 	return out;
 }
 
+bool ReadBool( ifstream &in, unsigned int version ) {
+	if ( version < 0x04010001 ) {
+		//Bools are stored as integers before version 4.1.0.1
+		return (ReadUInt( in ) != 0);
+	} else {
+		//And as bytes from 4.1.0.1 on
+		return (ReadByte( in ) != 0);
+	}
+}
+
 /**
  * Write utility functions.
  */
@@ -287,6 +297,16 @@ void WriteString( string val, ofstream& out ) {
 	out.write( val.c_str(), std::streamsize(val.size()) );
 }
 
+void WriteBool( bool val, ofstream& out, unsigned int version ) {
+	if ( version < 0x04010001 ) {
+		//Bools are stored as integers before version 4.1.0.1
+		WriteUInt(int(val), out );
+	} else {
+		//And as bytes from 4.1.0.1 on
+		WriteByte(byte(val), out );
+	}
+}
+
 void WriteBlockName( const char* name, uint nameLength, ofstream& out ){
 
 	WriteUInt( nameLength, out );
diff --git a/NIF_IO.h b/NIF_IO.h
index 1860fefd..b70ba863 100644
--- a/NIF_IO.h
+++ b/NIF_IO.h
@@ -196,6 +196,8 @@ 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 );
@@ -219,6 +221,8 @@ void WriteFloat( float val, ofstream& out );
 
 void WriteString( string val, ofstream& out );
 
+void WriteBool( bool val, ofstream& out, unsigned int version );
+
 void WriteFVector2( fVector2& fvec, ofstream& out );
 
 void WriteFVector3( fVector3& fvec, ofstream& out );
@@ -251,4 +255,4 @@ private:
 
 
 
-#endif // TAH_NIF_LIB_NIF_IO_H
+#endif
diff --git a/nif_attrs.h b/nif_attrs.h
index 8b62ab6b..8033dd88 100644
--- a/nif_attrs.h
+++ b/nif_attrs.h
@@ -46,7 +46,7 @@ const char ATTRERR[] = "Attribute type Missmatch.";
 
 class AAttr : public IAttr {
 public:
-	AAttr( string name, IBlock * owner ) : _name(name), _owner(owner) {}
+	AAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : _name(name), _owner(owner), _first_ver(first_ver), _last_ver(last_ver) {}
 	~AAttr() {}
 	string GetType() const { return "void"; }
 	string GetName() const { return _name; }
@@ -79,9 +79,25 @@ public:
 	void ClearLinks() { cout << "ClearLinks" << endl; throw runtime_error(ATTRERR); }
 	void RemoveLinks( blk_ref block ) { cout << "RemoveLinks" << endl; throw runtime_error(ATTRERR); }
 	blk_ref FindLink( string block_type ) { cout << "FindLink" << endl; throw runtime_error(ATTRERR); }
+	//Read/WriteFunctions
+	void Read( ifstream& in, unsigned int version ) {
+		if ( version >= _first_ver && version <= _last_ver ) {
+			this->ReadAttr( in, version );
+		}
+	}
+	void Write( ofstream& out, unsigned int version ) {
+		if ( version >= _first_ver && version <= _last_ver ) {
+			this->WriteAttr( out, version );
+		}
+	}
 protected:
+	//Internal Read/Write Functions
+	virtual void ReadAttr( ifstream& in, unsigned int version ) = 0;
+	virtual void WriteAttr( ofstream& out, unsigned int version ) = 0;
+
 	string _name;
 	IBlock * _owner;
+	unsigned int _first_ver, _last_ver;
 };
 
 class lnk_ref : public blk_ref {
@@ -161,11 +177,11 @@ private:
 
 class IntAttr : public AAttr {
 public:
-	IntAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	IntAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~IntAttr() {}
 	string GetType() const { return "int"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -183,11 +199,11 @@ private:
 
 class ShortAttr : public AAttr {
 public:
-	ShortAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	ShortAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~ShortAttr() {}
 	string GetType() const { return "short"; }
-	void Read( ifstream& in ) { data = ReadUShort( in ); }
-	void Write( ofstream& out ) { WriteUShort( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUShort( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUShort( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -205,11 +221,11 @@ private:
 
 class ByteAttr : public AAttr {
 public:
-	ByteAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	ByteAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~ByteAttr() {}
 	string GetType() const { return "byte"; }
-	void Read( ifstream& in ) { data = ReadByte( in ); }
-	void Write( ofstream& out ) { WriteByte( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadByte( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteByte( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -227,11 +243,11 @@ private:
 
 class FloatAttr : public AAttr {
 public:
-	FloatAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0.0f) {}
+	FloatAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0.0f) {}
 	~FloatAttr() {}
 	string GetType() const { return "float"; }
-	void Read( ifstream& in ) { data = ReadFloat( in ); }
-	void Write( ofstream& out ) { WriteFloat( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadFloat( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteFloat( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -249,19 +265,19 @@ private:
 
 class Float3Attr : public AAttr {
 public:
-	Float3Attr( string name, IBlock * owner) : AAttr(name, owner) {
+	Float3Attr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver) : AAttr(name, owner, first_ver, last_ver) {
 		data[0] = 0.0f;
 		data[1] = 0.0f;
 		data[2] = 0.0f;
 	}
 	~Float3Attr() {}
 	string GetType() const { return "float3"; }
-	void Read( ifstream& in ) { 
+	void ReadAttr( ifstream& in, unsigned int version ) { 
 		data[0] = ReadFloat( in );
 		data[1] = ReadFloat( in );
 		data[2] = ReadFloat( in );
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		WriteFloat( data[0], out );
 		WriteFloat( data[1], out );
 		WriteFloat( data[2], out );
@@ -294,11 +310,11 @@ private:
 
 class StringAttr : public AAttr {
 public:
-	StringAttr( string name, IBlock * owner) : AAttr( name, owner ) {}
+	StringAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) {}
 	~StringAttr() {}
 	string GetType() const { return "string"; }
-	void Read( ifstream& in ) { data = ReadString( in ); }
-	void Write( ofstream& out ) { WriteString( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadString( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteString( data, out ); }
 	string asString() const { return data; }
 	void Set(string & n) { data = n; }
 private:
@@ -307,10 +323,10 @@ private:
 
 class LinkAttr : public AAttr {
 public:
-	LinkAttr( string name, IBlock * owner ) : AAttr( name, owner ), link( owner ) {}
+	LinkAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), link( owner ) {}
 	~LinkAttr() {}
 	string GetType() const { return "link"; }
-	void Read( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		////Remove all links beloning to this attribute
 		//_owner->RemoveAttrLinks(this);
 
@@ -323,7 +339,7 @@ public:
 		//Set block index only
 		link.set_index( ReadUInt( in ) );
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		WriteUInt( link.get_index(), out );
 	}
 	string asString() const {
@@ -357,11 +373,11 @@ private:
 
 class FlagsAttr : public AAttr {
 public:
-	FlagsAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	FlagsAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~FlagsAttr() {}
 	string GetType() const { return "flags"; }
-	void Read( ifstream& in ) { data = ReadUShort( in ); }
-	void Write( ofstream& out ) { WriteUShort( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUShort( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUShort( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -386,21 +402,21 @@ private:
 
 class MatrixAttr : public AAttr {
 public:
-	MatrixAttr( string name, IBlock * owner) : AAttr( name, owner ) {
+	MatrixAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) {
 		data[0][0] = 1.0f;	data[0][1] = 0.0f;	data[0][2] = 0.0f;
 		data[1][0] = 0.0f;	data[1][1] = 1.0f;	data[1][2] = 0.0f;
 		data[2][0] = 0.0f;	data[2][1] = 0.0f;	data[2][2] = 1.0f;
 	}
 	~MatrixAttr() {}
 	string GetType() const { return "matrix33"; }
-	void Read( ifstream& in ) { 
+	void ReadAttr( ifstream& in, unsigned int version ) { 
 		for (int c = 0; c < 3; ++c) {
 			for (int r = 0; r < 3; ++r) {
 				data[r][c] = ReadFloat( in );
 			}
 		}
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		for (int c = 0; c < 3; ++c) {
 			for (int r = 0; r < 3; ++r) {
 				WriteFloat( data[r][c], out );
@@ -456,9 +472,9 @@ private:
 
 class BoneAttr : public AAttr {
 public:
-	BoneAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
+	BoneAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {}
 	~BoneAttr() {}
-	void Read ( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		ISkinInstInternal * data = (ISkinInstInternal*)_owner->QueryInterface( SkinInstInternal );
 		if ( data != NULL ) {
 			data->ReadBoneList( in );
@@ -466,7 +482,7 @@ public:
 			throw runtime_error ("Attempted to use a bone list attribute on a block that doesn't support it.");
 		}
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		ISkinInstInternal * data = (ISkinInstInternal*)_owner->QueryInterface( SkinInstInternal );
 		blk_ref data_blk = _owner->GetAttr("Data")->asLink();
 		if ( data_blk.is_null() == false )  {
@@ -519,10 +535,10 @@ typedef LinkSetList::iterator LinkSetIt;
 
 class LinkGroupAttr : public AAttr {
 public:
-	LinkGroupAttr( string name, IBlock * owner ) : AAttr( name, owner ) {}
+	LinkGroupAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) {}
 	~LinkGroupAttr() {}
 	string GetType() const { return "linkgroup"; }
-	void Read( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		int len = ReadUInt( in );
 		//cout << "Link Group Size:  " << len << endl;
 
@@ -536,7 +552,7 @@ public:
 				AddLink( blk_ref( index ) );
 		}
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		//Write the number of links
 		WriteUInt( uint(links.size()), out );
 		//cout << "Link Group Size:  " << uint(links.size()) << endl;
@@ -618,7 +634,7 @@ private:
 
 class BBoxAttr : public AAttr {
 public:
-	BBoxAttr( string name, IBlock * owner) : AAttr( name, owner ) {
+	BBoxAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) {
 		data.isUsed = false;
 		
 		data.unknownInt = 0;
@@ -635,8 +651,8 @@ public:
 	}
 	~BBoxAttr() {}
 	string GetType() const { return "bbox"; }
-	void Read( ifstream& in ) { 
-		data.isUsed = (ReadUInt( in ) != 0);
+	void ReadAttr( ifstream& in, unsigned int version ) { 
+		data.isUsed = ReadBool( in, version );
 		if ( data.isUsed ){
 			data.unknownInt = ReadUInt( in );
 			data.translation.x = ReadFloat( in );
@@ -652,8 +668,8 @@ public:
 			data.radius.z = ReadFloat( in );
 		}
 	}
-	void Write( ofstream& out ) {
-		WriteUInt(int(data.isUsed), out );
+	void WriteAttr( ofstream& out, unsigned int version ) {
+		WriteBool( data.isUsed, out, version );
 		if ( data.isUsed ){
 			WriteUInt( data.unknownInt, out );
 			WriteFloat( data.translation.x, out );
@@ -700,20 +716,20 @@ private:
 
 class CIntAttr : public AAttr {
 public:
-	CIntAttr( string name, IBlock * owner) : AAttr( name, owner ) {
+	CIntAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) {
 		data.isUsed = false;
 		data.unknownInt = 0;
 	}
 	~CIntAttr() {}
 	string GetType() const { return "condint"; }
-	void Read( ifstream& in ) {
-		data.isUsed = ( ReadUInt( in ) != 0 );
+	void ReadAttr( ifstream& in, unsigned int version ) {
+		data.isUsed = ReadBool( in, version );
 		if (data.isUsed) {
 			data.unknownInt = ReadUInt( in );
 		}
 	}
-	void Write( ofstream& out ) { 
-		WriteUInt( int(data.isUsed), out );
+	void WriteAttr( ofstream& out, unsigned int version ) { 
+		WriteBool( data.isUsed, out, version );
 		if (data.isUsed) {
 			WriteUInt( data.unknownInt, out );
 		}
@@ -743,11 +759,11 @@ private:
 
 class VertModeAttr : public AAttr {
 public:
-	VertModeAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	VertModeAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~VertModeAttr() {}
 	string GetType() const { return "vertmode"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -778,11 +794,11 @@ private:
 
 class LightModeAttr : public AAttr {
 public:
-	LightModeAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	LightModeAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~LightModeAttr() {}
 	string GetType() const { return "lightmode"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -821,23 +837,28 @@ private:
 
 class TextureAttr : public LinkAttr {
 public:
-	TextureAttr( string name, IBlock * owner, bool isBumpMap = false ) : LinkAttr(name, owner),  _isBumpMap(isBumpMap) {
+	TextureAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver, bool isBumpMap = false ) : LinkAttr(name, owner, first_ver, last_ver),  _isBumpMap(isBumpMap) {
 		memset( &data, 0, sizeof(data) );
 	}
 	~TextureAttr() {}
 	string GetType() const { return "texture"; }
-	void Read( ifstream& in ) {
-		data.isUsed = ( ReadUInt( in ) != 0 );
+	void ReadAttr( ifstream& in, unsigned int version ) {
+		data.isUsed = ReadBool( in, version );
 		if ( data.isUsed ) {	
 			//Read in link for TextureSource
-			LinkAttr::Read( in );
+			LinkAttr::ReadAttr( in, version );
 
 			data.clampMode = TexClampMode( ReadUInt( in ) );
 			data.filterMode = TexFilterMode( ReadUInt( in ) );
 			data.textureSet = ReadUInt( in );
 			data.PS2_L = ReadUShort( in );
 			data.PS2_K = ReadUShort( in );
-			data.unknownShort = ReadUShort( in );
+
+			//unknownShort exists up to version 4.1.0.12
+			if ( version <= 0x0401000C ) {
+				data.unknownShort = ReadUShort( in );
+			}
+
 			if ( _isBumpMap ) {
 				data.bmLumaScale = ReadFloat( in );
 				data.bmLumaOffset = ReadFloat( in );
@@ -848,18 +869,21 @@ public:
 			}
 		}
 	}
-	void Write( ofstream& out ) {
-		WriteUInt( uint(data.isUsed), out );
+	void WriteAttr( ofstream& out, unsigned int version ) {
+		WriteBool( data.isUsed, out, version );
 		if ( data.isUsed ) {
 			//Write link
-			LinkAttr::Write( out );
+			LinkAttr::WriteAttr( out, version );
 
 			WriteUInt( data.clampMode, out );
 			WriteUInt( data.filterMode, out );
 			WriteUInt( data.textureSet, out );
 			WriteUShort( data.PS2_L, out );
 			WriteUShort( data.PS2_K, out );
-			WriteUShort( data.unknownShort, out );
+			//unknownShort exists up to version 4.1.0.12
+			if ( version <= 0x0401000C ) {
+				WriteUShort( data.unknownShort, out );
+			}
 			if ( _isBumpMap ) {
 				WriteFloat( data.bmLumaScale, out );
 				WriteFloat( data.bmLumaOffset, out );
@@ -981,11 +1005,11 @@ private:
 
 class ApplyModeAttr : public AAttr {
 public:
-	ApplyModeAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	ApplyModeAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~ApplyModeAttr() {}
 	string GetType() const { return "applymode"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -1029,12 +1053,12 @@ private:
 
 class TexSourceAttr : public LinkAttr {
 public:
-	TexSourceAttr( string name, IBlock * owner ) : LinkAttr(name, owner) {}
+	TexSourceAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : LinkAttr(name, owner, first_ver, last_ver) {}
 	~TexSourceAttr() {
 		memset(&data, 0, sizeof(data) );
 	}
 	string GetType() const { return "texsource"; }
-	void Read( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		data.useExternal = ( ReadByte( in ) != 0 );
 		if ( data.useExternal ) {
 			data.fileName = ReadString( in );
@@ -1042,17 +1066,17 @@ public:
 			data.unknownByte = ReadByte( in );
 
 			//Read link for Pixel Data
-			LinkAttr::Read( in );
+			LinkAttr::ReadAttr( in, version );
 		}
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		WriteByte( byte(data.useExternal), out );
 		if ( data.useExternal ) {
 			WriteString( data.fileName, out );
 		} else {
 			WriteByte ( data.unknownByte, out );
 			//Write link for Pixel Data
-			LinkAttr::Write( out );
+			LinkAttr::WriteAttr( out, version );
 		}
 	}
 	string asString() const {
@@ -1085,11 +1109,11 @@ private:
 
 class PixelLayoutAttr : public AAttr {
 public:
-	PixelLayoutAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	PixelLayoutAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~PixelLayoutAttr() {}
 	string GetType() const { return "pixellayout"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -1138,11 +1162,11 @@ private:
 
 class MipMapFormatAttr : public AAttr {
 public:
-	MipMapFormatAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	MipMapFormatAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~MipMapFormatAttr() {}
 	string GetType() const { return "mipmapformat"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -1179,11 +1203,11 @@ private:
 
 class AlphaFormatAttr : public AAttr {
 public:
-	AlphaFormatAttr( string name, IBlock * owner) : AAttr( name, owner ), data(0) {}
+	AlphaFormatAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
 	~AlphaFormatAttr() {}
 	string GetType() const { return "alphaformat"; }
-	void Read( ifstream& in ) { data = ReadUInt( in ); }
-	void Write( ofstream& out ) { WriteUInt( data, out ); }
+	void ReadAttr( ifstream& in, unsigned int version ) { data = ReadUInt( in ); }
+	void WriteAttr( ofstream& out, unsigned int version ) { WriteUInt( data, out ); }
 	string asString() const {
 		stringstream out;
 		out.setf(ios::fixed, ios::floatfield);
@@ -1217,13 +1241,13 @@ private:
 
 class NodeAncestorAttr : public AAttr {
 public:
-	NodeAncestorAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
+	NodeAncestorAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {}
 	~NodeAncestorAttr() {}
 	string GetType() const { return "nodeancestor"; }
-	void Read( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		ReadUInt(in);
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		WriteUInt( _owner->GetParent()->GetBlockNum(), out );
 	}
 	blk_ref FindNodeAncestor() const {
@@ -1261,13 +1285,13 @@ public:
 
 class SkeletonRootAttr : public AAttr {
 public:
-	SkeletonRootAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
+	SkeletonRootAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {}
 	~SkeletonRootAttr() {}
 	string GetType() const { return "skeletonroot"; }
-	void Read( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		original_root = ReadUInt( in );  //Read data but do nothing with it
 	}
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		WriteUInt( FindRoot().get_index(), out );
 	}
 	blk_ref FindRoot() const {
@@ -1317,11 +1341,11 @@ private:
 
 class ParticleGroupAttr : public AAttr {
 public:
-	ParticleGroupAttr( string name, IBlock * owner ) : AAttr(name, owner) {}
+	ParticleGroupAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {}
 	~ParticleGroupAttr() {}
 	string GetType() const { return "particlegroup"; }
 
-	void Read( ifstream& in ) {
+	void ReadAttr( ifstream& in, unsigned int version ) {
 		num_particles = ReadUShort( in );
 		num_valid = ReadUShort( in );
 
@@ -1337,7 +1361,7 @@ public:
 		}
 	}
 
-	void Write( ofstream& out ) {
+	void WriteAttr( ofstream& out, unsigned int version ) {
 		WriteUShort( num_particles, out );
 		WriteUShort( num_valid, out );
 
diff --git a/niflib.cpp b/niflib.cpp
index 80234cee..45077ade 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -64,30 +64,48 @@ blk_ref CreateBlock( string block_type ) {
 		block = new NiAmbientLight;
 	} else if (block_type == "NiAutoNormalParticles") {
 		block = new NiAutoNormalParticles;
+		} else if (block_type == "NiAutoNormalParticlesData") {
+		block = new NiAutoNormalParticlesData;
 	} else if (block_type == "NiBillboardNode") {
 		block = new NiBillboardNode;
+	} else if (block_type == "NiBooleanExtraData") {
+		block = new NiBooleanExtraData;
 	} else if (block_type == "NiBSAnimationNode") {
 		block = new NiBSAnimationNode;
+	} else if (block_type == "NiBSPArrayController") {
+		block = new NiBSPArrayController;
 	} else if (block_type == "NiBSParticleNode") {
 		block = new NiBSParticleNode;
 	} else if (block_type == "NiCamera") {
 		block = new NiCamera;
+	} else if (block_type == "NiCollisionData") {
+		block = new NiCollisionData;
 	} else if (block_type == "NiColorData") {
 		block = new NiColorData;
+	//} else if (block_type == "NiControllerSequence") {
+	//	block = new NiControllerSequence;
 	} else if (block_type == "NiDirectionalLight") {
 		block = new NiDirectionalLight;
 	} else if (block_type == "NiDitherProperty") {
 		block = new NiDitherProperty;
+	} else if (block_type == "NiFlipController") {
+		block = new NiFlipController;
 	} else if (block_type == "NiFloatData") {
 		block = new NiFloatData;
 	} else if (block_type == "NiGeomMorpherController") {
 		block = new NiGeomMorpherController;
 	} else if (block_type == "NiGravity") {
 		block = new NiGravity;
+	} else if (block_type == "NiIntegerExtraData") {
+		block = new NiIntegerExtraData;
 	} else if (block_type == "NiKeyframeController") {
 		block = new NiKeyframeController;
 	} else if (block_type == "NiKeyframeData") {
 		block = new NiKeyframeData;
+	} else if (block_type == "NiLODNode") {
+		block = new NiLODNode;
+	} else if (block_type == "NiLookAtController") {
+		block = new NiLookAtController;
 	} else if (block_type == "NiMaterialColorController") {
 		block = new NiMaterialColorController;
 	} else if (block_type == "NiMaterialProperty") {
@@ -96,10 +114,20 @@ blk_ref CreateBlock( string block_type ) {
 		block = new NiMorphData;
 	} else if (block_type == "NiNode") {
 		block = new NiNode;
+	} else if (block_type == "NiPalette") {
+		block = new NiPalette;
+	} else if (block_type == "NiParticleBomb") {
+		block = new NiParticleBomb;
 	} else if (block_type == "NiParticleColorModifier") {
 		block = new NiParticleColorModifier;
 	} else if (block_type == "NiParticleGrowFade") {
 		block = new NiParticleGrowFade;
+	} else if (block_type == "NiParticleMeshes") {
+		block = new NiParticleMeshes;
+	} else if (block_type == "NiParticleMeshesData") {
+		block = new NiParticleMeshesData;
+	} else if (block_type == "NiParticleMeshModifier") {
+		block = new NiParticleMeshModifier;
 	} else if (block_type == "NiParticleRotation") {
 		block = new NiParticleRotation;
 	} else if (block_type == "NiParticleSystemController") {
@@ -114,8 +142,8 @@ blk_ref CreateBlock( string block_type ) {
 		block = new NiPosData;
 	} else if (block_type == "NiRotatingParticles") {
 		block = new NiRotatingParticles;
-	//} else if (block_type == "NiRotatingParticlesData") {
-	//	block = new NiRotatingParticlesData;
+	} else if (block_type == "NiRotatingParticlesData") {
+		block = new NiRotatingParticlesData;
 	} else if ( block_type == "NiSequenceStreamHelper") {
 		block = new NiSequenceStreamHelper;
 	} else if (block_type == "NiShadeProperty") {
@@ -124,10 +152,16 @@ blk_ref CreateBlock( string block_type ) {
 		block = new NiSkinData;
 	} else if (block_type == "NiSkinInstance") {
 		block = new NiSkinInstance;
+	//} else if (block_type == "NiSkinPartition") {
+	//	block = new NiSkinPartition;
 	} else if (block_type == "NiSourceTexture") {
 		block = new NiSourceTexture;
 	} else if (block_type == "NiSpecularProperty") {
 		block = new NiSpecularProperty;
+	} else if (block_type == "NiSphericalCollider") {
+		block = new NiSphericalCollider;
+	} else if (block_type == "NiStencilProperty") {
+		block = new NiStencilProperty;
 	} else if (block_type == "NiStringExtraData") {
 		block = new NiStringExtraData;
 	} else if (block_type == "NiTextKeyExtraData") {
@@ -138,6 +172,8 @@ blk_ref CreateBlock( string block_type ) {
 		block = new NiTexturingProperty;
 	} else if (block_type == "NiTriShape") {
 		block = new NiTriShape;
+	} else if (block_type == "NiTriStrips") {
+		block = new NiTriStrips;
 	} else if (block_type == "NiTriShapeData") {
 		block = new NiTriShapeData;
 	} else if ( block_type == "NiUVController") {
@@ -214,11 +250,11 @@ vector<blk_ref> ReadNifList( string file_name ) {
 	uint numBlocks = ReadUInt( in );
 
 	//Output
-	//cout << endl 
-	//	 << "====[ File Header ]====" << endl
-	//	 << "Header:  " << header_string << endl
-	//	 << "Version:  " << Hex(version) << endl
-	//	 << "Number of blocks: " << int(numBlocks) << endl;
+	cout << endl << endl 
+		 << "====[ " << file_name << " | File Header ]====" << endl
+		 << "Header:  " << header_string << endl
+		 << "Version:  " << Hex(version) << endl
+		 << "Number of blocks: " << int(numBlocks) << endl;
 
 	//vector<blk_ref> v;
 	//return v;
@@ -254,7 +290,7 @@ vector<blk_ref> ReadNifList( string file_name ) {
 			throw runtime_error("Read failue - Bad block position");
 		}
 
-		//cout << endl << i << ":  " << blockName << endl;
+		cout << endl << i << ":  " << blockName;
 
 		//Create Block of the type that was found
 		blocks[i] = CreateBlock(blockName);
@@ -279,6 +315,8 @@ vector<blk_ref> ReadNifList( string file_name ) {
 			delete [] blockName;
 	}
 
+	cout << endl;
+
 	//--Read Footer--//
 	uint unknownInt = ReadUInt( in );
 	uint unknownInt2 = ReadUInt( in );
@@ -476,6 +514,10 @@ attr_ref::operator Texture() { return _attr->asTexture(); }
 
 //--Query Functions--//
 
+IShapeData * QueryShapeData( blk_ref & block ) {
+	return (IShapeData*)block->QueryInterface( ID_SHAPE_DATA );
+}
+
 ITriShapeData * QueryTriShapeData( blk_ref & block ) {
 	return (ITriShapeData*)block->QueryInterface( ID_TRI_SHAPE_DATA );
 }
diff --git a/niflib.h b/niflib.h
index 3d2cb886..931e6200 100644
--- a/niflib.h
+++ b/niflib.h
@@ -77,6 +77,7 @@ const int ID_NODE = 2;
 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;
 
 //NIF Versions
 const int VER_4_0_0_2 = 0x04000002;
@@ -362,6 +363,11 @@ public:
 
 	//To check for specialized Interfaces
 	virtual void * QueryInterface( int id ) = 0;
+
+	//Name Functions
+	virtual bool Namable() = 0;
+	virtual void SetName( string & name ) = 0;
+	virtual string GetName() = 0;
 	
 protected:
 	friend class blk_ref;
@@ -375,8 +381,8 @@ public:
 	virtual ~IAttr() {}
 	virtual string GetType() const = 0;
 	virtual string GetName() const = 0;
-	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;
 	//Getters
 	virtual int asInt() const = 0;
 	virtual float asFloat() const = 0;
@@ -422,31 +428,41 @@ public:
 	virtual Matrix44 GetLocalBindPos() = 0;
 };
 
-class ITriShapeData {
+class IShapeData {
 public:
-	ITriShapeData() {}
-	virtual ~ITriShapeData () {}
+	IShapeData() {}
+	virtual ~IShapeData() {}
 	//Counts
 	virtual short GetVertexCount() = 0;
 	virtual short GetUVSetCount() = 0;
-	virtual short GetTriangleCount() = 0;
 	virtual void SetVertexCount(int n) = 0;
 	virtual void SetUVSetCount(int n) = 0;
-	virtual void SetTriangleCount(int n) = 0;
-	//Match Detection
-	virtual void SetMatchDetectionMode(bool choice) = 0;
-	virtual bool GetMatchDetectionMode() = 0;
 	//Getters
 	virtual vector<Vector3> GetVertices() = 0;
 	virtual vector<Vector3> GetNormals() = 0;
 	virtual vector<Color> GetColors() = 0;
 	virtual vector<UVCoord> GetUVSet( int index ) = 0;
-	virtual vector<Triangle> GetTriangles() = 0;
 	//Setters
 	virtual void SetVertices( const vector<Vector3> & in ) = 0;
 	virtual void SetNormals( const vector<Vector3> & in ) = 0;
 	virtual void SetColors( const vector<Color> & in ) = 0;
 	virtual void SetUVSet( int index, const vector<UVCoord> & in ) = 0;
+};
+
+
+class ITriShapeData {
+public:
+	ITriShapeData() {}
+	virtual ~ITriShapeData () {}
+	//Counts
+	virtual short GetTriangleCount() = 0;
+	virtual void SetTriangleCount(int n) = 0;
+	//Match Detection
+	virtual void SetMatchDetectionMode(bool choice) = 0;
+	virtual bool GetMatchDetectionMode() = 0;
+	//Getters
+	virtual vector<Triangle> GetTriangles() = 0;
+	//Setters
 	virtual void SetTriangles( const vector<Triangle> & in ) = 0;
 };
 
@@ -857,9 +873,10 @@ public:
 	friend ostream & operator<<(ostream & lh, const blk_ref & rh) {
 		if (rh._block != NULL) {
 			lh << rh._block->GetBlockType() << "(Block " << rh._block->GetBlockNum() << ")";
+			
 			attr_ref attr = rh._block->GetAttr("Name");
-			if (attr.is_null() == false ) {
-				lh << " [" << attr->asString() << "]";
+			if ( rh->Namable() == true ) {
+				lh << " [" << rh->GetName() << "]";
 			}
 		} else {
 			if (rh._index == -1)
-- 
GitLab