diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
deleted file mode 100644
index 3c052f6c952e3a1e40bc4e5887db020da838dd58..0000000000000000000000000000000000000000
--- a/NIF_Blocks.cpp
+++ /dev/null
@@ -1,4963 +0,0 @@
-/* Copyright (c) 2005, NIF File Format Library and Tools
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-   * Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-   * Redistributions in binary form must reproduce the above
-     copyright notice, this list of conditions and the following
-     disclaimer in the documentation and/or other materials provided
-     with the distribution.
-
-   * Neither the name of the NIF File Format Library and Tools
-     project nor the names of its contributors may be used to endorse
-     or promote products derived from this software without specific
-     prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE. */
-
-//#define IM_DEBUG
-
-#include "NIF_Blocks.h"
-#include "nif_math.h"
-#include "nif_attrs.h"
-#include <cmath>
-#include <sstream>
-#ifdef IM_DEBUG
-#include <imdebug.h>
-#endif
-
-#ifdef WIN32
-#define endl "\r\n"
-#endif
-
-extern string current_file;
-
-/***********************************************************
- * ANode methods
- **********************************************************/
-
-Matrix44 ANode::GetLocalTransform() const {
-	//Get transform data from atributes
-	Matrix33 rot = GetAttr("Rotation")->asMatrix33();
-	Float3 tran = GetAttr("Translation")->asFloat3();
-	float scale = GetAttr("Scale")->asFloat();
-
-	return Matrix44( Vector3( tran[0], tran[1], tran[2] ), rot, scale );
-}
-
-Matrix44 ANode::GetWorldTransform() const {
-	//Get Parent Transform if there is one
-	blk_ref par = GetParent();
-	INode * node;
-	if ( par.is_null() == false && ( node = (INode*)par->QueryInterface(ID_NODE) ) != NULL) {
-		//Get Local Transform
-		Matrix44 local = GetLocalTransform();
-
-		//Get Parent World Transform
-		Matrix44 par_world = node->GetWorldTransform();
-
-		//Multipy local matrix and parent world matrix for result
-		return par_world * local;
-	}
-	else {
-		//No parent transform, simply return local transform
-		return GetLocalTransform();
-	}
-}
-
-Matrix44 ANode::GetWorldBindPos() const {
-	return bindPosition;
-	//for (int i = 0; i < 4; ++i) {
-	//	for (int j = 0; j < 4; ++j) {
-	//		out_matrix[i][j] = bindPosition[i][j];
-	//	}
-	//}
-}
-
-Matrix44 ANode::GetLocalBindPos() const {
-	//Get Parent Transform if there is one
-	blk_ref par = GetParent();
-	INode * node;
-	if ( par.is_null() == false && ( node = (INode*)par->QueryInterface(ID_NODE) ) != NULL) {
-		//There is a node parent
-		//multiply its inverse with this block's bind position to get the local bind position
-		Matrix44 par_mat = node->GetWorldBindPos();
-		Matrix44 par_inv = par_mat.Inverse();
-		
-		return bindPosition * par_inv;
-	}
-	else {
-		//No parent transform, simply return local transform
-		return GetWorldBindPos();
-	}
-}
-
-void ANode::SetWorldBindPos( Matrix44 const & m ) {
-	bindPosition = m;
-	//for (int i = 0; i < 4; ++i) {
-	//	for (int j = 0; j < 4; ++j) {
-	//		bindPosition[i][j] = in_matrix[i][j];
-	//	}
-	//}
-}
-
-void ANode::IncCrossRef( IBlock * block ) {
-	//Add block to list
-	ABlock::IncCrossRef( block );
-
-	ResetSkinnedFlag();
-}
-
-void ANode::DecCrossRef( IBlock * block ) {
-	ABlock::DecCrossRef( block );
-
-	ResetSkinnedFlag();
-}
-
-void ANode::ResetSkinnedFlag() {
-	//Count the number of cross references that are NiSkinData
-	int count = 0;
-	list<IBlock*>::iterator it;
-	for (it = _cross_refs.begin(); it != _cross_refs.end(); ++it) {
-		if ( (*it)->QueryInterface( ID_SKIN_DATA ) != NULL ) {
-			++count;
-		}
-	}
-
-	//Get Flags attribute
-	attr_ref flag_attr = GetAttr("Flags");
-	int flags = flag_attr->asInt();
-
-	//If count == 0, then flag SHOULD be set
-	if ( count == 0 && ((flags & 8) == 0) ) {
-		//Flag is not set, flip the bit
-		flags ^= 8;
-	}
-
-	//If count > 0, then flag should NOT be set
-	if ( count >> 0 && ((flags & 8) != 0) ) {
-		//Flag is set, flip the bit
-		flags ^= 8;
-	}
-
-	//Store result
-	flag_attr->Set(flags);
-}
-
-/***********************************************************
- * NiNode methods
- **********************************************************/
-
-string NiNode::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-	
-	out << ABlock::asString();
-
-	//Matrix33 m;
-	//GetAttr("Rotation")->asMatrix33( m );
-
-	//Vector rows[3];
-
-	//rows[0] = Vector(m[0][0], m[0][1], m[0][2]);
-	//rows[1] = Vector(m[1][0], m[1][1], m[1][2]);
-	//rows[2] = Vector(m[2][0], m[2][1], m[2][2]);
-
-
-	////out << "Rotation Matrix Test:" << endl
-	////	<< "   Dot Products of each row with each other row:" << endl;
-
-	////for (int i = 0; i < 3; ++i) {
-	////	for (int j = 0; j < 3; ++j) {
-	////		out << "      Rows " << i << " & " << j << ":  " << rows[i].dot(rows[j]) << endl;
-	////	}
-	////}
-
-	//float pi = 3.141592653589793f;
-	//out << "Euler Angles:" << endl
-	//	<< "   X:  " << atan2( m[1][2], m[2][2] ) / pi * 180.0 << endl
-	//	<< "   Y:  " << asin( -m[0][2] ) / pi * 180.0 << endl
-	//	<< "   Z:  " << atan2( m[0][1], m[0][0] ) / pi * 180.0 << endl;
-
-	//Quat q = MatrixToQuat( m );
-	//out << "Quaternion:  [" << setw(6) << q.w << " (" << setw(6) << q.x << "," << setw(6) << q.y << "," << setw(6) << q.z << ")]" << endl;
-
-	//Matrix built_up = IdentityMatrix();
-	//GetBuiltUpTransform(blk_ref(this), built_up);
-	//out << "Built Up Transformations:" << endl
-	//	<< "   |" << setw(6) << built_up(0,0) << "," << setw(6) << built_up(0,1) << "," << setw(6) << built_up(0,2) << "," << setw(6) << built_up(0,3) << " |" << endl
-	//	<< "   |" << setw(6) << built_up(1,0) << "," << setw(6) << built_up(1,1) << "," << setw(6) << built_up(1,2) << "," << setw(6) << built_up(1,3) << " |" << endl
-	//	<< "   |" << setw(6) << built_up(2,0) << "," << setw(6) << built_up(2,1) << "," << setw(6) << built_up(2,2) << "," << setw(6) << built_up(2,3) << " |" << endl
-	//	<< "   |" << setw(6) << built_up(3,0) << "," << setw(6) << built_up(3,1) << "," << setw(6) << built_up(3,2) << "," << setw(6) << built_up(3,3) << " |" << endl;
-
-	//for ( int r = 0; r < 3; ++r ) {
-	//	for ( int c = 0; c < 3; ++c) {
-	//		m[r][c] = built_up(r,c);
-	//	}
-	//}
-	//q = MatrixToQuat( m );
-	//out << "Quaternion:  [" << setw(6) << q.w << " (" << setw(6) << q.x << "," << setw(6) << q.y << "," << setw(6) << q.z << ")]" << endl;
-	//out << "Euler Angles:" << endl
-	//	<< "   X:  " << atan2( m[1][2], m[2][2] ) / pi * 180.0 << endl
-	//	<< "   Y:  " << asin( -m[0][2] ) / pi * 180.0 << endl
-	//	<< "   Z:  " << atan2( m[0][1], m[0][0] ) / pi * 180.0 << endl;
-
-	//built_up = built_up.inverse();
-
-	//out << "Inverse Built Up:" << endl
-	//	<< "   |" << setw(6) << built_up(0,0) << "," << setw(6) << built_up(0,1) << "," << setw(6) << built_up(0,2) << "," << setw(6) << built_up(0,3) << " |" << endl
-	//	<< "   |" << setw(6) << built_up(1,0) << "," << setw(6) << built_up(1,1) << "," << setw(6) << built_up(1,2) << "," << setw(6) << built_up(1,3) << " |" << endl
-	//	<< "   |" << setw(6) << built_up(2,0) << "," << setw(6) << built_up(2,1) << "," << setw(6) << built_up(2,2) << "," << setw(6) << built_up(2,3) << " |" << endl
-	//	<< "   |" << setw(6) << built_up(3,0) << "," << setw(6) << built_up(3,1) << "," << setw(6) << built_up(3,2) << "," << setw(6) << built_up(3,3) << " |" << endl;
-
-	//for ( int r = 0; r < 3; ++r ) {
-	//	for ( int c = 0; c < 3; ++c) {
-	//		m[r][c] = built_up(r,c);
-	//	}
-	//}
-	//q = MatrixToQuat( m );
-	//out << "Quaternion:  [" << setw(6) << q.w << " (" << setw(6) << q.x << "," << setw(6) << q.y << "," << setw(6) << q.z << ")]" << endl;
-
-	//out << "Euler Angles:" << endl
-	//	<< "   X:  " << atan2( m[1][2], m[2][2] ) / pi * 180.0 << endl
-	//	<< "   Y:  " << asin( -m[0][2] ) / pi * 180.0 << endl
-	//	<< "   Z:  " << atan2( m[0][1], m[0][0] ) / pi * 180.0 << endl;
-
-	out << "Flag Analysis:" << endl
-		<< "   Hidden:  ";
-
-	int flags = GetAttr("Flags")->asInt();
-
-	if (flags & 1)
-		out << "Yes" << endl;
-	else
-		out << "No" << endl;
-
-	out << "   Collision Detection Mode:  ";
-
-	
-	if (flags & 2)
-		out << "Use Triangles" << endl;
-	else if (flags & 4)
-		out << "Use Oriented Bounding Boxes" << endl;
-	else
-		out << "None" << endl;
-
-	out << "   Is Skin Influence:  ";
-
-	if (flags & 8)
-		out << "No" << endl;
-	else
-		out << "Yes" << endl;
-
-	//Create list of influenced skins
-	list<IBlock*> skin_refs;
-
-	list<IBlock*>::const_iterator it;
-	for (it = _cross_refs.begin(); it != _cross_refs.end(); ++it ) {
-		if ( (*it)->QueryInterface( ID_SKIN_DATA ) != NULL ) {
-			skin_refs.push_back(*it);
-		}
-	}
-
-	if (skin_refs.size() > 0) {
-		out << "Influenced Skins:" << endl;
-
-		list<IBlock*>::const_iterator it;
-		for (it = skin_refs.begin(); it != skin_refs.end(); ++it ) {
-			out << "   " << blk_ref(*it) << endl;
-		}
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiTexturingProperty methods
- **********************************************************/
-
-void NiTexturingProperty::Read( istream& file, unsigned int version ){
-
-	AProperty::Read( file, version );
-
-	appl_mode = ApplyMode( ReadUInt(file) );
-
-	uint tex_count = ReadUInt( file );
-
-	textures.resize( tex_count );
-
-	for ( uint i = 0; i < textures.size(); ++i ) {
-		NifStream( textures[i], file, version );
-
-		if ( i == BUMP_MAP && textures[i].isUsed == true ) {
-			NifStream( bmLumaScale, file );
-			NifStream( bmLumaOffset, file );
-			NifStream( bmMatrix[0][0], file );
-			NifStream( bmMatrix[1][0], file );
-			NifStream( bmMatrix[0][1], file );
-			NifStream( bmMatrix[1][1], file );
-		}
-	}
-
-	//Extra Texture group exists from version 10.0.1.0 on
-	if ( version >= VER_10_0_1_0 ) {
-		uint extra_tex_count = ReadUInt( file );
-
-		extra_textures.resize( extra_tex_count );
-
-		for ( uint i = 0; i < extra_textures.size(); ++i ) {
-			NifStream( extra_textures[i].first, file, version );
-			if ( extra_textures[i].first.isUsed ) {
-				NifStream( extra_textures[i].second, file );
-			}
-		}
-	}
-}
-
-void NiTexturingProperty::Write( ostream& file, unsigned int version ) const {
-
-	AProperty::Write( file, version );
-
-	WriteUInt( uint(appl_mode), file );
-
-	WriteUInt( uint(textures.size()), file );
-
-	for ( uint i = 0; i < textures.size(); ++i ) {
-		NifStream( textures[i], file, version );
-
-		if ( i == BUMP_MAP && textures[i].isUsed == true ) {
-			NifStream( bmLumaScale, file );
-			NifStream( bmLumaOffset, file );
-			NifStream( bmMatrix[0][0], file );
-			NifStream( bmMatrix[1][0], file );
-			NifStream( bmMatrix[0][1], file );
-			NifStream( bmMatrix[1][1], file );
-		}
-	}
-
-	//Extra Texture group exists from version 10.0.1.0 on
-	if ( version >= VER_10_0_1_0 ) {
-		WriteUInt( uint(extra_textures.size()), file );
-
-		for ( uint i = 0; i < extra_textures.size(); ++i ) {
-			NifStream( extra_textures[i].first, file, version );
-			if ( extra_textures[i].first.isUsed ) {
-				NifStream( extra_textures[i].second, file );
-			}
-		}
-	}
-}
-
-string NiTexturingProperty::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Apply Mode:  " << appl_mode << endl
-		<< "Main Textures:  " << uint(textures.size()) << endl;
-
-	for ( uint i = 0; i < textures.size(); ++i ) {
-		out << "   Texture " << i + 1 << ":  ";
-		switch (i) {
-			case BASE_MAP:
-				out << "Base Map";
-				break;
-			case DARK_MAP:
-				out << "Dark Map";
-				break;
-			case DETAIL_MAP:
-				out << "Detail Map";
-				break;
-			case GLOSS_MAP:
-				out << "Gloss Map";
-				break;
-			case GLOW_MAP:
-				out << "Glow Map";
-				break;
-			case BUMP_MAP:
-				out << "Bump Map";
-				break;
-			case DECAL_0_MAP:
-				out << "Decal 0 Map";
-				break;
-		};
-		out << endl;
-
-		out << textures[i].asString();
-	}
-
-	if ( textures.size() >= BUMP_MAP && textures[BUMP_MAP].isUsed == true ) {
-		out << "BumpMap Info:" << endl
-			<< "   Luma Offset:  " << bmLumaOffset << endl
-			<< "   Luma Scale:  " << bmLumaScale << endl
-			<< "   Matrix:" << endl
-			<< "      |" << setw(6) << bmMatrix[0][0] << "," << setw(6) << bmMatrix[0][1] << " |" << endl
-			<< "      |" << setw(6) << bmMatrix[1][0] << "," << setw(6) << bmMatrix[1][1] << " |" << endl;
-	}
-
-	for ( uint i = 0; i < extra_textures.size(); ++i ) {
-		out << "   Extra Texture " << i + 1 << ":  " << endl;
-		
-		out << extra_textures[i].first.asString()
-			<< "      Unknown Extra Int:  " << extra_textures[i].second << endl;
-	}
-
-	return out.str();
-}
-
-void NiTexturingProperty::FixLinks( const vector<blk_ref> & blocks ) {
-	ABlock::FixLinks( blocks );
-
-	//Main Textures
-	for (uint i = 0; i < textures.size(); ++i ) {
-		if ( textures[i].isUsed == true ) {
-			//Fix link for this child
-			textures[i].source = blocks[ textures[i].source.get_index() ];
-
-			//Add this block to child as a parent
-			AddChild( textures[i].source.get_block() );
-		}
-	}
-
-	//Extra Textures
-	for (uint i = 0; i < extra_textures.size(); ++i ) {
-		if ( extra_textures[i].first.isUsed == true ) {
-			//Fix link for this child
-			extra_textures[i].first.source = blocks[ extra_textures[i].first.source.get_index() ];
-
-			//Add this block to child as a parent
-			AddChild( extra_textures[i].first.source.get_block() );
-		}
-	}
-}
-
-list<blk_ref> NiTexturingProperty::GetLinks() const {
-	list<blk_ref> links = ABlock::GetLinks();
-
-	//--Add Internal Links--//
-
-	//Main Textures
-	for (uint i = 0; i < textures.size(); ++i ) {
-		if ( textures[i].isUsed == true ) {
-			links.push_back( textures[i].source );
-		}
-	}
-
-	//Extra Textures
-	for (uint i = 0; i < extra_textures.size(); ++i ) {
-		if ( extra_textures[i].first.isUsed == true ) {
-			links.push_back( extra_textures[i].first.source );
-		}
-	}
-
-	//Remove NULL links
-	links.remove( blk_ref(-1) );
-
-	return links;
-}
-
-NiTexturingProperty::~NiTexturingProperty() {
-	//Remove all parents that were set as this block is dying.
-
-	//Main Textures
-	for (uint i = 0; i < textures.size(); ++i ) {
-		if ( textures[i].isUsed == true ) {
-			RemoveChild( textures[i].source.get_block() );
-		}
-	}
-
-	//Extra Textures
-	for (uint i = 0; i < extra_textures.size(); ++i ) {
-		if ( extra_textures[i].first.isUsed == true ) {
-			RemoveChild( extra_textures[i].first.source.get_block() );
-		}
-	}
-}
-
-void NiTexturingProperty::SetTextureCount( int new_count ) {
-
-	if ( new_count < int(textures.size()) ) {
-		//Remove active texture links that are about to be destroyed as children
-		for ( uint i = new_count; i < textures.size(); ++i ) {
-			RemoveChild( textures[i].source.get_block() );
-		}
-	}
-
-	//Resize array
-	textures.resize( new_count );
-}
-
-void NiTexturingProperty::SetExtraTextureCount( int new_count ) {
-	if ( new_count < int(extra_textures.size()) ) {
-		//Remove active texture links that are about to be destroyed as children
-		for ( uint i = new_count; i < extra_textures.size(); ++i ) {
-			RemoveChild( extra_textures[i].first.source.get_block() );
-		}
-	}
-
-	//Resize array
-	extra_textures.resize( new_count );
-}
-
-void NiTexturingProperty::SetTexture( int n, TexDesc & new_val ) {
-	//Make sure index is not out of range
-	if ( n < 0 || n > int(textures.size()) ) {
-		throw runtime_error("SetTexture - Index out of range.  Call SetTextureCount first.");
-	}
-
-	//If new texture isUsed is false, then nullify any links
-	if ( new_val.isUsed == false ) {
-		new_val.source = blk_ref(-1);
-	}
-
-	//If blk_ref is different, children need to be changed
-	if ( new_val.source != textures[n].source ) {
-		//the new reference is different, discard the old child...
-		if ( textures[n].source.is_null() == false ) {
-			RemoveChild( textures[n].source.get_block() );
-		}
-		
-		//and add the new one
-		if ( new_val.source.is_null() == false ) {
-			AddChild( new_val.source.get_block() );
-		}
-	}
-
-	//Finally copy the values
-	textures[n] = new_val;
-}
-
-void NiTexturingProperty::SetExtraTexture( int n, TexDesc & new_val ) {
-	//Make sure index is not out of range
-	if ( n < 0 || n > int(extra_textures.size()) ) {
-		throw runtime_error("SetTexture - Index out of range.  Call SetTextureCount first.");
-	}
-
-	//If new texture isUsed is false, then nullify any links
-	if ( new_val.isUsed == false ) {
-		new_val.source = blk_ref(-1);
-	}
-
-	//If blk_ref is different, children need to be changed
-	if ( new_val.source != extra_textures[n].first.source ) {
-		//the new reference is different, discard the old child...
-		if ( extra_textures[n].first.source.is_null() == false ) {
-			RemoveChild( extra_textures[n].first.source.get_block() );
-		}
-		
-		//and add the new one
-		if ( new_val.source.is_null() == false ) {
-			AddChild( new_val.source.get_block() );
-		}
-	}
-
-	//Finally copy the values
-	extra_textures[n].first = new_val;
-}
-
-/***********************************************************
- * NiBoneLODController methods
- **********************************************************/
-
-void NiBoneLODController::Read( istream& file, unsigned int version ){
-
-	AController::Read( file, version );
-
-	unkInt1 = ReadUInt( file );
-	
-	uint numNodeGroups = ReadUInt( file );
-
-	unkInt2 = ReadUInt( file );
-	
-	// Read Node Groups
-	_node_groups.resize( numNodeGroups );
-	for (uint i = 0; i < _node_groups.size(); ++i ) {
-		uint groupSize = ReadUInt( file );
-		_node_groups[i].resize(groupSize);
-		for (uint j = 0; j < _node_groups[i].size(); ++j ) {
-			_node_groups[i][j].set_index( ReadUInt(file) );
-		}
-	}
-
-	// Read Shape Groups
-	uint numShapeGroups = ReadUInt( file );
-	_shape_groups.resize( numShapeGroups );
-	for ( uint i = 0; i < _shape_groups.size(); ++i ) {
-		uint groupSize = ReadUInt( file );
-		_shape_groups[i].resize( groupSize );
-		for ( uint j = 0; j < _shape_groups[i].size(); ++j ) {
-			_shape_groups[i][j].first.set_index( ReadUInt(file) );
-			_shape_groups[i][j].second.set_index( ReadUInt(file) );
-		}
-	}
-
-	//Read Shape Group 2
-	uint numShapeGroup2 = ReadUInt( file );
-	_shape_group2.resize( numShapeGroup2 );
-
-	for ( uint i = 0; i < _shape_group2.size(); ++i ) {
-		_shape_group2[i].set_index( ReadUInt(file) );
-	}
-	
-}
-
-void NiBoneLODController::Write( ostream& file, unsigned int version ) const {
-	AController::Write( file, version );
-
-	WriteUInt( unkInt1, file );
-	
-	WriteUInt( uint(_node_groups.size()), file );
-
-	WriteUInt( unkInt2, file );
-	
-	//Node Groups
-	for (uint i = 0; i < _node_groups.size(); ++i ) {
-		WriteUInt( uint(_node_groups[i].size()), file );
-		for (uint j = 0; j < _node_groups[i].size(); ++j ) {
-			WriteUInt( _node_groups[i][j]->GetBlockNum(), file );
-		}
-	}
-
-	//Shape Groups
-	WriteUInt( uint(_shape_groups.size()), file );
-	for ( uint i = 0; i < _shape_groups.size(); ++i ) {
-		WriteUInt( (uint)_shape_groups[i].size(), file );
-		for ( uint j = 0; j < _shape_groups[i].size(); ++j ) {
-			WriteUInt( _shape_groups[i][j].first->GetBlockNum(), file );
-			WriteUInt( _shape_groups[i][j].second->GetBlockNum(), file );
-		}
-	}
-
-	//Shape Group 2
-	WriteUInt( (uint)_shape_group2.size(), file );
-	for ( uint i = 0; i < _shape_group2.size(); ++i ) {
-		WriteUInt( _shape_group2[i]->GetBlockNum(), file );
-	}
-}
-
-string NiBoneLODController::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Unknown Int 1:  " << unkInt1 << endl
-		<< "Num Node Groups:  " << uint(_node_groups.size()) << endl
-		<< "Unknown Int 2:  " << unkInt2 << endl;
-
-	//Node Groups
-	for (uint i = 0; i < _node_groups.size(); ++i ) {
-		out << "   " << i + 1 << ":  " << " Group Size:  " << uint(_node_groups[i].size()) << endl;
-
-		for (uint j = 0; j < _node_groups[i].size(); ++j ) {
-			out << "      " << j + 1 << ":  " << _node_groups[i][j] << endl;
-		}
-	}
-
-	// Shape Groups
-	out << "Num Shape Groups:  " << uint(_shape_groups.size()) << endl;
-	for ( uint i = 0; i < _shape_groups.size(); ++i ) {
-		out << "   " << i + 1 << ":  " << " Group Size:  " << uint(_shape_groups[i].size()) << endl;
-		for ( uint j = 0; j < _shape_groups[i].size(); ++j ) {
-			out << "      " << j + 1 << ":  " << _shape_groups[i][j].first << endl
-				 << "      " << j + 1 << ":  " << _shape_groups[i][j].second << endl;
-		}
-	}
-
-	//Shape Group 2
-	out << "Num Shape Group 2:  " << uint(_shape_group2.size()) << endl;
-	for ( uint i = 0; i < _shape_group2.size(); ++i ) {
-		out << "   " << i + 1 << ":  " <<  _shape_group2[i] << endl;
-	}
-
-	return out.str();
-}
-
-void NiBoneLODController::FixLinks( const vector<blk_ref> & blocks ) {
-	ABlock::FixLinks( blocks );
-
-	//Node Groups
-	for (uint i = 0; i < _node_groups.size(); ++i ) {
-		for (uint j = 0; j < _node_groups[i].size(); ++j ) {
-			//Fix link for this child
-			_node_groups[i][j] = blocks[ _node_groups[i][j].get_index() ];
-
-			//Add this block to child as a parent
-			AddChild( _node_groups[i][j].get_block() );
-		}
-	}
-
-	// Shape Groups
-	for ( uint i = 0; i < _shape_groups.size(); ++i ) {
-		for ( uint j = 0; j < _shape_groups[i].size(); ++j ) {
-			//Fix links for this child
-			_shape_groups[i][j].first = blocks[ _shape_groups[i][j].first.get_index() ];
-			_shape_groups[i][j].second = blocks[ _shape_groups[i][j].second.get_index() ];
-
-			//Add these blocks to child as a parent
-			AddChild( _shape_groups[i][j].first.get_block() );
-			AddChild( _shape_groups[i][j].second.get_block() );
-		}
-	}
-
-	//Shape Group 2
-	for ( uint i = 0; i < _shape_group2.size(); ++i ) {
-		//Fix link for this child
-		_shape_group2[i] = blocks[ _shape_group2[i].get_index() ];
-
-		//Add this block to child as a parent
-		AddChild( _shape_group2[i].get_block() );
-	}
-}
-
-list<blk_ref> NiBoneLODController::GetLinks() const {
-	list<blk_ref> links = ABlock::GetLinks();
-
-	//--Add Internal Links--//
-
-	//Node Groups
-	for (uint i = 0; i < _node_groups.size(); ++i ) {
-		for (uint j = 0; j < _node_groups[i].size(); ++j ) {
-			links.push_back( _node_groups[i][j] );
-		}
-	}
-
-	// Shape Groups
-	for ( uint i = 0; i < _shape_groups.size(); ++i ) {
-		for ( uint j = 0; j < _shape_groups[i].size(); ++j ) {
-			links.push_back( _shape_groups[i][j].first );
-			links.push_back( _shape_groups[i][j].second );
-		}
-	}
-
-	//Shape Group 2
-	for ( uint i = 0; i < _shape_group2.size(); ++i ) {
-		links.push_back( _shape_group2[i] );
-	}
-
-	//Remove NULL links
-	links.remove( blk_ref(-1) );
-
-	return links;
-}
-
-NiBoneLODController::~NiBoneLODController() {
-	//Remove all parents that were set as this block is dying.
-
-	//Node Groups
-	for (uint i = 0; i < _node_groups.size(); ++i ) {
-		for (uint j = 0; j < _node_groups[i].size(); ++j ) {
-			RemoveChild( _node_groups[i][j].get_block() );
-		}
-	}
-
-	// Shape Groups
-	for ( uint i = 0; i < _shape_groups.size(); ++i ) {
-		for ( uint j = 0; j < _shape_groups[i].size(); ++j ) {
-			//Add these blocks to child as a parent
-			/*((ABlock*)_shape_groups[i][j].first.get)->RemoveParent( this );
-			((ABlock*)_shape_groups[i][j].second)->RemoveParent( this );*/
-			RemoveChild( _shape_groups[i][j].first.get_block() );
-			RemoveChild( _shape_groups[i][j].second.get_block() );
-		}
-	}
-
-	//Shape Group 2
-	for ( uint i = 0; i < _shape_group2.size(); ++i ) {
-		//Add this block to child as a parent
-		RemoveChild( _shape_group2[i].get_block() );
-	}
-}
-
-/***********************************************************
- * NiRangeLODData methods
- **********************************************************/
-
-void NiRangeLODData::Read( istream& file, unsigned int version ){
-	NifStream( _center, file );
-	int numRanges = ReadUInt( file );
-	ranges.resize( numRanges );
-	NifStream( ranges, file );
-}
-
-void NiRangeLODData::Write( ostream& file, unsigned int version ) const {
-	NifStream( _center, file );
-	WriteUInt( uint(ranges.size()), file );
-	NifStream( ranges, file );
-}
-
-string NiRangeLODData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "   LOD Center:  (" << _center.x << ", " << _center.y << ", " << _center.z << ")" << endl;
-
-	out << uint(ranges.size()) << endl;
-
-	for ( uint i = 0; i < ranges.size(); ++i ) {
-		out << "   " << i + 1 << ")   Near:  " << ranges[i].near << "   Far:  " << ranges[i].far << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiScreenLODData methods
- **********************************************************/
-
-void NiScreenLODData::Read( istream& file, unsigned int version ){
-	//8 unknown floats
-	for ( uint i = 0; i < 8; ++i ) {
-		NifStream( unk_floats[i], file );
-	}
-
-	//float list
-	uint unk_count = ReadUInt( file );
-	unk_float_list.resize( unk_count );
-
-	NifStream( unk_float_list, file );
-}
-
-void NiScreenLODData::Write( ostream& file, unsigned int version ) const {
-	//8 unknown floats
-	for ( uint i = 0; i < 8; ++i ) {
-		NifStream( unk_floats[i], file );
-	}
-
-	//float list
-	WriteUInt( uint( unk_float_list.size()), file );
-
-	NifStream( unk_float_list, file );
-}
-
-string NiScreenLODData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	//8 unknown floats
-	out << "Unknown 8 Floats:  " << endl;
-	for ( uint i = 0; i < 8; ++i ) {
-		out << "   " << i + 1 << ":  " << unk_floats[i] << endl;
-	}
-
-	//float list
-	out << "Unknown Float List:  " << uint(unk_float_list.size());
-	for ( uint i = 0; i < unk_float_list.size(); ++i ) {
-		out << "   " << i + 1 << ":  " << unk_float_list[i] << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * AShapeData methods
- **********************************************************/
-
-/**
- * AShapeData::Read - Assumes block name has already been read from in
- */
-void AShapeData::Read( istream& in, unsigned int version ){
-
-	//GetAttr("Name")->Read( in, version );
-	
-	ushort vert_count = ReadUShort( in );
-
-	//There is an unknown short here from version 10.1.0.0 on
-	if ( version >= VER_10_1_0_0 ) {
-		ReadUShort( in );
-	}
-
-	bool hasVertices = ReadBool( in, version );
-	if ( hasVertices != 0 ){
-		vertices.resize( vert_count );
-		for ( uint i = 0; i < vertices.size(); ++i ){
-			NifStream( vertices[i], in );
-		}
-	}
-
-	/// numTexSets up here up from version 10.0.1.0 on along with an unknown byte
-	ushort numTexSets;
-	bool hasUnknown;
-	if ( version >= VER_10_0_1_0 ) {
-		numTexSets = ReadByte( in );
-		hasUnknown = ReadBool( in, version );
-	}
-
-	bool hasNormals = ReadBool( in, version );;
-	if ( hasNormals != 0 ){
-		normals.resize( vert_count );
-		for ( uint i = 0; i < normals.size(); ++i ){
-			NifStream( normals[i], in );
-		}
-	}
-
-	//After version 10.1.0.0 there's several unknown vectors here
-	if ( version >= VER_10_1_0_0 && hasUnknown == true ) {
-		unk_vects.resize( vert_count * 2 );
-		for ( uint i = 0; i < unk_vects.size(); ++i ){
-			NifStream( unk_vects[i], in );
-		}
-	}
-
-	//Read center and radius but throw the values away
-	ReadFloat( in );
-	ReadFloat( in );
-	ReadFloat( in );
-	ReadFloat( in );
-
-	bool hasVertexColors = ReadBool( in, version );;
-	if ( hasVertexColors != 0 ){
-		colors.resize( vert_count );
-		for ( uint i = 0; i < colors.size(); ++i ){
-			NifStream( colors[i], in );
-		}
-	}
-	// numTexSets down here up to version 4.2.2.0
-	if ( version <= VER_4_2_2_0 ) {
-		numTexSets = ReadUShort( in );
-	}
-	// hasUVs does not exist after version 4.0.0.2
-	bool hasUVs = true;
-	if ( version <= VER_4_0_0_2 ) {
-		hasUVs = ReadBool( in, version );
-	}
-	if ( numTexSets > 0 && hasUVs == true ){
-		uv_sets.resize( numTexSets );
-		for ( uint i = 0; i < uv_sets.size(); ++i ){
-			uv_sets[i].resize( vert_count );
-			for ( uint j = 0; j < uv_sets[i].size(); ++j){
-				uv_sets[i][j].u = ReadFloat( in );
-				uv_sets[i][j].v = ReadFloat( in );
-			}
-		}
-	}
-
-	//Unknown Short here from version 10.0.1.0 on
-	//Just read it and throw it away for now
-	if ( version >= VER_10_0_1_0) {
-		ReadUShort( in );
-	}
-
-	//GetAttr("Unknown Link")->Read( in, version );
-}
-
-string AShapeData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Name:  " << GetAttr("Name")->asString() << endl;
-
-	out << "Vertices:  " << uint(vertices.size());
-	if (verbose) {
-		out << "   ";
-		for ( uint i = 0; i < vertices.size(); ++i) {
-			if (i % 3 == 0)
-				out << endl << "   ";
-			else
-				out << "  ";
-
-			out << "(" << setw(5) << vertices[i].x << ", " << setw(5) << vertices[i].y << ", " << setw(5) << vertices[i].z << " )";
-		}
-	} else {
-		out << endl << "<<Data Not Shown>>";
-	}
-	out << endl;
-
-	out << "Normals:  " << uint(normals.size());
-	if (verbose) {
-		out << "   ";
-		for ( uint i = 0; i < normals.size(); ++i) {
-			if (i % 3 == 0)
-				out << endl << "   ";
-			else
-				out << "  ";
-
-			out << "(" << setw(5) << normals[i].x << ", " << setw(5) << normals[i].y << ", " << setw(5) << normals[i].z << " )";
-		}
-	} else {
-		out << endl << "<<Data Not Shown>>";
-	}
-	out << endl;
-	
-	out << "Unknown Vectors:  " << uint(unk_vects.size());
-	if (verbose) {
-		out << "   ";
-		for ( uint i = 0; i < unk_vects.size(); ++i) {
-			if (i % 3 == 0)
-				out << endl << "   ";
-			else
-				out << "  ";
-
-			out << "(" << setw(5) << unk_vects[i].x << ", " << setw(5) << unk_vects[i].y << ", " << setw(5) << unk_vects[i].z << " )";
-		}
-	} else {
-		out << endl << "<<Data Not Shown>>";
-	}
-	out << endl;
-
-	Vector3 center;
-	float radius;
-	CalcCentAndRad( center, radius );
-
-
-	out << "Center:  (" << center.x << ", " << center.y << ", " << center.z << ")" << endl
-		<< "Radius:  " << radius << endl
-		<< "Vertex Colors:  " << uint(colors.size());
-	if (verbose) {
-		out << "   ";
-		for ( uint i = 0; i < colors.size(); ++i) {
-			if (i % 3 == 0)
-				out << endl << "   ";
-			else
-				out << "  ";
-			
-			out << "(" << setw(5) << colors[i].r << ", " << setw(5) << colors[i].g << ", " << setw(5) << colors[i].b << ", " << setw(5) << colors[i].a << " )";
-		}
-	} else {
-		out << endl << "<<Data Not Shown>>";
-	}
-	out << endl;
-
-	out << "Texture Coordinate Sets:  " << uint(uv_sets.size());
-	if (verbose) {
-		for ( uint i = 0; i < uv_sets.size(); ++i) {
-			out << endl 
-				<< "   UV Set " << i+1 << ":";
-
-			for ( uint j = 0; j < uv_sets[i].size(); ++j) {
-				if (j % 3 == 0)
-					out << endl << "      ";
-				else
-					out << "  ";
-
-				out << "(" << setw(5) << uv_sets[i][j].u << ", " << setw(5) << uv_sets[i][j].v << " )";
-			}
-		}
-	} else {
-		out << endl << "<<Data Not Shown>>";
-	}
-	out << endl;
-
-	out << "Unknown Link:  " << GetAttr("Unknown Link")->asString() << endl;
-
-	return out.str();
-}
-
-void AShapeData::CalcCentAndRad( Vector3 & center, float & radius ) const {
-	//Check if there are no vertices
-	if ( vertices.size() == 0 ) {
-		center.Set(0.0f, 0.0f, 0.0f);
-		radius = 0.0f;
-		return;
-	}
-	
-	//Set lows and highs to first vertex
-	Vector3 lows = vertices[0];
-	Vector3 highs = vertices[0];
-
-	//Iterate through the rest of the vertices, adjusting the stored values
-	//if a vertex with lower or higher values is found
-	for (vector<Vector3>::const_iterator i = vertices.begin()+1; i != vertices.end(); ++i ) {
-		if ( i->x > highs.x ) highs.x = i->x;
-		else if ( i->x < lows.x ) lows.x = i->x;
-
-		if ( i->y > highs.y ) highs.y = i->y;
-		else if ( i->y < lows.y ) lows.y = i->y;
-
-		if ( i->z > highs.z ) highs.z = i->z;
-		else if ( i->z < lows.z ) lows.z = i->z;
-	}
-
-	//Now we know the extent of the shape, so the center will be the average of the lows and highs.
-	center.x = (highs.x + lows.x) / 2.0f;
-	center.y = (highs.y + lows.y) / 2.0f;
-	center.z = (highs.z + lows.z) / 2.0f;
-
-	//The radius will be the largest distance from the center
-	Vector3 diff;
-	float dist2(0.0f), maxdist2(0.0f);
-	for (vector<Vector3>::const_iterator i = vertices.begin(); i != vertices.end(); ++i ) {
-		diff = center;
-		diff.x -= i->x;
-		diff.y -= i->y;
-		diff.z -= i->z;
-		dist2 = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
-		if ( dist2 > maxdist2 ) maxdist2 = dist2;
-	};
-	radius = sqrt(maxdist2);
-}
-
-/**
- * AShapeData::Write
- */
-void AShapeData::Write( ostream& out, unsigned int version ) const {
-
-	//GetAttr("Name")->Write( out, version );
-	
-	WriteUShort( ushort(vertices.size()), out );
-
-	//There is an unknown short here from version 10.1.0.0 on
-	if ( version >= VER_10_1_0_0 ) {
-		WriteUShort( 0, out );
-	}
-
-	WriteBool( vertices.size() > 0, out, version );
-
-	for ( uint i = 0; i < vertices.size(); ++i ){
-		NifStream( vertices[i], out );
-	}
-
-	/// numTexSets up here up from version 10.0.1.0 on along with an unkown byte
-	if ( version >= VER_10_0_1_0 ) {
-		WriteByte( byte(uv_sets.size()), out );
-		WriteBool( unk_vects.size() > 0, out, version );
-	}
-
-	WriteBool( normals.size() > 0, out, version );
-
-	for ( uint i = 0; i < normals.size(); ++i ){
-		NifStream( normals[i], out );
-	}
-
-	//Unkown vectors here from version 10.2.0.0 on
-	if ( version >= VER_10_2_0_0 ) {
-		for ( uint i = 0; i < unk_vects.size(); ++i ){
-			NifStream( unk_vects[i], out );
-		}
-	}
-
-	Vector3 center;
-	float radius;
-	CalcCentAndRad( center, radius );
-	
-	NifStream( center, out );
-	NifStream( radius, out );
-
-	if ( version <= VER_4_0_0_2 ) {
-		// NifTexture bug workaround:
-		if ( colors.size() > 0 )
-			WriteUInt( 0xffffffff, out );
-		else
-			WriteUInt( 0, out );		
-	} else
-		WriteBool( colors.size() > 0, out, version );
-
-	for ( uint i = 0; i < colors.size(); ++i ){
-		NifStream( colors[i], out );
-	}
-
-	// numTexSets down here up to version 4.2.2.0
-	if ( version <= VER_4_2_2_0 ) {
-		WriteUShort( ushort(uv_sets.size()), out );
-	}
-	// hasUVs does not exist after version 4.0.0.2
-	//bool hasUVs = true;
-	if ( version <= VER_4_0_0_2 ) {
-		//WriteBool( uv_sets.size() > 0, out, version );
-		// NifTexture bug workaround:
-		if (uv_sets.size() > 0)
-			WriteUInt( 0xffffffff, out );
-		else
-			WriteUInt( 0, out );
-	}
-
-	for ( uint i = 0; i < uv_sets.size(); ++i ){
-		for ( uint j = 0; j < uv_sets[i].size(); ++j){
-			NifStream(uv_sets[i][j].u, out );
-			NifStream(uv_sets[i][j].v, out );
-		}
-	}
-
-	//Unknown Short here from version 10.0.1.0 on
-	//Just write a zero
-	if ( version >= VER_10_0_1_0) {
-		WriteUShort( 0, out );
-	}
-
-	//GetAttr("Unknown Link")->Write( out, version );
-}
-
-void AShapeData::SetVertexCount(int n) {
-	if ( n > 65535 || n < 0 )
-		throw runtime_error("Invalid Vertex Count: must be between 0 and 65535.");
-
-	if ( n == 0 ) {
-		vertices.clear();
-		normals.clear();
-		colors.clear();
-		for (uint i = 0; i < uv_sets.size(); ++i) {
-			uv_sets[i].clear();
-		}
-		return;
-	}
-	
-	//n != 0
-	vertices.resize(n);
-
-	if ( normals.size() != 0 ) { 
-		normals.resize(n);
-	}
-	if ( colors.size() != 0 ) {
-		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() && in.size() != 0 )
-		throw runtime_error("Vector size must equal Vertex Count or zero.  Call SetVertexCount() to resize.");
-	vertices = in;
-}
-
-void AShapeData::SetNormals( const vector<Vector3> & in ) {
-	if (in.size() != vertices.size() && in.size() != 0 )
-		throw runtime_error("Vector size must equal Vertex Count or zero.  Call SetVertexCount() to resize.");
-	normals = in;
-}
-
-void AShapeData::SetColors( const vector<Color4> & in ) {
-	if (in.size() != vertices.size() && in.size() != 0 )
-		throw runtime_error("Vector size must equal Vertex Count or zero.  Call SetVertexCount() to resize.");
-	colors = in;
-}
-
-void AShapeData::SetUVSet( int index, const vector<TexCoord> & 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( istream& file, unsigned int version ) {
-	AShapeData::Read( file, version );
-
-	//numParticles exists up to version 4.0.0.2
-	if ( version <= VER_4_0_0_2 ) {
-		//This is just the number of vertices over again, so discard the data
-		ReadUShort( file );
-	}
-
-	//Size exists up to version 10.0.1.0
-	if ( version <= VER_10_0_1_0 ) {
-		NifStream( size, file );
-	}
-
-	//numActive exists up to version 4.0.0.2
-	if ( version <= VER_4_0_0_2 ) {
-		NifStream( numActive, file );
-	}
-
-	//Unknown short exits from version 4.1.0.12 to 10.0.1.0
-	if ( version >= VER_4_1_0_12 && version <= VER_10_0_1_0 ) {
-		NifStream( unkShort, file );
-	}
-
-	hasSizes = ReadBool( file, version );
-
-	if ( hasSizes ) {
-		sizes.resize( vertices.size() );
-		NifStream( sizes, file );
-	}
-}
-
-void AParticlesData::Write( ostream& file, unsigned int version ) const {
-	AShapeData::Write( file, version );
-
-	//numParticles exists up to version 4.0.0.2
-	if ( version <= VER_4_0_0_2 ) {
-		//This is just the number of vertices over again
-		NifStream( uint(vertices.size()), file );
-	}
-
-	//Size exists up to version 10.0.1.0
-	if ( version <= VER_10_1_0_0 ) {
-		NifStream( size, file );
-	}
-
-	//numActive exists up to version 4.0.0.2
-	if ( version <= VER_4_0_0_2 ) {
-		NifStream( numActive, file );
-	}
-
-	//Unknown short exits from version 4.1.0.12 to 10.0.1.0
-	if ( version >= VER_4_1_0_12 && version <= VER_10_0_1_0 ) {
-		NifStream( unkShort, file );
-	}
-
-	WriteBool( hasSizes, file, version );
-
-	if ( hasSizes ) {
-		NifStream( sizes, file );
-	}
-}
-
-string AParticlesData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AShapeData::asString();
-
-	out << "Num Particles:  " << uint(vertices.size()) << endl
-		<< "Size:  " << size << endl
-		<< "Num Active:  " << numActive << endl
-		<< "Unknown Short:  " << unkShort << 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();
-}
-
-/***********************************************************
- * APSysData methods
- **********************************************************/
-
-void APSysData::Read( istream& file, unsigned int version ) {
-	AShapeData::Read( file, version );
-
-	bool hasUnkFlts = ReadBool( file, version );
-	if ( hasUnkFlts ) {
-		unkFloats1.resize( vertices.size() );
-		for ( uint i = 0; i < unkFloats1.size(); ++i ) {
-			NifStream( unkFloats1[i], file );
-		}
-	}
-
-	NifStream( unkShort, file );
-
-	hasUnkFlts = ReadBool( file, version );
-	if ( hasUnkFlts ) {
-		unkFloats2.resize( vertices.size() );
-		for ( uint i = 0; i < unkFloats2.size(); ++i ) {
-			NifStream( unkFloats2[i], file );
-		}
-	}
-
-	NifStream( unkByte, file );
-}
-
-void APSysData::Write( ostream& file, unsigned int version ) const {
-	AShapeData::Write( file, version );
-
-	WriteBool( unkFloats1.size() > 0, file, version );
-	for ( uint i = 0; i < unkFloats1.size(); ++i ) {
-		NifStream( unkFloats1[i], file );
-	}
-
-	NifStream( unkShort, file );
-
-	WriteBool( unkFloats2.size() > 0, file, version );
-	for ( uint i = 0; i < unkFloats2.size(); ++i ) {
-		NifStream( unkFloats2[i], file );
-	}
-
-
-	NifStream( unkByte, file );
-}
-
-string APSysData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AShapeData::asString();
-
-	out << "Unknown Floats 1:  " << uint(unkFloats1.size()) << endl;
-	if (verbose) {
-		for ( uint i = 0; i < unkFloats1.size(); ++i ) {
-			out << i + 1 << ":  " << unkFloats1[i] << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>";
-	}
-
-	out << "Unknown Short:  " << unkShort << endl
-		<< "Unknown Floats 2:  " << uint(unkFloats2.size()) << endl;
-	if (verbose) {
-		for ( uint i = 0; i < unkFloats2.size(); ++i ) {
-			out << i + 1 << ":  " << unkFloats2[i] << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>";
-	}
-
-	out << "Unknown Byte:  " << int(unkByte) << endl;
-
-	return out.str();
-}
-
-/***********************************************************
- * NiMeshPSysData methods
- **********************************************************/
-
-void NiMeshPSysData::Read( istream& file, unsigned int version ) {
-	APSysData::Read( file, version );
-
-	unkFloats.resize( vertices.size() * 14 );
-	NifStream( unkFloats, file );
-
-	NifStream( unkInt, file );
-
-	//GetAttr("Modifier")->Read( file, version );
-
-	// From version 10.2.0.0 there are several new entries here
-	if ( version >= VER_10_2_0_0 ) {
-		NifStream( unkByte, file );
-
-		//GetAttr("Unknown Link Group")->Read( file, version );
-		//GetAttr("Unknown Link 2")->Read( file, version );
-	}
-}
-
-void NiMeshPSysData::Write( ostream& file, unsigned int version ) const {
-	APSysData::Write( file, version );
-
-	NifStream( unkFloats, file );
-
-	NifStream( unkInt, file );
-
-	//GetAttr("Modifier")->Write( file, version );
-
-	// From version 10.2.0.0 there are several new entries here
-	if ( version >= VER_10_2_0_0 ) {
-		NifStream( unkByte, file );
-
-		//GetAttr("Unknown Link Group")->Write( file, version );
-		//GetAttr("Unknown Link 2")->Write( file, version );
-	}
-}
-
-string NiMeshPSysData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AShapeData::asString();
-
-	out << "Unknown Floats:  " << uint(unkFloats.size()) << endl;
-
-	if (verbose) {
-		for (uint i = 0; i < unkFloats.size(); ++i ) {
-			out << "   " << i + 1 << unkFloats[i] << endl;
-		}
-	} else {
-		out << "   <<<Data Not Shown>>>";
-	}
-
-	out << "Unknown Int:  " << unkInt << endl
-		<< "Modifier:  " << GetAttr("Modifier")->asString() << endl
-		<< "Unknown Byte:  " << unkByte << endl
-		<< "Unknown Link Group:  " << GetAttr("Unknown Link Group")->asString() << endl
-		<< "Unknown Link 2:  " << GetAttr("Unknown Link 2")->asString() << endl;
-
-	return out.str();
-}
-
-/***********************************************************
- * NiPSysData methods
- **********************************************************/
-
-void NiPSysData::Read( istream& file, unsigned int version ) {
-	APSysData::Read( file, version );
-
-	//before version 20.0.0.4 there are unknown floats here
-	if ( version < VER_20_0_0_4	) {
-		unkFloats.resize( vertices.size() * 10 );
-		NifStream( unkFloats, file );
-	} else {
-		//From version 20.0.0.4 on there are a lot of unknown bytes
-		unkBool1 = ReadBool( file, version );
-		if ( unkBool1 ) {
-			//32 bytes per vertex
-			unkBytes1.resize( vertices.size() * 32 );
-		} else {
-			// 28 bytes per vertex
-			unkBytes1.resize( vertices.size() * 28 );
-		}
-		NifStream( unkBytes1, file );
-
-		NifStream( unkByte, file );
-
-		unkBool2 = ReadBool( file, version );
-		if ( unkBool2 ) {
-			unkBytes2.resize( vertices.size() * 4 );
-			NifStream( unkBytes2, file );
-		}
-	}
-
-	NifStream( unkInt, file );
-}
-
-void NiPSysData::Write( ostream& file, unsigned int version ) const {
-	APSysData::Write( file, version );
-
-	//before version 20.0.0.4 there are unknown floats here
-	if ( version < VER_20_0_0_4	) {
-		NifStream( unkFloats, file );
-	} else {
-		//From version 20.0.0.4 on there are a lot of unknown bytes
-		WriteBool( unkBool1, file, version );
-		
-		NifStream( unkBytes1, file );
-		
-		NifStream( unkByte, file );
-
-		WriteBool( unkBool2, file, version);
-		if ( unkBool2 ) {
-			NifStream( unkBytes2, file );
-		}
-	}
-
-	NifStream( unkInt, file );
-}
-
-string NiPSysData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AShapeData::asString();
-
-	out << "Unknown Floats:  " << uint(unkFloats.size()) << endl;
-
-	if (verbose) {
-		for (uint i = 0; i < unkFloats.size(); ++i ) {
-			out << "   " << i + 1 << unkFloats[i] << endl;
-		}
-	} else {
-		out << "   <<<Data Not Shown>>>";
-	}
-
-	out << "Unknown Bool 1:  " << unkBool1 << endl
-		<< "Unknown Bytes 1:  " << uint(unkBytes1.size()) << endl;
-
-	if (verbose) {
-		out << HexString( &unkBytes1[0], uint(unkBytes1.size()) );
-	} else {
-		out << "   <<<Data Not Shown>>>";
-	}
-
-	out << "Unknown Byte:  " << unkByte << endl
-		<< "Unknown Bool 2:  " << unkBool2 << endl
-		<< "Unknown Bytes 2:  " << uint(unkBytes2.size()) << endl;
-
-
-	if (verbose) {
-		out << HexString( &unkBytes2[0], uint(unkBytes2.size()) );
-	} else {
-		out << "   <<<Data Not Shown>>>";
-	}
-
-	out << "Unknown Int:  " << unkInt << endl;
-
-	return out.str();
-}
-
-/***********************************************************
- * ARotatingParticlesData methods
- **********************************************************/
-
-void ARotatingParticlesData::Read( istream& file, unsigned int version ) {
-	AParticlesData::Read( file, version );
-
-	//After version 10.1.0.0 there are several new entries
-	if ( version >= VER_10_1_0_0 ) {
-		NifStream( numActiveRot, file );
-
-		bool hasUnkFloats = ReadBool( file, version );
-
-		if ( hasUnkFloats ) {
-			unkFloats.resize( vertices.size() );
-			NifStream( unkFloats, file );
-		}
-	}
-
-	bool hasRotations = ReadBool( file, version );
-
-	if ( hasRotations ) {
-		rotations.resize( vertices.size() );
-		NifStream( rotations, file );
-	}
-}
-
-void ARotatingParticlesData::Write( ostream& file, unsigned int version ) const {
-	AParticlesData::Write( file, version );
-
-	//After version 10.1.0.0 there are several new entries
-	if ( version >= VER_10_1_0_0 ) {
-		NifStream( numActiveRot, file );
-
-		WriteBool( (unkFloats.size() > 0), file, version );
-
-		NifStream( unkFloats, file );
-	}
-
-	WriteBool( (rotations.size() > 0), file, version );
-	NifStream( rotations, file );
-}
-
-string ARotatingParticlesData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AParticlesData::asString() << endl
-		<< "Num Active Rot:  " << numActiveRot << endl
-		<< "Unknown Floats:  ";
-	
-	if ( unkFloats.size() > 0 ) {
-		if (verbose) {
-			out << endl;
-			for ( uint i = 0; i < unkFloats.size(); ++i) {
-				out << i << ":  " << unkFloats[i] << endl;
-			}
-		} else {
-			out << endl << "<<Data Not Shown>>" << endl;
-		}
-	} else {
-		out << "None" << endl;
-	}
-
-	out << "Rotations:  ";
-	
-	if ( rotations.size() > 0 ) {
-		if (verbose) {
-			out << endl;
-			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>>" << endl;
-		}
-	} else {
-		out << "None" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiParticleMeshesData methods
- **********************************************************/
-
-void NiParticleMeshesData::Read( istream& in, unsigned int version ) {
-	ARotatingParticlesData::Read( in, version );
-
-	//GetAttr("Unknown Link 2")->Read( in, version );
-}
-
-void NiParticleMeshesData::Write( ostream& out, unsigned int version ) const {
-	ARotatingParticlesData::Write( out, version );
-
-	//GetAttr("Unknown Link 2")->Write( out, version );
-}
-
-string NiParticleMeshesData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << ARotatingParticlesData::asString()
-		<< "Unknown Link 2:  " << GetAttr("Unknown Link 2")->asString() << endl;
-
-	return out.str();
-}
-
-/***********************************************************
- * NiTriShapeData methods
- **********************************************************/
-
-/**
- * NiTriShapeData::Read - Assumes block name has already been read from in
- */
-void NiTriShapeData::Read( istream& in, unsigned int version ){
-	AShapeData::Read( in, version );
-
-	ushort numTriangles = ReadUShort( in );
-	ReadUInt( in ); // numTriangles * 3, we can throw this away
-	
-	//From version 10.1.0.0 on there is a bool to check whether or not there are any triangles
-	//We already know the answer to this from the numTriangles count, don't we?
-	//Jus in case, set numTriangles to zero if this is false.
-	if ( version >= VER_10_1_0_0 ) {
-		if ( ReadBool( in, version ) == false ) {
-			numTriangles = 0;
-		}
-	}
-
-	triangles.resize( numTriangles );
-	for ( uint i = 0; i < triangles.size(); ++i ){
-		triangles[i].v1 = ReadUShort( in );
-		triangles[i].v2 = ReadUShort( in );
-		triangles[i].v3 = ReadUShort( in );
-	}
-
-	ushort matchGroupCount = ReadUShort( in );
-	match_group_mode = ( matchGroupCount != 0 );  // Only record whether or not file prefers to have match data generated
-
-	ushort 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() const {
-	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( ostream& out, unsigned int version ) const {
-
-	AShapeData::Write( out, version );
-
-	WriteUShort( ushort(triangles.size()), out );
-	WriteUInt( ushort(triangles.size()) * 3, out );
-	
-	//From version 10.1.0.0 on there is a bool to check whether or not there are any triangles
-	if ( version >= VER_10_1_0_0 ) {
-		WriteBool( triangles.size() > 0, out, version );
-	}
-
-	for ( uint i = 0; i < triangles.size(); ++i ){
-		WriteUShort( triangles[i].v1, out );
-		WriteUShort( triangles[i].v2, out );
-		WriteUShort( triangles[i].v3, out );
-	}
-
-	if ( match_group_mode ) {
-		WriteUShort( ushort(vertices.size()), out ); //Match Group Count = Vertex Count
-
-		vector<ushort> matches;
-		for ( uint i = 0; i < vertices.size(); ++i ){
-			// Find all vertices that match this one.
-			matches.clear();
-			for (ushort j = 0; j < vertices.size(); ++j) {
-				if ( i != j && vertices[i].x == vertices[j].x && vertices[i].y == vertices[j].y && vertices[i].z == vertices[j].z ) {
-					matches.push_back(j);
-				}
-			}
-			//Match Count
-			WriteUShort( ushort(matches.size()) , out );
-
-			//Output Vertex indicies
-			for (ushort j = 0; j < matches.size(); ++j) {
-				WriteUShort( matches[j], out );
-			}
-		}	
-	} else {
-		WriteUShort( 0, out ); //Match Group Count = 0
-	}
-}
-
-//--Setters--//
-
-void NiTriShapeData::SetTriangles( const vector<Triangle> & in ) {
-	if ( in.size() > 65535 || in.size() < 0 ) {
-		throw runtime_error("Invalid Triangle Count: must be between 0 and 65535.");
-	}
-
-	triangles = in;
-}
-
-/***********************************************************
- * NiTriStripsData methods
- **********************************************************/
-
-void NiTriStripsData::Read( istream& 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
-	ushort numStrips = ReadUShort( in );
-	strips.resize( numStrips );
-	for ( uint i = 0; i < strips.size(); ++i ) {
-		ushort stripSize = ReadUShort( in );
-		strips[i].resize( stripSize );
-	}
-
-	//From version 10.1.0.0 on there is a bool to check whether or not there are any points
-	//We already know the answer to this from the counts above, don't we?
-	//Just in case, clear all strips if this is false.
-	if ( version >= VER_10_1_0_0 ) {
-		if ( ReadBool( in, version ) == false ) {
-			strips.resize(0);
-		}
-	}
-
-	//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 NiTriStripsData::Write( ostream& out, unsigned int version ) const {
-
-	AShapeData::Write( out, version );
-
-	//Write number of triangles and strips
-	ushort numTriangles = GetTriangleCount();
-	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 );
-	}
-
-	//From version 10.1.0.0 on there is a bool to check whether or not there are any points
-	if ( version >= VER_10_1_0_0 ) {
-		WriteBool( numTriangles > 0, out, version );
-	}
-
-	//Write points
-	for ( uint i = 0; i < strips.size(); ++i ) {
-		for ( uint j = 0; j < strips[i].size(); ++j ) {
-			WriteUShort( strips[i][j], out );
-		}
-	}
-}
-
-string NiTriStripsData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AShapeData::asString();
-
-	out << "Triangles:  " << GetTriangleCount() << 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();
-}
-
-short NiTriStripsData::GetStripCount() const {
-	return short(strips.size());
-}
-
-void NiTriStripsData::SetStripCount(int n) {
-	strips.resize( n );
-}
-
-//Getters
-vector<short> NiTriStripsData::GetStrip( int index ) const {
-	return strips[index];
-}
-
-vector<Triangle> NiTriStripsData::GetTriangles() const {
-
-	//Create a vector to hold the triangles
-	vector<Triangle> triangles( GetTriangleCount() );
-	int n = 0; // Current triangle
-
-	//Cycle through all strips
-	vector< vector<short> >::const_iterator it;
-	for (it = strips.begin(); it != strips.end(); ++it ) {
-		//The first three values in the strip are the first triangle
-		triangles[n].Set( (*it)[0], (*it)[1], (*it)[2] );
-
-		//Move to the next triangle
-		++n;
-
-		//The remaining triangles use the previous two indices as their first two indices.
-		for( uint i = 3; i < it->size(); ++i ) {
-			//Odd numbered triangles need to be reversed to keep the vertices in counter-clockwise order
-			if ( i % 2 == 0 ) {
-				//cout << (*it)[i - 2] << ", " << (*it)[i - 1] << ", " << (*it)[i] << endl;
-				triangles[n].Set( (*it)[i - 2], (*it)[i - 1], (*it)[i] );
-			} else {
-				//cout << (*it)[i] << ", " << (*it)[i - 1] << ", " << (*it)[i - 2] << endl;
-				triangles[n].Set( (*it)[i], (*it)[i - 1], (*it)[i - 2] );
-			}
-
-			//Move to the next triangle
-			++n;
-		}
-	}
-
-	return triangles;
-}
-
-//Setter
-void NiTriStripsData::SetStrip( int index, const vector<short> & in ) {
-	strips[index] = in;
-}
-
-short NiTriStripsData::GetTriangleCount() const {
-
-	//Calculate number of triangles
-	//Sum of length of each strip - 2
-	short numTriangles = 0;
-	for ( uint i = 0; i < strips.size(); ++i ) {
-		numTriangles += short(strips[i].size() - 2);
-	}
-
-	return numTriangles;
-}
-
-/***********************************************************
- * NiBSplineData methods
- **********************************************************/
-
-void NiBSplineData::Read( istream& file, unsigned int version ){
-	NifStream( unkInt, file );
-	
-	uint count = ReadUInt( file );
-	unkShorts.resize( count );
-	NifStream( unkShorts, file );
-}
-
-void NiBSplineData::Write( ostream& file, unsigned int version ) const {
-
-	NifStream( unkInt, file );
-
-	WriteUInt( uint(unkShorts.size()), file );
-
-	NifStream( unkShorts, file );
-}
-
-string NiBSplineData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Unknown Int:  " << unkInt << endl
-		<< "Unknown Shorts?:  " << uint(unkShorts.size()) << endl;
-
-	if (verbose) {
-		for ( uint i = 0; i < unkShorts.size(); ++i ) {
-			out << "   " << i + 1 << ":  " << unkShorts[i] << endl;
-		}
-	} else {
-		out << "   <<<Data Not Shown>>>" << endl;
-	}
-	
-
-	return out.str();
-}
-
-/***********************************************************
- * NiCollisionData methods
- **********************************************************/
-
-void NiCollisionData::Read( istream& in, unsigned int version ){
-	//Read parent node but don't store it
-	ReadUInt( in );
-
-	unknownInt1 = ReadUInt( in );
-	unknownByte = ReadByte( in );
-	collisionType = ReadUInt( in );
-
-	if ( collisionType == 0 ) {
-		unknownInt2 = ReadUInt( in );
-		ReadFVector3( unknown3Floats, in );
-	} 
-	else if ( collisionType == 1 ) {
-		for (int i = 0; i < 15; ++i ) {
-			unknown15Floats[i] = ReadFloat( in );
-		}
-	} 
-	else if ( collisionType == 2) {
-		for ( int i = 0; i < 8; ++i ) {
-			unknown8Floats[i] = ReadFloat( in );
-		}
-	} 
-}
-
-void NiCollisionData::Write( ostream& out, unsigned int version ) const {
-
-	//Write Parent node number
-	WriteUInt( GetParent().get_index(), out );
-
-	WriteUInt( unknownInt1, out );
-	WriteByte( unknownByte, out );
-	WriteUInt( collisionType, out );
-
-	if ( collisionType == 0 ) {
-		WriteUInt( unknownInt2, out );
-		WriteFVector3( unknown3Floats, out );
-	} 
-	else if ( collisionType == 1 ) {
-		for (int i = 0; i < 15; ++i ) {
-			WriteFloat( unknown15Floats[i], out );
-		}
-	} 
-	else if ( collisionType == 2) {
-		for ( int i = 0; i < 8; ++i ) {
-			WriteFloat( unknown8Floats[i], out );
-		}
-	} 
-}
-
-string NiCollisionData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	//Parent is already written, so don't do anything with it
-
-	out << "Unknown Int 1:  " << unknownInt1 << endl
-		<< "Unknown Byte:  " << int(unknownByte) << endl
-		<< "Collision Type:  " << collisionType << endl
-		<< "Collision Data:" << endl;
-
-	if ( collisionType == 0 ) {
-		out << "   Unknown Int 2:  " << unknownInt2 << endl
-			<< "   Unknown 3 Floats:   " << unknown3Floats << endl;
-	} 
-	else if ( collisionType == 1 ) {
-		out << "   Unknown 15 Floats:" << endl;
-		for (int i = 0; i < 15; ++i ) {
-			out << "      " << i + 1 << ":  " << unknown15Floats[i] << endl;
-		}
-	} 
-	else if ( collisionType == 2) {
-		out << "   Unknown 8 Floats:" << endl;
-		for ( int i = 0; i < 8; ++i ) {
-			out << "      " << i + 1 << ":  " << unknown8Floats[i] << endl;
-		}
-	} 
-
-	return out.str();
-}
-
-/***********************************************************
- * NiSkinData methods
- **********************************************************/
-
-void NiSkinData::Read( istream& in, unsigned int version ) {
-	
-	for (int c = 0; c < 3; ++c) {
-		for (int r = 0; r < 3; ++r) {
-			rotation[r][c] = ReadFloat( in );
-		}
-	}
-	ReadFVector3( translation, in );
-	scale = ReadFloat( in );
-	int boneCount = ReadUInt( in );
-	//GetAttr("Skin Partition")->Read( in, version );
-	//unknownByte exists from version 4.2.1.0 on
-	if ( version >= VER_4_2_1_0 ) {
-		unknownByte = ReadByte( in );
-	}
-	bones.resize(boneCount);
-	for( int i = 0; i < boneCount; i++ ) {
-		for (int c = 0; c < 3; ++c) {
-			for (int r = 0; r < 3; ++r) {
-				bones[i].rotation[r][c] = ReadFloat( in );
-			}
-		}
-		ReadFVector3( bones[i].translation, in );
-		bones[i].scale = ReadFloat( in );
-		ReadFVector4( bones[i].unknown4Floats, in );
-		short numWeights = ReadUShort( in );
-		bones[i].weights.clear();
-		for ( int j = 0; j < numWeights; ++j ){
-			short vertexNum = ReadUShort( in );
-			float vertexWeight = ReadFloat( in );
-			bones[i].weights[vertexNum] = vertexWeight;
-		}
-	}
-}
-
-void NiSkinData::Write( ostream& out, unsigned int version ) const {
-	//Calculate offset matrices prior to writing data
-
-	Matrix33 rot;
-	fVector3 tr;
-	float sc;
-	CalculateOverallOffset(rot, tr, sc);
-
-	for (int c = 0; c < 3; ++c) {
-		for (int r = 0; r < 3; ++r) {
-			WriteFloat( rot[r][c], out );
-		}
-	}
-	WriteFVector3( tr, out );
-	WriteFloat( sc, out );
-	WriteUInt(short(bone_map.size()), out);
-	//GetAttr("Skin Partition")->Write( out, version );
-	//unknownByte exists from version 4.2.1.0 on
-	if ( version >= VER_4_2_1_0) {
-		WriteByte( unknownByte, out );
-	}
-
-	//Get parent node for bone calculations
-	INode const * const par_node = this->GetNodeParent();
-	Bone bone; // temporary value
-
-	map<IBlock *, Bone >::const_iterator it;
-	for( it = bone_map.begin(); it != bone_map.end(); ++it ) {
-		//Calculae offset for this bone (data is not stored)
-		CalculateBoneOffset( par_node, it->first, bone );	
-
-		for (int c = 0; c < 3; ++c) {
-			for (int r = 0; r < 3; ++r) {
-				WriteFloat( bone.rotation[r][c], out );
-			}
-		}
-		WriteFVector3( bone.translation, out );
-		WriteFloat( bone.scale, out );
-
-		//Write 4 zeros for unknown floats
-		WriteFloat( 0.0f, out );
-		WriteFloat( 0.0f, out );
-		WriteFloat( 0.0f, out );
-		WriteFloat( 0.0f, out );
-
-		//WriteFVector4( bone.unknown4Floats, out );
-
-		WriteUShort( short(it->second.weights.size() ), out );
-		
-		map<int, float>::const_iterator it2;
-		for ( it2 = it->second.weights.begin(); it2 != it->second.weights.end(); ++it2 ){
-			WriteUShort( it2->first, out );
-			WriteFloat( it2->second, out );
-		}
-	}
-}
-
-//void GetBuiltUpTransform(blk_ref block, Matrix & m/*, blk_ref stop*/) {
-//	Matrix33 temp;
-//	float3 t2;
-//
-//	// Do multiplication
-//	attr_ref rot_attr = block["Rotation"];
-//	attr_ref trn_attr = block["Translation"];
-//	attr_ref scl_attr = block["Scale"];
-//
-//	if ( rot_attr.is_null() == false && trn_attr.is_null() == false && scl_attr.is_null() == false ) {
-//		rot_attr->asMatrix33(temp);
-//		trn_attr->asFloat3(t2);
-//		float scale = scl_attr->asFloat();
-//
-//		Matrix tr = Matrix( temp[0][0], temp[0][1], temp[0][2], 0.0f,
-//							temp[1][0], temp[1][1], temp[1][2], 0.0f,
-//							temp[2][0], temp[2][1], temp[2][2], 0.0f,
-//							t2[0], t2[1], t2[2], 1.0f);
-//
-//		Matrix s = Matrix( scale, 0.0f, 0.0f, 0.0f,
-//							0.0f, scale, 0.0f, 0.0f,
-//							0.0f, 0.0f, scale, 0.0f,
-//							0.0f, 0.0f, 0.0f, 1.0f );
-//
-//		m = m * (tr * s);
-//	}
-//	else {
-//		throw runtime_error("GetBuiltUpTranslations attempted to access a block without rotation, translation, and scale");
-//	}
-//
-//	//Check if there are any parents
-//	blk_ref par = block->GetParent();
-//	if ( par.is_null() == false ) {
-//		//There is a parent, re-call this function on it
-//		GetBuiltUpTransform( par, m/*, stop*/ );
-//	}
-//}
-
-string NiSkinData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	//If there is no parent, do nothing
-	if ( this->GetParent().is_null() == true ) {
-		out << "No parent - data cannot be calculated." << endl
-			<< ABlock::asString() << endl;
-		return out.str();
-	}
-	
-	Matrix33 rot;
-	fVector3 tr;
-	float sc;
-	try {
-		CalculateOverallOffset(rot, tr, sc);
-	} catch ( runtime_error & e ) {
-		out << e.what() << endl;
-		return out.str();
-	}
-
-	out << "Rotate:" << endl
-		<< "   |" << setw(6) << rot[0][0] << "," << setw(6) << rot[0][1] << "," << setw(6) << rot[0][2] << " |" << endl
-		<< "   |" << setw(6) << rot[1][0] << "," << setw(6) << rot[1][1] << "," << setw(6) << rot[1][2] << " |" << endl
-		<< "   |" << setw(6) << rot[2][0] << "," << setw(6) << rot[2][1] << "," << setw(6) << rot[2][2] << " |" << endl
-		<< "Translate:  " << tr << endl
-		<< "Scale:  " << sc << endl
-		<< "Bone Count:  " << uint(bone_map.size()) << endl
-		<< "Skin Partition:  " << GetAttr("Skin Partition")->asLink() << endl
-		<< "Unknown Byte:  " << int(unknownByte) << endl
-		<< "Bones:" << endl;
-
-	//Get parent node for bone calculations
-	INode const * const par_node = this->GetNodeParent();
-	Bone bone; // temporary value
-
-	map<IBlock *, Bone >::const_iterator it;
-	int num = 0;
-	for( it = bone_map.begin(); it != bone_map.end(); ++it ) {
-		//Calculae offset for this bone (data is not stored)
-		CalculateBoneOffset( par_node, it->first, bone );
-
-		num++;
-		out << "Bone " << num << ":" << endl
-			<< "   Block:  " << it->first->GetBlockNum() << endl //blk_ref(it->first) << endl
-			<< "   Bone Offset Transforms:" << endl
-			<< "      Rotation:" << endl
-			<< "         |" << setw(6) << bone.rotation[0][0] << "," << setw(6) << bone.rotation[0][1] << "," << setw(6) << bone.rotation[0][2] << " |" << endl
-			<< "         |" << setw(6) << bone.rotation[1][0] << "," << setw(6) << bone.rotation[1][1] << "," << setw(6) << bone.rotation[1][2] << " |" << endl
-			<< "         |" << setw(6) << bone.rotation[2][0] << "," << setw(6) << bone.rotation[2][1] << "," << setw(6) << bone.rotation[2][2] << " |" << endl
-			<< "      Translate:  " << bone.translation << endl
-			<< "      Scale:  " << bone.scale << endl;
-
-		float q[4] = {  bone.unknown4Floats[0],
-					    bone.unknown4Floats[1],
-					    bone.unknown4Floats[2],
-					    bone.unknown4Floats[3] };
-
-		out << "Unknown 4 Floats:  " << setw(6) << q[0] << "," << setw(6) << q[1] << "," << setw(6) << q[2] << "," << setw(6) << q[3] << endl;
-
-		out << "   Weights:  " << uint( it->second.weights.size()) << endl;
-
-		if (verbose) {
-			map<int, float>::const_iterator it2;
-			for ( it2 = it->second.weights.begin(); it2 != it->second.weights.end(); ++it2 ){
-				out << "   Vertex: " << it2->first << "\tWeight: " << it2->second << endl;
-			}
-		} else {
-			out << "      <<Data Not Shown>>" << endl;
-		}
-	}
-	
-	//out << setprecision(1);
-
-	return out.str();
-}
-
-void NiSkinData::SetBones( const vector<blk_ref> & bone_blocks ) {
-	//--Move bones from temproary vector to map, sorted by blk_ref--//
-	for (uint i = 0; i < bones.size(); ++i) {
-		//IBlockInternal * blk_int = (IBlockInternal*)bone_blocks[i]->QueryInterface(BlockInternal);
-
-		//move the data
-		bone_map.insert( pair<IBlock *, Bone>(bone_blocks[i].get_block(), bones[i]) );
-
-		//Increment reference at bone node site
-		((ABlock*)bone_blocks[i].get_block())->IncCrossRef(this);
-	}
-
-	//Clear temporary vector data
-	bones.clear();
-}
-
-void NiSkinData::StraightenSkeleton() {
-	////Get bone blocks from parent SkinInstance
-	//vector<blk_ref> bone_blks;
-	//blk_ref par = GetParent();
-	//blk_link l;
-	//attr_ref bone_attr = par->GetAttr("Bones");
-
-	//if ( bone_attr.is_null() == false ) {
-	//	for (int i = 0; i < par->LinkCount(); ++i) {
-	//		l = par->GetLink(i);
-	//		if ( l.attr == bone_attr ) {
-	//			bone_blks.push_back(l.block);
-	//		}
-	//	}
-	//}
-
-	//Loop through all bones
-	map<IBlock *, Bone>::iterator it;
-	for ( it = bone_map.begin(); it != bone_map.end(); ++it ) {
-		//Friendlier name for current bone
-		Bone & bone = it->second;
-
-		//Get current offset Matrix33 for this bone
-
-		Matrix44 parent_offset(
-			bone.rotation[0][0], bone.rotation[0][1], bone.rotation[0][2], 0.0f,
-			bone.rotation[1][0], bone.rotation[1][1], bone.rotation[1][2], 0.0f,
-			bone.rotation[2][0], bone.rotation[2][1], bone.rotation[2][2], 0.0f,
-			bone.translation[0], bone.translation[1], bone.translation[2], 1.0f
-		); 
-		//Loop through all bones again, checking for any that have this bone as a parent
-		map<IBlock *, Bone>::iterator it2;
-		for ( it2 = bone_map.begin(); it2 != bone_map.end(); ++it2 ) {
-			if ( it2->first->GetParent() == it->first ) {
-				//Block 2 has block 1 as a parent
-
-				Bone & bone2 = it2->second;
-
-				//Get child offset Matrix33
-				Matrix44 child_offset(
-					bone2.rotation[0][0], bone2.rotation[0][1], bone2.rotation[0][2], 0.0f,
-					bone2.rotation[1][0], bone2.rotation[1][1], bone2.rotation[1][2], 0.0f,
-					bone2.rotation[2][0], bone2.rotation[2][1], bone2.rotation[2][2], 0.0f,
-					bone2.translation[0], bone2.translation[1], bone2.translation[2], 1.0f
-				);
-
-				//Do calculation to get correct bone postion in relation to parent
-				Matrix44 inverse_co = child_offset.Inverse();
-				Matrix44 child_pos = inverse_co * parent_offset;
-
-				//Store result in block's Bind Position Matrix
-				INode * node = (INode*)it2->first->QueryInterface(ID_NODE);
-				if (node != NULL) {
-					node->SetWorldBindPos(child_pos);
-				}
-
-    //            //Store result in child block
-				//Matrix33 rotate = { 
-				//	child_pos[0][0], child_pos[0][1], child_pos[0][2],
-				//	child_pos[1][0], child_pos[1][1], child_pos[1][2],
-				//	child_pos[2][0], child_pos[2][1], child_pos[2][2]
-				//};
-				//it2->first->GetAttr("Rotation")->Set( rotate );
-				//it2->first->GetAttr("Translation")->Set( child_pos[3][0], child_pos[3][1], child_pos[3][2] );
-			}
-		}
-	}
-}
-
-void NiSkinData::RepositionTriShape( blk_ref & tri_shape ) {
-	//Get block we're going to move
-	//blk_ref tri_shape = GetParent()->GetParent();
-
-	//There must be at least one bone to do anything
-	if ( bone_map.size() > 0 ) {
-		//--Start Position--//
-		//Matrix start_mat = IdentityMatrix();
-		//GetBuiltUpTransform( tri_shape->GetParent(), start_mat );
-
-		//--End Position--//
-
-		//Get first bone
-		IBlock * bone_blk = bone_map.begin()->first;
-		Bone & bone = bone_map.begin()->second;
-
-
-		//blk_ref skin_inst = GetParent();
-		//blk_link l;
-		//attr_ref bone_attr = skin_inst->GetAttr("Bones");
-		//
-		//blk_ref bone_blk;
-		//if ( bone_attr.is_null() == false ) {
-		//	for (int i = 0; i < skin_inst->LinkCount(); ++i) {
-		//		l = skin_inst->GetLink(i);
-		//		if ( l.attr == bone_attr ) {
-		//			//Found the first bone, record it and break
-		//			bone_blk = l.block;
-		//			break;
-		//		}
-		//	}
-		//}
-		//else {
-		//	//Couldn't get the bone attr
-		//	cout << "Couldn't get the bone attr" << endl;
-		//	return;
-		//}
-
-		Matrix44 offset_mat(
-			bone.rotation[0][0], bone.rotation[0][1], bone.rotation[0][2], 0.0f,
-			bone.rotation[1][0], bone.rotation[1][1], bone.rotation[1][2], 0.0f,
-			bone.rotation[2][0], bone.rotation[2][1], bone.rotation[2][2], 0.0f,
-			bone.translation[0], bone.translation[1], bone.translation[2], 1.0f
-		);
-			
-		//Get built up rotations to the root of the skeleton from this bone
-		INode * bone_node = (INode*)bone_blk->QueryInterface(ID_NODE);
-		if (bone_node == NULL)
-			throw runtime_error("Failed to get Node interface.");
-
-		Matrix44 bone_mat = bone_node->GetWorldBindPos();
-
-		Matrix44 result_mat = offset_mat * bone_mat;
-
-		//GetBuiltUpTransform( bone_blk, end_mat );
-
-		//--Set TriShape Bind Position to Result--//
-		INode * shape_node = (INode*)tri_shape->QueryInterface(ID_NODE);
-		if (shape_node == NULL)
-			throw runtime_error("Failed to get Node interface.");
-
-		shape_node->SetWorldBindPos( result_mat );
-
-		
-
-		//Matrix res_mat = end_mat;// * start_mat.inverse();
-
-		//tri_shape->GetAttr("Translation")->Set( res_mat(3,0), res_mat(3,1), res_mat(3,2) );
-
-		//Matrix33 rotation = { res_mat(0,0), res_mat(0,1), res_mat(0,2),
-		//					res_mat(1,0), res_mat(1,1), res_mat(1,2),
-		//					res_mat(2,0), res_mat(2,1), res_mat(2,2) };
-		//tri_shape->GetAttr("Rotation")->Set( rotation );
-	}
-	else {
-		cout << "Reposition Failed for " << tri_shape << endl;
-	}
-}
-
-void NiSkinData::FixLinks( const vector<blk_ref> & blocks ) {
-
-	ABlock::FixLinks( blocks );
-
-	//Fix indicies for bones as they are copied from NiSkinInstance block
-	blk_ref inst_blk = GetParent();
-	if ( inst_blk.is_null() == false ) {
-		ISkinInstInternal * inst_data = (ISkinInstInternal*)inst_blk->QueryInterface( SkinInstInternal );
-		if ( inst_data != NULL ) {
-			vector<int> bone_list = inst_data->GetBoneList();
-			vector<blk_ref> bone_blks( bone_list.size() );
-			for ( uint i = 0; i < bone_list.size(); ++i ) {
-				bone_blks[i] = blocks[ bone_list[i] ];
-			}
-			SetBones( bone_blks );
-		}
-	}
-
-	//Straigten up the skeleton to match with the "bind pose" for any skin instances that exist
-	StraightenSkeleton();
-}
-
-vector<blk_ref> NiSkinData::GetBones() const {
-	//Put all the valid bones from the map into a vector to return
-	vector<blk_ref> bone_blks( bone_map.size() );
-
-	map<IBlock *, Bone>::const_iterator it;
-	int count = 0;
-	for (it = bone_map.begin(); it != bone_map.end(); ++it ) {
-		bone_blks[count] = it->first;
-		count++;
-	}
-
-	return bone_blks;
-}
-
-void NiSkinData::ReassignCrossRefs( const map<string,blk_ref> & name_map ) {
-	//This branch has been moved as part of a merge, so the cross references need to be moved to
-	//Point to the new blocks with the same names.  As far as I know, cross references always point
-	//To ParentNode blocks.
-
-	vector< map<IBlock *, Bone>::iterator > erase_list;
-
-	map<IBlock *, Bone>::iterator it;
-	int count = 0;
-	for (it = bone_map.begin(); it != bone_map.end(); ++it ) {
-		//Get the name of the current target if there is one
-		attr_ref name_attr = it->first->GetAttr("Name");
-		if ( name_attr.is_null() == true ) {
-			//Somehow this crossref is pointing to a block with no name
-			//do nothing
-			continue;
-		}
-
-		string name = name_attr->asString();
-
-		//Check if this name exists in the new group of nodes
-		map<string,blk_ref>::const_iterator found_it = name_map.find( name );
-
-		if ( found_it == name_map.end() ) {
-			//There were no matches, so this cross reference will continue to
-			//point to the same place
-			continue;
-		}
-
-		//--Reassign this cross reference to the new node with the same name that was found--//
-
-		//cout << "Found a cross reference to re-assign to new block with name:  " << name << endl;
-
-		//Remove this cross reference from its current target
-		((ABlock*)it->first)->DecCrossRef(this);
-
-		//Assign to block with matching name
-		bone_map[found_it->second.get_block()] = it->second;
-		erase_list.push_back( it );
-
-		//Add as a cross reference to new block
-		((ABlock*)found_it->second.get_block())->IncCrossRef(this);
-	}
-
-	//Erase all old map entires
-	for ( uint i = 0; i < erase_list.size(); ++i ) {
-		bone_map.erase( erase_list[i] );
-	}
-
-	//cout << "Finished loop." << endl;
-}
-
-map<int, float> NiSkinData::GetWeights( blk_ref const & bone ) const {
-	// since operator[] might insert a new element, it can't be const
-	// so we need the find function
-	return bone_map.find(bone.get_block())->second.weights;
-}
-
-void NiSkinData::AddBone( blk_ref const & bone, map<int, float> const & in ) {
-		
-	//Add bone to internal list
-	bone_map[bone.get_block()].weights = in;
-	
-	//Increment reference at bone node site
-	((ABlock*)bone.get_block())->IncCrossRef(this);
-}
-
-void NiSkinData::RemoveCrossLink( IBlock * block_to_remove ) {
-	//Remove bone from internal list
-	bone_map.erase( block_to_remove );
-
-	//Do not decrement bone node location because it is already dead
-}
-
-void NiSkinData::RemoveBone( blk_ref const & bone ) {
-	//Remove bone from internal list
-	bone_map.erase( bone.get_block() );
-
-	//Decrement reference at bone node site
-	//IBlockInternal * blk_int = (IBlockInternal*)bone->QueryInterface(BlockInternal);
-	((ABlock*)bone.get_block())->DecCrossRef(this);
-}
-
-NiSkinData::~NiSkinData() {
-	//Inform all linked bone nodes that this NiSkinData block is dying
-	map<IBlock *, Bone>::iterator it;
-	for (it = bone_map.begin(); it != bone_map.end(); ++it) {
-		//IBlockInternal * node_int = (IBlockInternal*)it->first->QueryInterface(BlockInternal);
-		((ABlock*)it->first)->DecCrossRef(this);
-	}
-}
-
-INode * NiSkinData::GetNodeParent() const {
-	//--Get Node Parent Bind Pose--//
-
-
-	blk_ref par_block = GetParent();
-	if ( par_block.is_null() == true ) {
-		//throw runtime_error("SkinData block does not have parent.");
-		return NULL;
-	}
-
-	par_block = par_block->GetParent();
-	
-	if ( par_block.is_null() == true ) {
-		//throw runtime_error("SkinData block does not have parent of parent.");
-		return NULL;
-	}
-
-	INode * par_node = (INode*)par_block->QueryInterface(ID_NODE);	
-	if ( par_node == NULL ) {
-		//throw runtime_error("SkinData block's parent of parent is not a node.");
-		return NULL;
-	}
-
-	return par_node;
-}
-
-void NiSkinData::CalculateBoneOffset( INode const * const par_node, IBlock const * const bone_block, Bone & result ) const {
-
-	//--Get Bone Bind Pose--//
-
-	//Get Bone Node
-	INode * const bone_node = (INode*)bone_block->QueryInterface(ID_NODE);
-
-	//Get bind matricies
-	Matrix44 par_mat, bone_mat, inv_mat, res_mat;
-	par_mat = par_node->GetWorldBindPos();
-	bone_mat = bone_node->GetWorldBindPos();
-
-	//Inverse bone matrix & multiply with parent node matrix
-	inv_mat = bone_mat.Inverse();
-	res_mat = par_mat * inv_mat;
-
-	//--Extract Scale from first 3 rows--//
-	float scale[3];
-	for (int r = 0; r < 3; ++r) {
-		//Get scale for this row
-		scale[r] = sqrt(res_mat[r][0] * res_mat[r][0] + res_mat[r][1] * res_mat[r][1] + res_mat[r][2] * res_mat[r][2] + res_mat[r][3] * res_mat[r][3]);
-
-		//Normalize the row by dividing each factor by scale
-		res_mat[r][0] /= scale[r];
-		res_mat[r][1] /= scale[r];
-		res_mat[r][2] /= scale[r];
-		res_mat[r][3] /= scale[r];
-	}
-
-	//--Store Result--//
-
-	//Store rotation matrix
-	for (int c = 0; c < 3; ++c) {
-		for (int r = 0; r < 3; ++r) {
-			result.rotation[r][c] = res_mat[r][c];
-		}
-	}
-
-	//Store translate vector
-	result.translation[0] = res_mat[3][0];
-	result.translation[1] = res_mat[3][1];
-	result.translation[2] = res_mat[3][2];
-
-	
-	//Store average scale
-	result.scale = (scale[0] + scale[1] + scale[2]) / 3.0f;
-}
-
-void NiSkinData::CalculateOverallOffset( Matrix33 & rot, fVector3 & tr, float & sc ) const {
-	// Node parent world transform
-	INode const * par = this->GetNodeParent();
-	if ( par == NULL ) {
-		throw runtime_error("Cannot calculate overall offset because this NiSkinData does not seem to be connected to a NiTriShape through a NiSkinInstance.");
-	}
-
-	Matrix44 par_mat = par->GetWorldTransform();
-	
-	// Skeleton root world transform
-	blk_ref skel = GetParent()->GetAttr("Skeleton Root")->asLink();
-	INode const * iskel = (INode const *)skel->QueryInterface(ID_NODE);
-	if ( iskel == NULL )
-		throw runtime_error("SkinInfluence skeleton root is not a node.");
-	Matrix44 skel_mat = iskel->GetWorldTransform();
-	
-	// Inverse parent node transform & multiply with skeleton matrix
-	Matrix44 inv_mat = par_mat.Inverse();
-	Matrix44 res_mat = inv_mat * skel_mat;
-
-	//--Extract Scale from first 3 rows--//
-	float scale[3];
-	for (int r = 0; r < 3; ++r) {
-		//Get scale for this row
-		scale[r] = sqrt(res_mat[r][0] * res_mat[r][0] + res_mat[r][1] * res_mat[r][1] + res_mat[r][2] * res_mat[r][2]);
-
-		//Normalize the row by dividing each factor by scale
-		res_mat[r][0] /= scale[r];
-		res_mat[r][1] /= scale[r];
-		res_mat[r][2] /= scale[r];
-	}
-
-	//--Store Result--//
-
-	//Store rotation matrix
-	for (int c = 0; c < 3; ++c)
-		for (int r = 0; r < 3; ++r)
-			rot[r][c] = res_mat[r][c];
-
-	//Store translate vector
-	tr[0] = res_mat[3][0];
-	tr[1] = res_mat[3][1];
-	tr[2] = res_mat[3][2];
-
-	//Store average scale
-	sc = (scale[0] + scale[1] + scale[2]) / 3.0f;
-}
-
-/***********************************************************
- * NiGeomMorpherController methods
- **********************************************************/
-
-string NiGeomMorpherController::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << ABlock::asString();
-
-	out << "Flag Analysis:" << endl
-		<< "   Animation Type:  ";
-
-	int flags = GetAttr("Flags")->asInt();
-	
-	if (flags & 1)
-		out << "APP_INIT" << endl;
-	else
-		out << "APP_TIME" << endl;
-
-	out << "   Cycle Type:  ";
-	if (flags & 2)
-		out << "REVERSE" << endl;
-	else if (flags & 4)
-		out << "CLAMP" << endl;
-	else
-		out << "LOOP" << endl;
-
-	out << "   Animation Enabled:  ";
-	if (flags & 8)
-		out << "Yes" <<endl;
-	else
-		out << "No" <<endl;
-
-	return out.str();
-}
-
-/***********************************************************
- * AKeyframeData methods
- **********************************************************/
-
-void AKeyframeData::Read( istream& file, unsigned int version ) {
-
-	scaleType = rotationType = translationType = xyzTypes[0] = xyzTypes[1] = xyzTypes[2] = KeyType(0);
-
-	//--Rotation--//
-	uint numRotations = ReadUInt( file );
-
-	if (numRotations > 0) {
-		NifStream( rotationType, file );
-
-		if ( rotationType != 4 ) {
-			rotKeys.resize( numRotations );
-			for ( unsigned int i = 0; i < rotKeys.size(); ++i ) {
-				StreamQuatKey(rotKeys[i], file, rotationType );
-			}
-		}
-		else {
-			//Before version 10.2.0.0, read vestigial time and discard
-			if ( version < VER_10_2_0_0 ) {
-				ReadFloat( file );
-			}
-
-			for (int i = 0; i < 3; i++) {
-				int subCount = ReadUInt( file );
-				if ( subCount > 0 ) {
-					NifStream( xyzTypes[i], file );
-
-					xyzKeys[i].resize( subCount );
-					for (uint j = 0; j < xyzKeys[i].size(); j++) {
-						NifStream(xyzKeys[i][j], file, xyzTypes[i] );
-					}
-				}
-			}
-		}
-	}
-
-	//--Translation--//
-	uint numTranslations = ReadUInt( file );
-
-	if (numTranslations > 0) {
-		NifStream( translationType, file );
-
-		transKeys.resize( numTranslations );
-		for ( unsigned int i = 0; i < transKeys.size(); ++i ) {
-			NifStream(transKeys[i], file, translationType );
-		}
-	}
-
-	//--Scale--//
-	uint numScalings = ReadUInt( file );
-
-	if (numScalings > 0) {
-		NifStream( scaleType, file );
-
-		scaleKeys.resize( numScalings );
-		for ( unsigned int i = 0; i < scaleKeys.size(); ++i ) {
-			NifStream(scaleKeys[i], file, scaleType );
-		}
-	}
-}
-
-void AKeyframeData::Write( ostream& file, unsigned int version ) const {
-
-	//--Rotation--//
-	WriteUInt( uint(rotKeys.size()) , file );
-
-	if ( rotKeys.size() > 0) {
-		NifStream( rotationType, file );
-
-		if ( rotationType != 4 ) {
-			for ( unsigned int i = 0; i < rotKeys.size(); ++i ) {
-				StreamQuatKey(rotKeys[i], file, rotationType );
-			}
-		}
-		else {
-			//Before version 10.2.0.0, write vestigial time
-			if ( version < VER_10_2_0_0 ) {
-				WriteFloat( 0.0, file );
-			}
-
-			for (int i = 0; i < 3; i++) {
-				WriteUInt( uint(xyzKeys[i].size()) , file );
-				if ( xyzKeys[i].size() > 0 ) {
-					NifStream( xyzTypes[i], file );
-
-					for (uint j = 0; j < xyzKeys[i].size(); j++) {
-						NifStream(xyzKeys[i][j], file, xyzTypes[i] );
-					}
-				}
-			}
-		}
-	}
-
-	//--Translation--//
-	WriteUInt( uint(transKeys.size()) , file );
-
-	if ( transKeys.size() > 0) {
-		NifStream( translationType, file );
-
-		for ( unsigned int i = 0; i < transKeys.size(); ++i ) {
-			NifStream(transKeys[i], file, translationType );
-		}
-	}
-
-	//--Scale--//
-	WriteUInt( uint(scaleKeys.size()), file );
-
-	if (scaleKeys.size() > 0) {
-		NifStream( scaleType, file );
-
-		for ( unsigned int i = 0; i < scaleKeys.size(); ++i ) {
-			NifStream(scaleKeys[i], file, scaleType );
-		}
-	}
-}
-
-string AKeyframeData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	//--Rotation--//
-	out << "Rotations:  " << uint(rotKeys.size()) << endl;
-
-	if (rotKeys.size() > 0) {
-		out << "Rotation Type:  " << rotationType << endl;
-
-		if (verbose) {
-			for (unsigned int i = 0; i < rotKeys.size(); i++) {
-				out << "Key Time:  " << rotKeys[i].time << "  ";
-
-				if (rotationType != 4) {
-					out << "Rotation:  Q[" << rotKeys[i].data.w << " ( " << rotKeys[i].data.x << ", " << rotKeys[i].data.y << ", " << rotKeys[i].data.z << ")]" << endl;
-					//	<< "   As Matrix:";
-					//QuatToMatrix(rotKeys[i].data, out );
-					//out << "   As Angles:";
-					//QuatToEuler(rotKeys[i].data, out );
-
-				}
-				
-				if (rotationType == 3) {
-					out << ", T " << rotKeys[i].tension << ", B " << rotKeys[i].bias << ", C " << rotKeys[i].continuity;
-				} else if (rotationType == 4) {
-					out << "Rotation Type 4 Unsupported - Data was not read" << endl;
-				}
-				out << endl;
-			}
-		} else {
-			out << "<<Data Not Shown>>" << endl;
-		}	
-	}
-
-	//--Translation--//
-	out << "Translations:  " << uint(transKeys.size()) << endl;
-
-	if (transKeys.size() > 0) {
-		out << "Translation Type:  " << translationType << endl;
-
-		if (verbose) {
-			for (unsigned int i = 0; i < transKeys.size(); i++) {
-				out << "Key Time:  " << transKeys[i].time << "  ";
-				
-				out << "Data:  V(" << transKeys[i].data.x << ", " << transKeys[i].data.y << ", " << transKeys[i].data.z;
-
-				if (translationType == 2) {
-					out << "), F(" << transKeys[i].forward_tangent.x << ", " << transKeys[i].forward_tangent.y << ", " << transKeys[i].forward_tangent.z << "), B(" << transKeys[i].backward_tangent.x << ", " << transKeys[i].backward_tangent.y << ", " << transKeys[i].backward_tangent.z << ")";
-				}else if (translationType == 3) {
-					out << ", T " << transKeys[i].tension << ", B " << transKeys[i].bias << ", C " << transKeys[i].continuity;
-				}
-				out << endl;
-			}
-		} else {
-			out << "<<Data Not Shown>>" << endl;
-		}
-		
-	}
-                        
-	//--Scale--//
-	out << "Scalings:  " << uint(scaleKeys.size()) << endl;
-
-	if (verbose) {
-		if (scaleKeys.size() > 0) {
-			out << "Scale Type:  " << scaleType << endl;
-
-			for (unsigned int i = 0; i < scaleKeys.size(); i++) {
-				out << "Key Time:  " << scaleKeys[i].time  << "  ";
-
-				out << "Data:  S(" << scaleKeys[i].data << ")";
-
-				if (scaleType == 2) {
-					out << ", FT(" << scaleKeys[i].forward_tangent << "), BT(" << scaleKeys[i].backward_tangent << ")";
-				} else if (scaleType == 3) {
-					out << ", T " << scaleKeys[i].tension << ", B " << scaleKeys[i].bias << ", C " << scaleKeys[i].continuity;
-				}
-				out << endl;
-			}
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	//out << setprecision(1);
-
-	return out.str();
-}
-
-/***********************************************************
- * NiPSysEmitterCtlrData methods
- **********************************************************/
-
-void NiPSysEmitterCtlrData::Read( istream& file, unsigned int version ) {
-
-	//--Float Keys--//
-	uint numKeys = ReadUInt( file );
-
-	if (numKeys > 0) {
-		NifStream( f_key_type, file );
-		float_keys.resize( numKeys );
-		for ( uint i = 0; i < float_keys.size(); ++i ) {
-			NifStream( float_keys[i], file, f_key_type );
-		}
-	}
-
-	//--Byte Keys--//
-	numKeys = ReadUInt( file );
-
-	byte_keys.resize( numKeys );
-	for ( uint i = 0; i < byte_keys.size(); ++i ) {
-		NifStream( byte_keys[i], file, LINEAR_KEY );
-	}	
-}
-
-void NiPSysEmitterCtlrData::Write( ostream& file, unsigned int version ) const {
-
-	//--Float Keys--//
-	WriteUInt( uint(float_keys.size()), file );
-
-	if (float_keys.size() > 0) {
-		NifStream( f_key_type, file );
-		for ( uint i = 0; i < float_keys.size(); ++i ) {
-			NifStream( float_keys[i], file, f_key_type );
-		}	
-	}
-
-	//--Byte Keys--//
-	WriteUInt( uint(byte_keys.size()), file );
-
-	for ( uint i = 0; i < byte_keys.size(); ++i ) {
-		NifStream( byte_keys[i], file, LINEAR_KEY );
-	}	
-}
-
-string NiPSysEmitterCtlrData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Float Key Count:  " << uint(float_keys.size()) << endl
-		<< "Float Key Type:  " << f_key_type << endl;
-
-	if (verbose) {
-		vector< Key<float> >::const_iterator it;
-		for ( it = float_keys.begin(); it != float_keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Float Value:  " << it->data << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	out << "Byte Key Count:  " << uint(byte_keys.size()) << endl;
-
-	if (verbose) {
-		vector< Key<byte> >::const_iterator it;
-		for ( it = byte_keys.begin(); it != byte_keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Float Value:  " << it->data << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiBoolData methods
- **********************************************************/
-
-void NiBoolData::Read( istream& file, unsigned int version ) {
-	uint keyCount = ReadUInt( file );
-	NifStream( _type, file );
-
-	_keys.resize( keyCount );
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, _type );
-	}
-}
-
-void NiBoolData::Write( ostream& file, unsigned int version ) const {
-	WriteUInt( uint(_keys.size()), file );
-	NifStream( _type, file );
-
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, _type );
-	}
-}
-
-string NiBoolData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Key Count:  " << uint(_keys.size()) << endl
-		<< "Key Type:  " << _type << endl;
-
-	if (verbose) {
-		vector< Key<byte> >::const_iterator it;
-		for ( it = _keys.begin(); it != _keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Is Visible:  ";
-			if ( it->data != 0 ) {
-				out << "True" << endl;
-			} else {
-				out << "False" << endl;
-			}
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiColorData methods
- **********************************************************/
-
-void NiColorData::Read( istream& file, unsigned int version ) {
-	uint keyCount = ReadUInt( file );
-	NifStream( _type, file );
-
-	_keys.resize( keyCount );
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, _type );
-	}
-}
-
-void NiColorData::Write( ostream& file, unsigned int version ) const {
-	WriteUInt( uint(_keys.size()), file );
-	NifStream( _type, file );
-
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, _type );
-	}
-}
-
-string NiColorData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Key Count:  " << uint(_keys.size()) << endl
-		<< "Key Type:  " << _type << endl;
-
-	if (verbose) {
-		vector< Key<Color4> >::const_iterator it;
-		for ( it = _keys.begin(); it != _keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Color:  " << it->data.r << ", " << it->data.g << ", " << it->data.b << ", " << it->data.a << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiControllerSequence methods
- **********************************************************/
-
-void NiControllerSequence::Read( istream& file, unsigned int version ) {
-	//GetAttr("Name")->Read( file, version );
-
-	//Up to version 10.1.0.0 the text key block is up here and named
-	if ( version <= VER_10_1_0_0 ) {
-		NifStream( txt_key_name, file );
-		txt_key_blk.set_index( ReadUInt( file ) );
-	}
-
-	//Read the ControllerLink array
-	uint count = ReadUInt( file );
-	children.resize( count );
-
-	//After version 10.2.0.0 there is an unknown int here
-	if ( version >=	VER_10_2_0_0 ) {
-		NifStream( unk_int1, file );
-	}
-
-	for (uint i = 0; i < children.size(); ++i ) {
-		//Up to version 10.1.0.0 the name is stored here in a string
-		if ( version <= VER_10_1_0_0 ) {
-			NifStream( children[i].name, file );
-		}
-		children[i].block.set_index( ReadUInt(file)  );
-		//From version 10.2.0.0 there is a lot more stuff here
-		if ( version >= VER_10_2_0_0 ) {
-			children[i].unk_link.set_index( ReadUInt(file) );
-			//Read and discard duplicate String Palette index
-			ReadUInt(file);
-			//Read offsets
-			NifStream( children[i].name_offset, file );
-			NifStream( children[i].unk_short1, file );
-			NifStream( children[i].property_offset, file );
-			NifStream( children[i].unk_short2, file );
-			NifStream( children[i].controller_offset, file );
-			NifStream( children[i].unk_short3, file );
-			NifStream( children[i].var1_offset, file );
-			NifStream( children[i].unk_short4, file );
-			NifStream( children[i].var2_offset, file );
-			NifStream( children[i].unk_short5, file );
-		}
-	}
-	
-	//And from version 10.2.0.0 there is a lot more stuff down here as well
-	if (version >= VER_10_2_0_0 ) {
-		NifStream( unk_float1, file );
-
-		//Text key link is down here now and has no name
-		txt_key_blk.set_index( ReadUInt( file ) ); 
-
-		for (int i = 0; i < 4; ++i ) {
-			NifStream( unk_4_floats[i], file );
-		}
-		//This does not exist after version 10.2.0.0
-		if ( version < VER_20_0_0_4 ) {
-			NifStream( unk_float2, file );
-		}
-		NifStream( unk_int2, file );
-		NifStream( unk_string, file );
-		//GetAttr("String Palette")->Read( file, version );
-	}
-}
-
-void NiControllerSequence::Write( ostream& file, unsigned int version ) const {
-	//GetAttr("Name")->Write( file, version );
-
-	//Up to version 10.1.0.0 the text key block is up here and named
-	if ( version <= VER_10_1_0_0 ) {
-		NifStream( txt_key_name, file );
-		WriteUInt( txt_key_blk.get_index(), file );
-	}
-
-	//Read the ControllerLink array
-	WriteUInt( uint(children.size()), file );
-
-	//After version 10.2.0.0 there is an unknown int here
-	if ( version >=	VER_10_2_0_0 ) {
-		NifStream( unk_int1, file );
-	}
-
-	for (uint i = 0; i < children.size(); ++i ) {
-		//Up to version 10.1.0.0 the name is stored here in a string
-		if ( version <= VER_10_1_0_0 ) {
-			NifStream( children[i].name, file );
-		}
-		WriteUInt( children[i].block.get_index(), file );
-		//From version 10.2.0.0 there is a lot more stuff here
-		if ( version >= VER_10_2_0_0 ) {
-			WriteUInt( children[i].unk_link.get_index(), file );
-			//Write duplicate String Palette index
-			//GetAttr("String Palette")->Write( file, version );
-
-			//Write offsets
-			NifStream( children[i].name_offset, file );
-			NifStream( children[i].unk_short1, file );
-			NifStream( children[i].property_offset, file );
-			NifStream( children[i].unk_short2, file );
-			NifStream( children[i].controller_offset, file );
-			NifStream( children[i].unk_short3, file );
-			NifStream( children[i].var1_offset, file );
-			NifStream( children[i].unk_short4, file );
-			NifStream( children[i].var2_offset, file );
-			NifStream( children[i].unk_short5, file );
-		}
-	}
-	
-	//And from version 10.2.0.0 there is a lot more stuff down here as well
-	if (version >= VER_10_2_0_0 ) {
-		NifStream( unk_float1, file );
-
-		//Text key link is down here now and has no name
-		WriteUInt( txt_key_blk.get_index(), file );
-
-		for (int i = 0; i < 4; ++i ) {
-			NifStream( unk_4_floats[i], file );
-		}
-		//This does not exist after version 10.2.0.0
-		if ( version < VER_20_0_0_4 ) {
-			NifStream( unk_float2, file );
-		}
-		NifStream( unk_int2, file );
-		NifStream( unk_string, file );
-		//GetAttr("String Palette")->Write( file, version );
-	}
-}
-
-string NiControllerSequence::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Name:  " << GetAttr("Name")->asString() << endl
-		<< "Text Key Name:  " << txt_key_name << endl
-		<< "Text Key Block:  " << txt_key_blk << endl
-		<< "Unknown Int 1:  " << unk_int1 << endl
-		<< "Kf Children:";
-
-	if ( children.size() == 0 ) {
-		out << "  None" << endl;
-	} else {
-		out << endl;
-	}
-
-	//Check for a string palette
-	string pal;
-	blk_ref str_pal = GetAttr("String Palette")->asLink();
-	if ( str_pal.is_fixed() == true && str_pal.is_null() == false ) {
-		pal = str_pal->GetAttr("Palette")->asString();
-	}
-
-	for (uint i = 0; i < children.size(); ++i ) {
-		out << "   Kf Child " << i + 1 << ":" << endl
-			<< "      Name:  "  << children[i].name << endl
-			<< "      Block:  " << children[i].block << endl
-			<< "      Unknown Link:  " << children[i].unk_link << endl
-			<< "      Name Offset      :  " << children[i].name_offset <<" (" << GetSubStr( pal, children[i].name_offset) << ")" << endl
-			<< "      Unknown Short 1  :  " << children[i].unk_short1 << endl
-			<< "      Property Offset  :  " << children[i].property_offset << " (" << GetSubStr( pal, children[i].property_offset) << ")" << endl
-			<< "      Unknown Short 2  :  " << children[i].unk_short2 << endl
-			<< "      Controller Offset:  " << children[i].controller_offset << " (" << GetSubStr( pal, children[i].controller_offset) << ")" << endl
-			<< "      Unknown Short 3  :  " << children[i].unk_short3 << endl
-			<< "      Variable 1 Offset:  " << children[i].var1_offset << " (" << GetSubStr( pal, children[i].var1_offset) << ")" << endl
-			<< "      Unknown Short 4  :  " << children[i].unk_short4 << endl
-			<< "      Variable 2 Offset:  " << children[i].var2_offset << " (" << GetSubStr( pal, children[i].var2_offset) << ")" << endl
-			<< "      Unknown Short 5  :  " << children[i].unk_short5 << endl;
-	}
-
-	out << "Unknown Float 1:  " << unk_float1 << endl
-		<< "Unknown 4 Floats:  " << unk_4_floats[0] << ", " << unk_4_floats[1] << ", " << unk_4_floats[2] << ", " << unk_4_floats[3] << endl
-		<< "Unknown Float 2:  " << unk_float2 << endl
-		<< "Unknown String:  " << unk_string << endl
-		<< "String Palette:  " << GetAttr("String Palette")->asString() << endl;
-
-	return out.str();
-}
-
-string NiControllerSequence::GetSubStr( const string & pal, short offset ) const {
-	string out;
-	
-	// -1 is a null offset
-	if ( offset == -1 ) {
-		return out;
-	}
-
-	for ( uint i = offset; i < pal.size(); ++i ) {
-		if ( pal[i] == '\0' ) {
-			break;
-		}
-		out.push_back( pal[i] );
-	}
-
-	return out;
-}
-
-void NiControllerSequence::FixLinks( const vector<blk_ref> & blocks ) {
-	ABlock::FixLinks( blocks );
-	
-	//Fix text key lnk
-	if ( txt_key_blk.is_null() == false ) {
-		txt_key_blk = blocks[txt_key_blk.get_index()];
-	
-		//Add this block as a child
-		AddChild( txt_key_blk.get_block() );
-	}
-
-	for (uint i = 0; i < children.size(); ++i ) {
-		if ( children[i].block.is_null() == false ) {
-			//Fix links for this child
-			children[i].block = blocks[children[i].block.get_index()];
-
-			//Add this block as a child
-			AddChild( children[i].block.get_block() );
-		}
-		
-		if ( children[i].unk_link.is_null() == false ) {
-			children[i].unk_link = blocks[children[i].unk_link.get_index()];
-
-			//Add this block as a child
-			AddChild( children[i].unk_link.get_block() );
-		}
-	}
-}
-
-list<blk_ref> NiControllerSequence::GetLinks() const {
-	list<blk_ref> links = ABlock::GetLinks();
-
-	//add link for text key block
-	if ( txt_key_blk.is_null() == false ) {
-		links.push_back( txt_key_blk );
-	}
-
-	//Add child links
-	for (uint i = 0; i < children.size(); ++i ) {
-		if ( children[i].block.is_null() == false ) {
-			links.push_back( children[i].block );
-		}
-		if ( children[i].unk_link.is_null() == false ) {
-			links.push_back( children[i].unk_link );
-		}
-	}
-
-	return links;
-}
-
-NiControllerSequence::~NiControllerSequence() {
-
-	//Remove text key child
-	RemoveChild( txt_key_blk.get_block() );
-
-	for (uint i = 0; i < children.size(); ++i ) {
-		//Remove child blocks
-		RemoveChild( children[i].block.get_block() );
-		RemoveChild( children[i].unk_link.get_block() );
-	}
-}
-
-void NiControllerSequence::SetTextKey( string new_name, blk_ref new_link ) {
-	//Set new name
-	txt_key_name = new_name;
-	
-	//Check for identical block values
-	if ( new_link == txt_key_blk )
-		return;
-	
-	//Remove old child
-	if ( txt_key_blk.is_null() == false ) {
-		RemoveChild( txt_key_blk.get_block() );
-	}
-
-	//Set new value
-	txt_key_blk = new_link;
-
-	//Add new child
-	if ( txt_key_blk.is_null() == false ) {
-		AddChild( txt_key_blk.get_block() );
-	}
-}
-
-void NiControllerSequence::AddKfChild( string new_name, blk_ref new_link, string controller_type = "" ) {
-	//Make sure the link isn't null
-	if ( new_link.is_null() == true ) {
-		throw runtime_error("Attempted to add a null link to NiControllerSequence block.");
-	}
-
-	KfChild kc;
-
-	kc.block = new_link;
-
-	//Check for a string palette
-	blk_ref str_pal = GetAttr("String Palette")->asLink();
-	if ( str_pal.is_null() == true ) {
-		//No string palette, store name internally
-		kc.name = new_name;
-	} else {
-		//String palette exists - store name and controller type there
-
-		//Make sure they didn't give us empty strings
-		if ( new_name.size() == 0 || controller_type.size() == 0 ) {
-			throw runtime_error( "You cannot use empty name or controller type strings when using a string palette.");
-		}
-
-		//Get palette
-		string pal = str_pal->GetAttr("Palette")->asString();
-
-		//--Search for copies of the text we want to add--//
-
-		//Search for the name string
-		int offset = (int)pal.find( new_name );
-		if ( offset == -1 ) {
-			//String not found, append it
-			kc.name_offset = (uint)pal.size();
-			pal.append( new_name + '\0' );
-		} else {
-			//String found, use existing offset
-			kc.name_offset = offset;
-		}
-
-		//Search for the controller type string
-		offset = (int)pal.find( controller_type );
-		if ( offset == -1 ) {
-			//String not found, append it
-			kc.controller_offset = (uint)pal.size();
-			pal.append( controller_type + '\0' );
-		} else {
-			//String found, use existing offset
-			kc.controller_offset = offset;
-		}
-
-		//Store the palette back to the string pal block
-		str_pal->GetAttr("Palette")->Set( pal );
-	}
-	
-	children.push_back( kc );
-
-	//Add new child
-	AddChild( kc.block.get_block() );
-	
-	//This should be impossible now, but don't want to forget it later
-	if ( kc.unk_link.is_null() != true ) {
-		AddChild( kc.unk_link.get_block() );
-	}
-}
-
-void NiControllerSequence::ClearKfChildren() {
-
-	//Cycle through all Kf Children, removing them as parents from the blocks they refer to
-	for (uint i = 0; i < children.size(); ++i ) {
-		if ( children[i].block.is_null() != true ) {
-			RemoveChild( children[i].block.get_block() );
-		}
-		if ( children[i].unk_link.is_null() != true ) {
-			RemoveChild( children[i].unk_link.get_block() );
-		}
-	}
-
-	//Clear list
-	children.clear();
-
-	//Check for a string palette
-	blk_ref str_pal = GetAttr("String Palette")->asLink();
-	if ( str_pal.is_null() == false ) {
-		//There's a string palette, so clear it out
-		str_pal->GetAttr("Palette")->Set( "" );
-	}
-}
-
-/***********************************************************
- * NiFloatData methods
- **********************************************************/
-
-void NiFloatData::Read( istream& file, unsigned int version ) {
-	uint keyCount = ReadUInt( file );
-	NifStream( _type, file );
-
-	_keys.resize( keyCount );
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, _type );
-	}
-}
-
-void NiFloatData::Write( ostream& file, unsigned int version ) const {
-	WriteUInt( uint(_keys.size()), file );
-	NifStream( _type, file );
-
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, _type );
-	}
-}
-
-string NiFloatData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Key Count:  " << uint(_keys.size()) << endl
-		<< "Key Type:  " << _type << endl;
-
-	if (verbose) {
-		vector< Key<float> >::const_iterator it;
-		for ( it = _keys.begin(); it != _keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Float Value:  " << it->data << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiStringExtraData methods
- **********************************************************/
-
-void NiStringExtraData::Read( istream& in, unsigned int version ) {
-	AExtraData::Read( in, version );
-	
-	//GetAttr("Name")->Read( in, version );
-	//GetAttr("Next Extra Data")->Read( in, version );
-	
-
-	//Up to version 4.2.2.0, read bytes remaining but don't bother to store it
-	if ( version <= VER_4_2_2_0 ) {
-		ReadUInt( in );
-	}
-
-	//GetAttr("String Data")->Read( in, version );
-}
-
-void NiStringExtraData::Write( ostream& out, unsigned int version ) const {
-	//GetAttr("Name")->Write( out, version );
-	//GetAttr("Next Extra Data")->Write( out, version );
-	AExtraData::Write( out, version );
-
-	attr_ref string_data = GetAttr("String Data");
-
-	//Up to version 4.2.2.0, Write Bytes Remaining; length of string + 4
-	if ( version <= VER_4_2_2_0 ) {
-		WriteUInt( uint(string_data->asString().length()) + 4, out );
-	}
-
-	//string_data->Write( out, version );
-}
-
-string NiStringExtraData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	attr_ref name_attr = GetAttr("Name");
-	//attr_ref next_data = GetAttr("Next Extra Data");
-	attr_ref string_data = GetAttr("String Data");
-
-	out << "Name:  " << name_attr->asString() << endl
-		//<< next_data->GetName() << ":  " << next_data->asLink() << endl
-		<< "Bytes Remaining:  " << uint(string_data->asString().length()) + 4 << endl
-		<< string_data->GetName() << ":  " << string_data->asString() << endl;
-	
-	return out.str();
-}
-
-/***********************************************************
- * NiMorphData methods
- **********************************************************/
-
-void NiMorphData::Read( istream& file, unsigned int version ) {
-
-	uint morphCount = ReadUInt( file );
-	NifStream( vertCount, file );
-
-	//GetAttr("Unknown Byte")->Read( file, version );
-	byte unk_byte = ReadByte( file );
-
-	morphs.resize(morphCount);
-	for ( uint i = 0; i < morphs.size() ; ++i ) {
-		uint numKeys = ReadUInt( file );
-
-		NifStream( morphs[i]._type, file );
-		
-		morphs[i].keys.resize( numKeys );
-		
-		for (uint j = 0; j < morphs[i].keys.size(); j++) {
-			NifStream( morphs[i].keys[j], file, morphs[i]._type );
-		}
-
-		morphs[i].morph.resize( vertCount );
-		//Stream whole array of Vector3
-		NifStream( morphs[i].morph, file );
-	}
-}
-
-void NiMorphData::Write( ostream& file, unsigned int version ) const {
-	WriteUInt( uint(morphs.size()), file );
-	NifStream( vertCount, file );
-
-	//GetAttr("Unknown Byte")->Write( file, version );
-
-	for ( uint i = 0; i < morphs.size() ; ++i ) {
-		WriteUInt( uint(morphs[i].keys.size()), file );
-
-		NifStream( morphs[i]._type, file );
-
-		for (uint j = 0; j < morphs[i].keys.size(); j++) {
-			NifStream( morphs[i].keys[j], file, morphs[i]._type );
-		}
-		
-		//Stream whole array of Vector3
-		NifStream( morphs[i].morph, file );
-	}
-}
-
-string NiMorphData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Morph Count:  " << uint(morphs.size()) << endl
-		<< "Vert Count:  " << vertCount << endl
-		<< "Unknown Byte:  " << GetAttr("Unknown Byte")->asString() << endl;
-
-	for ( uint i = 0; i < morphs.size() ; ++i ) {
-		out << "---Morph " << i + 1 << "---" << endl;
-
-		out << "Time Count:  " << uint(morphs[i].keys.size()) << endl
-			<< "Key Type:  " << morphs[i]._type << endl;
-		
-		if (verbose) {
-			for (uint j = 0; j < morphs[i].keys.size(); ++j ) {
-				//Always show time and data
-				out << "Key Time:  " << morphs[i].keys[j].time << ", Influence?: " << morphs[i].keys[j].data;
-				if ( morphs[i]._type == 2 ) {
-					//Uses Quadratic interpolation
-					out << ", FT(" << morphs[i].keys[j].forward_tangent << ") BT(" << morphs[i].keys[j].backward_tangent << ")";
-				} else if ( morphs[i]._type == 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 ) {
-				out << "Morph " << j + 1 << ":  (" << morphs[i].morph[j].x << ", " << morphs[i].morph[j].y << ", " << morphs[i].morph[j].z << ")" << endl;
-			}
-		} else {
-			out << "<<Data Not Shown>>" << endl;
-		}
-	}
-
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-	return out.str();
-}
-
-void NiMorphData::SetVertexCount( int n ) {
-	vertCount = n;
-	for ( uint i = 0; i < morphs.size(); ++i ) {
-		morphs[i].morph.resize( n );
-	}
-}
-
-void NiMorphData::SetMorphVerts( int n, const vector<Vector3> & in ) {
-	// Make sure the size of the incoming vector equal vertCount
-	if ( in.size() != vertCount )
-		throw runtime_error("Input array size must equal Vertex Count.  Call SetVertexCount() to resize.");
-
-	//It's the right size, so go ahead and set it
-	morphs[n].morph = in;
-}
-
-/***********************************************************
- * NiPalette methods
- **********************************************************/
-
-void NiPalette::Read( istream& file, unsigned int version ) {
-
-	NifStream( unkByte, file );
-	NifStream( numEntries, file );
-	
-
-	//Read Palette
-	for (int i = 0; i < 256; ++i) {
-		for (int j = 0; j < 4; ++j) {
-			NifStream( palette[i][j], file );
-		}
-	}
-}
-
-void NiPalette::Write( ostream& file, unsigned int version ) const {
-
-	NifStream( unkByte, file );
-	NifStream( numEntries, file );
-
-	//Write Palette
-	for (int i = 0; i < 256; ++i) {
-		for (int j = 0; j < 4; ++j) {
-			NifStream( palette[i][j], file );
-		}
-	}
-}
-
-
-string NiPalette::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Unknown Byte:  " << int(unkByte) << endl;
-	out << "Num Entries?:  " << numEntries << endl;
-
-	//Print Palette
-
-	vector<Color4>col_pal = GetPalette();
-
-	out << "Palette: (256 colors)" << endl;
-	if (verbose) {
-		for (int i = 0; i < 256; ++i) {
-			out << i + 1 << ":  " << setw(4) << int(palette[i][0]) << ", " << setw(4) << int(palette[i][1]) << ", " << setw(4) << int(palette[i][2]) << ", " << setw(4) << int(palette[i][3]) << "\t";
-			out << i + 1 << ":  " << setw(4) << col_pal[i].r << ", " << setw(4) << col_pal[i].g << ", " << setw(4) << col_pal[i].b << ", " << setw(4) << col_pal[i].a << endl;
-		}
-	} else {
-		out << "  <<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-
-vector<Color4> NiPalette::GetPalette() const {
-	vector<Color4> color_pal(256);
-
-	for ( uint i = 0; i < 256; ++i ) {
-		
-		color_pal[i].r = float(palette[i][0]) / 255.0f;
-		color_pal[i].g = float(palette[i][1]) / 255.0f;
-		color_pal[i].b = float(palette[i][2]) / 255.0f;
-		color_pal[i].a = float(palette[i][3]) / 255.0f;
-	}
-
-	return color_pal;
-}
-
-void NiPalette::SetPalette( const vector<Color4> & new_pal ) {
-	if ( new_pal.size() != 256 ) {
-		throw runtime_error( "Palette size must be 256" );
-	}
-
-	for ( uint i = 0; i < 256; ++i ) {
-		palette[i][0] = int( new_pal[i].r * 255.0f );
-		palette[i][1] = int( new_pal[i].g * 255.0f );
-		palette[i][2] = int( new_pal[i].b * 255.0f );
-		palette[i][3] = int( new_pal[i].a * 255.0f );
-	}
-}
-
-/***********************************************************
- * NiSkinPartition methods
- **********************************************************/
-
-void NiSkinPartition::Read( istream& file, unsigned int version ) {
-
-	uint numPartitions = ReadUInt( file );
-	partitions.resize( numPartitions );
-	
-	vector<SkinPartition>::iterator it;
-	for (it = partitions.begin(); it != partitions.end(); ++it ) {
-
-		//Read counts
-		ushort numVertices = ReadUShort( file );
-		ushort numTriangles = ReadUShort( file );
-		ushort numBones = ReadUShort( file );
-		ushort numStrips = ReadUShort( file );
-		ushort numWeights = ReadUShort( file );
-
-		//Read bones
-		it->bones.resize( numBones );
-		NifStream( it->bones, file );
-
-		//Read vertex map
-		//After version 10.1.0.0, the vertex map is conditioned on a bool
-		bool hasVertexMap = true;
-		if ( version >= VER_10_1_0_0 ) {
-			hasVertexMap = ReadBool( file, version );
-		}
-
-		if ( hasVertexMap ) {
-			it->vertexMap.resize( numVertices );
-			NifStream( it->vertexMap, file );
-		}
-
-		//Read vertex weights
-		//After version 10.1.0.0, the vertex weights are conditioned on a bool
-		bool hasVertexWeights = true;
-		if ( version >= VER_10_1_0_0 ) {
-			hasVertexWeights = ReadBool( file, version );
-		}
-
-		if ( hasVertexWeights ) {
-			//Resize vectors 2 deep
-			it->vertexWeights.resize( numVertices );
-			for ( uint i = 0; i < it->vertexWeights.size(); ++i ) {
-				it->vertexWeights[i].resize( numWeights );
-			}
-			//Read it all
-			NifStream( it->vertexWeights, file );
-		}
-
-		//Read strip lenghts, resize strip vectors as we go.
-		it->strips.resize( numStrips );
-		for ( uint i = 0; i < it->strips.size(); ++i ) {
-			it->strips[i].resize( ReadUShort( file ) );
-		}
-
-		//Read triangle strip points
-		//After version 10.1.0.0, the triangle strip points are conditioned on a bool
-		bool hasStrips = true;
-		if ( version >= VER_10_1_0_0 ) {
-			hasStrips = ReadBool( file, version );
-		}
-
-		if ( hasStrips ) {
-			//Read 2 deep
-			NifStream( it->strips, file );
-		}
-
-		//Read triangles
-		//Triangles only exist if numStrips == 0
-		if ( it->strips.size() == 0 ) {
-			it->triangles.resize( numTriangles );
-			NifStream( it->triangles, file );
-		}
-
-		//This bool exists in all versions
-		bool hasBoneIndices = ReadBool( file, version );
-
-		if ( hasBoneIndices ) {
-			//Resize vectors 2 deep
-			it->boneIndices.resize( numVertices );
-			for ( uint i = 0; i < it->vertexWeights.size(); ++i ) {
-				it->boneIndices[i].resize( numWeights );
-			}
-			//Read it all
-			NifStream( it->boneIndices, file );
-		}
-	}
-}
-
-void NiSkinPartition::Write( ostream& file, unsigned int version ) const {
-
-	WriteUInt( uint(partitions.size()), file );
-
-	vector<SkinPartition>::const_iterator it;
-	for (it = partitions.begin(); it != partitions.end(); ++it ) {
-		//Write counts
-		WriteUShort( ushort( it->vertexMap.size()), file );
-		WriteUShort( ushort( it->triangles.size()), file );
-		WriteUShort( ushort( it->bones.size()), file );
-		WriteUShort( ushort( it->strips.size()), file );
-		WriteUShort( ushort( it->vertexWeights.size()), file );
-
-		//Write bones
-		NifStream( it->bones, file );
-
-		//Write vertex map
-		//After version 10.1.0.0, the vertex map is conditioned on a bool
-		if ( version >= VER_10_1_0_0 ) {
-			WriteBool( it->vertexMap.size() > 0, file, version );
-		}
-
-		NifStream( it->vertexMap, file );
-
-		//Write vertex weights
-		//After version 10.1.0.0, the vertex weights are conditioned on a bool
-		if ( version >= VER_10_1_0_0 ) {
-			WriteBool( it->vertexWeights.size() > 0, file, version );
-		}
-
-		//Write vertex weights - 2 deep
-		NifStream( it->vertexWeights, file );
-
-		//Write strip lenghts
-		for ( uint i = 0; i < it->strips.size(); ++i ) {
-			WriteUShort( ushort(it->strips.size()), file );
-		}
-
-		//Write triangle strip points
-		//After version 10.1.0.0, the triangle strip points are conditioned on a bool
-		if ( version >= VER_10_1_0_0 ) {
-			WriteBool( it->strips.size() > 0, file, version );
-		}
-
-		//Write strip points - 2 deep
-		NifStream( it->strips, file );
-
-		//Write triangles
-		//Triangles only exist if numStrips == 0
-		if ( it->strips.size() == 0 ) {
-			NifStream( it->triangles, file );
-		}
-
-		//This bool exists in all versions
-		WriteBool( it->boneIndices.size() > 0, file, version );
-
-		//Write bone indices - 2 deep
-		NifStream( it->boneIndices, file );
-	}
-}
-
-
-string NiSkinPartition::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	int count = 0;
-	vector<SkinPartition>::const_iterator it;
-	for (it = partitions.begin(); it != partitions.end(); ++it ) {
-		count++;
-		//Write counts
-		out << "Skin Partition " << count << ":" << endl
-			<< "   Vertex Count:  " << ushort(it->vertexMap.size()) << endl
-			<< "   Triangle Count:  " << ushort(it->triangles.size()) << endl
-			<< "   Bone Count:  " << ushort(it->bones.size()) << endl
-			<< "   Triangle Strip Count:  " << ushort(it->strips.size()) << endl
-			<< "   Vertex Weight Count:  " << ushort(it->vertexWeights.size()) << endl;
-
-		if (verbose) {
-			out << "   Bones:" << endl;
-			for ( uint i = 0; i < it->bones.size(); ++i ) {
-				out << "      " << i + 1 << ":  " << it->bones[i] << endl;
-			}
-
-			out << "   Vertex Map:" << endl;
-			for ( uint i = 0; i < it->vertexMap.size(); ++i ) {
-				out << "      " << i + 1 << ":  " << it->vertexMap[i] << endl;
-			}
-
-			out << "   Vertex Weights:" << endl;
-			for ( uint i = 0; i < it->vertexWeights.size(); ++i ) {
-				out << "Group " << i + 1 << ":" << endl;
-				for ( uint j = 0; j < it->vertexWeights[i].size(); ++j ) {
-					out << "         " << j + 1 << ":  " << it->vertexWeights[i][j] << endl;
-				}
-			}
-
-			out << "   Triangle Strips:" << endl;
-			for ( uint i = 0; i < it->strips.size(); ++i ) {
-				out << "Strip " << i + 1 << ":" << endl;
-				for ( uint j = 0; j < it->strips[i].size(); ++j ) {
-					out << "         " << j + 1 << ":  " << it->strips[i][j] << endl;
-				}
-			}
-
-			out << "   Triangles:" << endl;
-			for ( uint i = 0; i < it->triangles.size(); ++i ) {
-				out << "      " << i + 1 << ":  " << setw(10) << it->triangles[i].v1 << "," << setw(10) << it->triangles[i].v2 << "," << setw(10) << it->triangles[i].v3 << endl;
-			}
-
-			out << "   Bone Indices:" << endl;
-			for ( uint i = 0; i < it->boneIndices.size(); ++i ) {
-				out << "Group " << i + 1 << ":" << endl;
-				for ( uint j = 0; j < it->boneIndices[i].size(); ++j ) {
-					out << "         " << j + 1 << ":  " << it->boneIndices[i][j] << endl;
-				}
-			}
-		} else {
-			out << "   <<Data Not Shown>>" << endl;
-		}
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiStringPalette methods
- **********************************************************/
-
-void NiStringPalette::Read( istream& file, unsigned int version ) {
-
-	//GetAttr("Palette")->Read( file, version );
-
-	//Read extra length and throw it away
-	ReadUInt( file );
-}
-
-void NiStringPalette::Write( ostream& file, unsigned int version ) const {
-
-	attr_ref pal_attr = GetAttr("Palette");
-	//pal_attr->Write( file, version );
-	string pal_str = pal_attr->asString();
-
-	//Write length of string again
-	WriteUInt( uint(pal_str.size()), file );
-}
-
-
-string NiStringPalette::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	string pal = GetAttr("Palette")->asString();
-
-	////Replace 0's with newlines
-	//replace( pal.begin(), pal.end(), 0x00, 0x0A );
-
-	//out << "String Palette:  " << endl
-	//	<< pal << endl;
-
-	out << "String Palette:" << endl
-		<< "   0:  ";
-
-	if ( pal.size() > 0 ) for ( uint i = 0; i < pal.size() - 1; ++i ) {
-		if ( pal[i] == '\0') {
-			out << endl << "   " << i + 1 << ":  ";
-		} else {
-			out << pal[i];
-		}
-	}
-	out << endl;
-
-	return out.str();
-}
-
-
-/***********************************************************
- * NiPixelData methods
- **********************************************************/
-
-void NiPixelData::Read( istream& file, unsigned int version ) {
-	//ABlock::Read( in, version );
-
-	pxFormat = PixelFormat( ReadUInt(file) );
-
-	//This data only exists before version 20.0.0.4
-	if ( version < VER_20_0_0_4 ) {
-		NifStream( redMask, file );
-		NifStream( greenMask, file );
-		NifStream( blueMask, file );
-		NifStream( alphaMask, file );
-
-		NifStream( bpp, file );
-
-		for ( int i = 0; i < 8; ++i ) {
-			NifStream( unk8Bytes[i], file );
-		}
-
-		//There is an unknown int here from version 10.1.0.0 to 10.2.0.0
-		if ( version >= VER_10_1_0_0 ) {
-			NifStream( unkInt, file );
-		}
-	} else {
-		//After version 20.0.0.4 there are 54 unknown bytes here
-		file.read( (char*)unk54Bytes, 54 );
-	}
-
-	//GetAttr("Palette")->Read( file, version );
-
-	uint mipCount = ReadUInt( file );
-
-
-
-	//Read Bytes per pixel and discard
-	ReadUInt( file );
-
-	mipmaps.resize( mipCount );
-	for ( uint i = 0; i < mipCount; ++i ) {
-		mipmaps[i].width = ReadUInt( file );
-		mipmaps[i].height = ReadUInt( file );
-		mipmaps[i].offset = ReadUInt( file );
-	}
-
-	dataSize = ReadUInt( file );
-	data = new byte[dataSize];
-
-	//After version 20.0.0.4 there is an unknown int here
-	if ( version >= VER_20_0_0_4 ) {
-		NifStream( unkInt2, file );
-	}
-	
-	file.read( (char *)data, dataSize);
-}
-
-void NiPixelData::Write( ostream& file, unsigned int version ) const {
-	//ABlock::Write( file, version );
-
-	WriteUInt( uint(pxFormat), file );
-
-	//This data only exists before version 20.0.0.4
-	if ( version < VER_20_0_0_4 ) {
-		NifStream( redMask, file );
-		NifStream( greenMask, file );
-		NifStream( blueMask, file );
-		NifStream( alphaMask, file );
-
-		NifStream( bpp, file );
-
-		for ( int i = 0; i < 8; ++i ) {
-			NifStream( unk8Bytes[i], file );
-		}
-		
-		//There is an unknown int here from version 10.1.0.0 to version 10.2.0.0
-		if ( version >= VER_10_1_0_0 ) {
-			NifStream( unkInt, file );
-		}
-	}
-
-	//GetAttr("Palette")->Write( file, version );
-
-	//If there is no data stored, then there are no mipmaps.
-	if ( dataSize > 0 ) {
-		WriteUInt( uint(mipmaps.size()), file );
-	} else {
-		WriteUInt( 0, file );
-	}
-
-	WriteUInt( bpp / 8, file );
-
-	//If there is no data stored, then there are no mipmaps.
-	if ( dataSize > 0 ) {
-		for ( uint i = 0; i < mipmaps.size(); ++i ) {
-			WriteUInt( mipmaps[i].width, file );
-			WriteUInt( mipmaps[i].height, file );
-			WriteUInt( mipmaps[i].offset, file );
-		}
-	}
-
-	WriteUInt( dataSize, file );
-
-	//After version 20.0.0.4 there is an unknown int here
-	if ( version >= VER_20_0_0_4 ) {
-		NifStream( unkInt2, file );
-	}
-
-	file.write( (char *)data, dataSize);
-}
-
-string NiPixelData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	//out << ABlock::asString();
-
-	out << "Pixel Format:  ";
-	switch (pxFormat) {
-		case PX_FMT_RGB8:
-			out << "0 - PX_FMT_RGB8" << endl;
-			break;
-		case PX_FMT_RGBA8:
-			out << "1 - PX_FMT_RGBA8" << endl;
-			break;
-		case PX_FMT_PAL8:
-			out << "2 - PX_FMT_PAL8" << endl;
-			break;
-		default:
-			out << uint(pxFormat) << " - ???" << endl;
-	}
-	
-	out << "Red Mask:  " << redMask << endl
-		<< "Green Mask:  " << greenMask << endl
-		<< "Blue Mask:  " << blueMask << endl
-		<< "Alpha Mask:  " << alphaMask << endl
-		<< "Bits Per Pixel:  " << bpp << endl
-		<< "Mipmap Count:  " << uint(mipmaps.size()) << endl
-		<< "Bytes Per Pixel:  " << bpp / 8 << endl
-		<< "Unknown 8 Bytes:" << endl;
-
-	for ( int i = 0; i < 8; ++i ) {
-		out << i + 1 << ":  " << int(unk8Bytes[i]) << endl;
-	}
-
-	out << "Unknown Int:  " << unkInt << endl
-		<< "Unknown 54 Bytes:" << endl;
-
-	if (verbose) {
-		out << HexString( unk54Bytes, 54 );
-	} else {
-		out << "   <<<Data Not Shown>>>";
-	}
-
-    out << "Palette:  "  << GetAttr("Palette")->asLink() << endl;
-
-	for ( uint i = 0; i < mipmaps.size(); ++i ) {
-		out << "Mipmap " << i + 1 << ":" << endl
-			<< "   Width:  "  << mipmaps[i].width << endl
-			<< "   Height:  " << mipmaps[i].height << endl
-			<< "   Offset into Image Data Block:  " << mipmaps[i].offset << endl;
-	
-#ifdef IM_DEBUG
-		switch ( pxFormat ) {
-			case PX_FMT_RGB8:
-				imdebug("rgb w=%d h=%d %p", mipmaps[i].width, mipmaps[i].height, &data[mipmaps[i].offset] );
-				break;
-			case PX_FMT_RGBA8:
-				imdebug("rgba w=%d h=%d %p", mipmaps[i].width, mipmaps[i].height, &data[mipmaps[i].offset] );
-				break;
-			//case 4:
-			//	byte * img = new byte[ mipmaps[i].width * mipmaps[i].height ];
-			//	for ( uint j = 0; j < mipmaps[i].width * mipmaps[i].height / 2; ++j ) {
-			//		byte temp = data[mipmaps[i].offset + j];
-
-			//		img[j*2] = (temp & 0x0F) * 0x11;
-			//		img[j*2+1] = (temp & byte(0xF0) >> 4) * 0x11; 
-
-			//		//cout << "Pixel " << j*2 << ":  " << setbase(16) << int(img[j*2]) << endl;
-			//		//cout << "Pixel " << j*2+1 << ":  " << setbase(16) << int(img[j*2+1]) << endl;
-			//	};
-			//	imdebug("lum w=%d h=%d %p", mipmaps[i].width, mipmaps[i].height, img );
-
-			//	delete [] img;
-			//	break;
-		}
-
-		//cout << "Displaying NiPixelData Image.  Press Enter to continue." << endl;
-		//cin.get();
-#endif
-	}
-
-	out << "Unknown Int 2:  " << unkInt2 << endl
-		<< "Mipmap Image Data:  "  << dataSize << " Bytes (Not Shown)" << endl;
-	
-	return out.str();
-}
-
-int NiPixelData::GetHeight() const {
-	if ( mipmaps.size() == 0 ) {
-		return 0;
-	} else {
-		return mipmaps[0].height;
-	}
-}
-
-int NiPixelData::GetWidth() const {
-	if (mipmaps.size() == 0 ) {
-		return 0;
-	} else {
-		return mipmaps[0].width;
-	}
-}
-
-PixelFormat NiPixelData::GetPixelFormat() const {
-	return pxFormat;
-}
-
-void NiPixelData::Reset( int new_width, int new_height, PixelFormat px_fmt ) {
-	//Ensure that texture dimentions are powers of two
-	if ( (new_height & (new_height-1)) != 0 ) {
-		throw runtime_error("Texture height must be a power of two.  1, 2, 4, 8, 16, 32, 64, 256, 512, etc.");
-	}
-
-	if ( (new_width & (new_width-1)) != 0 ) {
-		throw runtime_error("Texture width must be a power of two.  1, 2, 4, 8, 16, 32, 64, 256, 512, etc.");
-	}
-	
-	//Delete any data that was previously held
-	if ( data != NULL ) {
-		delete [] data;
-		data = NULL;
-		dataSize = 0;
-	}
-
-	dataSize = 0;
-	mipmaps.resize(1);
-
-
-	//Set up first mipmap
-	mipmaps[0].width = new_width;
-	mipmaps[0].height = new_height;
-	mipmaps[0].offset = 0;
-
-	//Set up pixel format fields
-	pxFormat = px_fmt;
-	switch(pxFormat) {
-		case PX_FMT_RGB8:
-			redMask    = 0x000000FF;
-			greenMask  = 0x0000FF00;
-			blueMask   = 0x00FF0000;
-			alphaMask  = 0x00000000;
-			bpp = 24;
-			unk8Bytes[0] = 96;
-			unk8Bytes[1] = 8;
-			unk8Bytes[2] = 130;
-			unk8Bytes[3] = 0;
-			unk8Bytes[4] = 0;
-			unk8Bytes[5] = 65;
-			unk8Bytes[6] = 0;
-			unk8Bytes[7] = 0;
-			break;
-		case PX_FMT_RGBA8 :
-			redMask    = 0x000000FF;
-			greenMask  = 0x0000FF00;
-			blueMask   = 0x00FF0000;
-			alphaMask  = 0xFF000000;
-			bpp = 32;
-			unk8Bytes[0] = 129;
-			unk8Bytes[1] = 8;
-			unk8Bytes[2] = 130;
-			unk8Bytes[3] = 32;
-			unk8Bytes[4] = 0;
-			unk8Bytes[5] = 65;
-			unk8Bytes[6] = 12;
-			unk8Bytes[7] = 0;
-			break;	
-		case PX_FMT_PAL8 :
-			redMask   = 0x00000000;
-			blueMask  = 0x00000000;
-			greenMask = 0x00000000;
-			alphaMask = 0x00000000;
-			bpp = 8;
-			unk8Bytes[0] = 34;
-			unk8Bytes[1] = 0;
-			unk8Bytes[2] = 0;
-			unk8Bytes[3] = 32;
-			unk8Bytes[4] = 0;
-			unk8Bytes[5] = 65;
-			unk8Bytes[6] = 12;
-			unk8Bytes[7] = 0;
-			break;	
-		//[4,0,0,0,0,0,0,0] if 0 (?) bits per pixel
-		default:
-			throw runtime_error("The pixel type you have requested is not currently supported.");
-	}
-}
-
-vector<Color4> NiPixelData::GetColors() const {
-	vector<Color4> pixels;
-
-	if ( mipmaps.size() == 0 ) {
-		//Return empty vector
-		return pixels;
-	}
-
-	//Pack the pixel data from the first mipmap into a vector of
-	//Color4 based on the pixel format.
-	pixels.resize( mipmaps[0].width * mipmaps[0].height );
-	switch(pxFormat) {
-		case PX_FMT_RGB8:
-			for ( uint i = 0; i < pixels.size(); ++i ) {
-				pixels[i].r = float(data[i * 3]) / 255.0f;
-				pixels[i].g = float(data[i * 3 + 1]) / 255.0f;
-				pixels[i].b = float(data[i * 3 + 2]) / 255.0f;
-				pixels[i].a = 1.0f;
-			}
-			break;
-		case PX_FMT_RGBA8:
-			for ( uint i = 0; i < pixels.size(); ++i ) {
-				pixels[i].r = float(data[i * 4]) / 255.0f;
-				pixels[i].g = float(data[i * 4 + 1]) / 255.0f;
-				pixels[i].b = float(data[i * 4 + 2]) / 255.0f;
-				pixels[i].a = float(data[i * 4 + 3]) / 255.0f;
-			}
-			break;
-		default:
-			throw runtime_error("The GetColors function only supports the PX_FMT_RGB8 and PX_FMT_RGBA8 pixel formats.");
-	}
-
-#ifdef IM_DEBUG
-
-	imdebug("rgba b=32f rs=2 w=%d h=%d %p", mipmaps[0].width, mipmaps[0].height, &pixels[0] );
-	//delete [] img;
-	cout << "Showing image returned by GetColors function." << endl;
-	cin.get();
-#endif
-
-	return pixels;
-}
-
-void NiPixelData::SetColors( const vector<Color4> & new_pixels, bool generate_mipmaps ) {
-	//Ensure that compatible pixel format is being used
-	if ( pxFormat != PX_FMT_RGB8 && pxFormat != PX_FMT_RGBA8 ) {
-		throw runtime_error("The SetColors function only supports the PX_FMT_RGB8 and PX_FMT_RGBA8 pixel formats.");
-	}
-
-	//Ensure that there is size information in the mipmaps
-	if ( mipmaps.size() == 0 ) {
-		throw runtime_error("The size informatoin has not been set.  Call the IPixelData::Reset() function first.");
-	}
-
-	//Ensure that the right number of pixels for the dimentions set have been passed
-	if ( new_pixels.size() != mipmaps[0].height * mipmaps[0].width ) {
-		throw runtime_error("You must pass one color for every pixel in the image.  There should be height * width colors.");
-	}
-
-	uint size = 0;
-	mipmaps.resize(1);
-	size = (mipmaps[0].height * mipmaps[0].width * bpp) / 8;
-
-	//Deal with multiple mipmaps
-	if ( generate_mipmaps == true ) {
-		MipMap m;
-		m.height = mipmaps[0].height;
-		m.width = mipmaps[0].width;
-
-		size = (mipmaps[0].height * mipmaps[0].width * bpp) / 8;
-
-		while ( m.width != 1 && m.height != 1 ) {
-			////cout << "Width:  " << m.width << "  Height:  " << m.height << "  Offset:  " << m.offset << endl;
-			m.width /= 2;
-			m.height /= 2;
-			m.offset = size;
-
-			size += (m.height * m.width * bpp) / 8;
-
-			mipmaps.push_back(m);
-		}
-	}
-
-	//Allocate space to store mipmaps
-	if ( data != NULL ) {
-		delete [] data;
-	}
-
-	dataSize = size * bpp / 8;
-	data = new byte[dataSize];
-
-	//Copy pixels to Color4 C array
-	Color4 * tmp_image = new Color4[new_pixels.size()];
-
-	for (uint i = 0; i < new_pixels.size(); ++i ) {
-		tmp_image[i] = new_pixels[i];
-	}
-
-	//Pack pixel data
-	for (uint i = 0; i < mipmaps.size(); ++i ) {
-		cout << "Width:  " << mipmaps[i].width << "  Height:  " << mipmaps[i].height << "  Offset:  " << mipmaps[i].offset << endl;
-
-		if ( i > 0 ) {
-			//Allocate space to store re-sized image.
-			Color4 * resized = new Color4[ mipmaps[i].width * mipmaps[i].height ];
-
-			//Visit every other pixel in each row and column of the previous image
-			for ( uint w = 0; w < mipmaps[i-1].width; w+=2 ) {
-				for ( uint h = 0; h < mipmaps[i-1].height; h+=2 ) {
-					//cout << "w:  " << w << "  h:  " << h << endl;
-					Color4 & av = resized[(h/2) * mipmaps[i].width + (w/2)];
-
-					//Start with the value of the current pixel
-					av = tmp_image[h * mipmaps[i-1].width + w];
-					float num_colors = 1.0f;
-
-					//Only process the pixel above if height is > 1
-					if ( h > 1 ) {
-						Color4 & px = tmp_image[(h+1) * mipmaps[i-1].width + w];
-						av.r += px.r;
-						av.g += px.g;
-						av.b += px.b;
-						av.a += px.a;
-						num_colors += 1.0f;
-					}
-
-					//Only process the pixel to the right if width > 1
-					if (w > 1 ) {
-						Color4 & px = tmp_image[h * mipmaps[i-1].width + (w+1)];
-						av.r += px.r;
-						av.g += px.g;
-						av.b += px.b;
-						av.a += px.a;
-						num_colors += 1.0f;
-					}
-
-					//Only process the pixel to the upper right if both width and height are > 1
-					if ( w > 1 && h >> 1 ) {
-						Color4 & px = tmp_image[(h+1) * mipmaps[i-1].width + (w+1)];
-						av.r += px.r;
-						av.g += px.g;
-						av.b += px.b;
-						av.a += px.a;
-						num_colors += 1.0f;
-					}
-
-					//Calculate average
-					av.r /= num_colors;
-					av.g /= num_colors;
-					av.b /= num_colors;
-					av.a /= num_colors;
-				}
-			}
-			//Resize is complete, set result to tmp_image
-
-			//delete old tmp_image data
-			delete [] tmp_image;
-
-			//Adjust pointer values
-			tmp_image = resized;
-			resized = NULL;
-		}
-
-		//Data is ready to be packed into the byes of this mipmap
-
-		#ifdef IM_DEBUG
-			cout << "Showing mipmap size " << mipmaps[i].width << " x " << mipmaps[i].height << "." << endl;
-			imdebug("rgba b=32f w=%d h=%d %p", mipmaps[i].width, mipmaps[i].height, &tmp_image[0] );
-			cin.get();
-		#endif
-
-		//Start at offset
-		byte * map = &data[mipmaps[i].offset];
-
-		switch(pxFormat) {
-		case PX_FMT_RGB8:
-			for ( uint j = 0; j < mipmaps[i].width * mipmaps[i].height; ++j ) {
-				map[j * 3] = int( tmp_image[j].r * 255.0f );
-				map[j * 3 + 1] = int( tmp_image[j].g * 255.0f );
-				map[j * 3 + 2] = int( tmp_image[j].b * 255.0f );
-			}
-
-			//#ifdef IM_DEBUG
-			//	cout << "Showing mipmap after being packed  - size " << mipmaps[i].width << " x " << mipmaps[i].height << "." << endl;
-			//	imdebug("rgb w=%d h=%d %p", mipmaps[i].width, mipmaps[i].height, &map[0] );
-			//	cin.get();
-			//#endif
-			break;
-		case PX_FMT_RGBA8:
-			for ( uint j = 0; j < mipmaps[i].width * mipmaps[i].height; ++j ) {
-				map[j * 4] = int( tmp_image[j].r * 255.0f );
-				map[j * 4 + 1] = int( tmp_image[j].g * 255.0f );
-				map[j * 4 + 2] = int( tmp_image[j].b * 255.0f );
-				map[j * 4 + 3] = int( tmp_image[j].a * 255.0f );
-			}
-
-			//#ifdef IM_DEBUG
-			//	cout << "Showing mipmap after being packed  - size " << mipmaps[i].width << " x " << mipmaps[i].height << "." << endl;
-			//	imdebug("rgba w=%d h=%d %p", mipmaps[i].width, mipmaps[i].height, &map[0] );
-			//	cin.get();
-			//#endif
-			break;
-		case PX_FMT_PAL8:
-			throw runtime_error("The SetColors function only supports the PX_FMT_RGB8 and PX_FMT_RGBA8 pixel formats.");
-			break;
-		}
-	}
-}
-
-//enum PixelFormat {
-//	PX_FMT_RGBA8 = 0, /*!< 32-bit color with alpha: uses 8 bits to store each red, blue, green, and alpha component. */
-//	PX_FMT_RGB8 = 1, /*!< 24-bit color: uses 8 bit to store each red, blue, and green component. */
-//	PX_FMT_PAL8 = 2 /*!< 8-bit palette index: uses 8 bits to store an index into the palette stored in a NiPallete block. */
-//};
-
-/***********************************************************
- * NiPosData methods
- **********************************************************/
-
-void NiPosData::Read( istream& file, unsigned int version ) {
-	uint keyCount = ReadUInt( file );
-	if ( keyCount > 0 ) {
-		NifStream( _type, file );
-
-		_keys.resize( keyCount );
-		for (uint i = 0; i < _keys.size(); i++) {
-			NifStream( _keys[i], file, _type );
-		}
-	}
-}
-
-void NiPosData::Write( ostream& file, unsigned int version ) const {
-	WriteUInt( uint(_keys.size()), file );
-	if ( _keys.size() > 0 ) {
-		NifStream( _type, file );
-
-		for (uint i = 0; i < _keys.size(); i++) {
-			NifStream( _keys[i], file, _type );
-		}
-}
-}
-
-string NiPosData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Key Count:  " << uint(_keys.size()) << endl
-		<< "Key Type:  " << _type << endl;
-
-	if (verbose) {
-		vector< Key<Vector3> >::const_iterator it;
-		for ( it = _keys.begin(); it != _keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Position:  " << it->data.x << ", " << it->data.y << ", " << it->data.z << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiTextKeyExtraData methods
- **********************************************************/
-
-void NiTextKeyExtraData::Read( istream& file, unsigned int version ) {
-	/*GetAttr("Name")->Read( file, version );
-	GetAttr("Next Extra Data")->Read( file, version );
-	*/
-	AExtraData::Read( file, version );
-	//GetAttr("Unknown Int")->Read( file, version );
-
-	uint keyCount = ReadUInt( file );
-
-	_keys.resize( keyCount );
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, LINEAR_KEY );
-	}
-}
-
-void NiTextKeyExtraData::Write( ostream& file, unsigned int version ) const {
-
-	/*GetAttr("Name")->Write( file, version );
-	GetAttr("Next Extra Data")->Write( file, version );
-	*/
-	AExtraData::Write( file, version );
-	//GetAttr("Unknown Int")->Write( file, version );
-
-	WriteUInt( uint(_keys.size()), file );
-
-	for (uint i = 0; i < _keys.size(); i++) {
-		NifStream( _keys[i], file, LINEAR_KEY );
-	}
-}
-
-string NiTextKeyExtraData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Name:  " << GetAttr("Name")->asString() << endl
-		//<< "Next Extra Data:  " << GetAttr("Next Extra Data")->asString() << endl
-		<< "Unknown Int:  " << GetAttr("Unknown Int")->asString() << endl
-		<< "Key Count:  " << uint(_keys.size()) << endl;
-
-	if (verbose) {
-		vector< Key<string> >::const_iterator it;
-		for ( it = _keys.begin(); it != _keys.end(); ++it ) {
-			out << "Key Time:  " <<  it->time << "  Key Text:  " << it->data << endl;
-		}
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiUVData methods
- **********************************************************/
-
-void NiUVData::Read( istream& in, unsigned int version ) {	
-	for (uint i = 0; i < 4; ++i) {
-		uint count = ReadUInt( in );
-
-		if ( count > 0 ) {
-			groups[i].keyType = ReadUInt( in );
-
-			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 );
-
-				if ( groups[i].keyType == 2) {
-					groups[i].keys[j].forward_tangent = ReadFloat( in );
-					groups[i].keys[j].backward_tangent = ReadFloat( in );
-				}
-			}
-		}
-	}
-}
-
-void NiUVData::Write( ostream& out, unsigned int version ) const {
-	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() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	for (uint i = 0; i < 4; ++i) {
-		out << "UV Group " << i + 1 << ":" << endl
-			<< "   Key Count:  " << uint(groups[i].keys.size()) << endl;
-
-		if ( groups[i].keys.size() > 0 ) {
-			out << "   Key Type:  " << groups[i].keyType << endl;
-
-			if (verbose) {
-				for (uint j = 0; j < groups[i].keys.size(); ++j) {
-					out << "   Key Time:  " << groups[i].keys[j].time << " Data:  " << groups[i].keys[j].data;
-
-					if ( groups[i].keyType == 2) {
-						out << "  F: " << groups[i].keys[j].forward_tangent << "  B: " << groups[i].keys[j].backward_tangent;
-					}
-					out << endl;
-				}
-			} else {
-				out << "<<Data Not Shown>>" << endl;
-			}
-		}
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiVertWeightsExtraData methods
- **********************************************************/
- 
-void NiVertWeightsExtraData::Read( istream& in, unsigned int version ) {
-	AExtraData::Read( in, version );
-
-	//Read byte count but throw it away
-	ReadUInt( in );
-
-	ushort verts = ReadUShort( in );
-
-	weights.resize( verts );
-	for (uint i = 0; i < weights.size(); ++i) {
-		weights[i] = ReadFloat( in );
-	}
-}
-
-void NiVertWeightsExtraData::Write( ostream& out, unsigned int version ) const {
-	AExtraData::Write( out, version );
-
-	uint bytes = 2 + 4 * uint(weights.size());
-	WriteUInt( bytes, out );
-	WriteUShort( ushort(weights.size()), out );
-
-	for (uint i = 0; i < weights.size(); ++i) {
-		 WriteFloat( weights[i], out );
-	}
-}
-
-string NiVertWeightsExtraData::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << AExtraData::asString();
-
-	out << "Bytes:  " << bytes << endl
-		<< "Verts:  " << uint(weights.size()) << endl;
-
-	if (verbose) {
-		for (uint i = 0; i < weights.size(); ++i) {
-			out << "Weight " << i + 1 << ":  " << weights[i] << endl;
-		}		
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiVisData methods
- **********************************************************/
-
-void NiVisData ::Read( istream& in, unsigned int version ) {
-	uint keyCount = ReadUInt( in );
-
-	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( ostream& out, unsigned int version ) const {
-	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() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Key Count:  "  << uint(keys.size()) << endl;
-
-	if (verbose) {
-		for (uint i = 0; i < keys.size(); ++i) {
-			out << "Key Time:  " << keys[i].time << "  Visible:  " << int(keys[i].data) << endl;
-		}		
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-/***********************************************************
- * NiLookAtInterpolator methods
- **********************************************************/
-
-//void NiLookAtInterpolator::Read( istream& file, unsigned int version ) {
-//	
-//	GetAttr("Unknown Short")->Read( file, version );
-//	
-//	//Float Array
-//	uint numFloats = ReadUInt( file );
-//	unkFloats.resize( numFloats );
-//	NifStream( unkFloats, file );
-//	
-//	GetAttr("Unknown Link")->Read( file, version );
-//
-//	//Byte Array
-//	for (uint i = 0; i < 8; ++i ) {
-//		NifStream( unkBytes[i], file );
-//	}
-//}
-//
-//void NiLookAtInterpolator::Write( ostream& file, unsigned int version ) const {
-//
-//}
-//
-//string NiLookAtInterpolator::asString() const {
-//	stringstream out;
-//	out.setf(ios::fixed, ios::floatfield);
-//	out << setprecision(1);
-//
-//	return out.str();
-//}
-
-
-/***********************************************************
- * UnknownMixIn methods
- **********************************************************/
-
-void UnknownMixIn::Read( istream &in, unsigned int version ) {
-	len = BlockSearch(in);
-
-	//Create byte array and read in unknown block
-	data = new byte [len];
-	in.read((char*)data, len);
-}
-
-string UnknownMixIn::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	out << "Unknown Data (" << len << " bytes):" << endl;
-	
-	if (verbose) {
-		//Display Data in Hex form
-		out << hex << setfill('0');
-		for (int j = 0; j < len; j++) {
-			out << uppercase << setw(2) << uint(data[j]);
-			if (j % 16 == 15 || j == len - 1)
-				out << endl;
-			else if (j % 16 == 7)
-				out << "   ";
-			else if (j % 8 == 3)
-				out << "  ";
-			else
-				out << " ";
-		}
-		out << dec << setfill(' ');
-	} else {
-		out << "<<Data Not Shown>>" << endl;
-	}
-
-	return out.str();
-}
-
-void UnknownMixIn::Write( ostream& out, unsigned int version ) const {
-	out.write( (const char*)data, len );
-}
-
-//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;
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
deleted file mode 100644
index a6cb046b7714344cf0f4bf3dc53e26fd493f7b46..0000000000000000000000000000000000000000
--- a/NIF_Blocks.h
+++ /dev/null
@@ -1,3130 +0,0 @@
-/* Copyright (c) 2005, NIF File Format Library and Tools
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-   * Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-   * Redistributions in binary form must reproduce the above
-     copyright notice, this list of conditions and the following
-     disclaimer in the documentation and/or other materials provided
-     with the distribution.
-
-   * Neither the name of the NIF File Format Library and Tools
-     project nor the names of its contributors may be used to endorse
-     or promote products derived from this software without specific
-     prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef _NIF_BLOCKS_H
-#define _NIF_BLOCKS_H
-
-/* INCLUDES */
-#include "NiObject.h"
-#include "obj\NiObject.h"
-#include "NIF_IO.h"
-
-#include <vector>
-#include <list>
-#include <map>
-#include <cmath>
-#include <sstream>
-
-//--Types--//
-//typedef	pair<attr_ref, blk_ref> LinkPair;
-//typedef multimap<attr_ref, blk_ref> LinkMap;
-//typedef LinkMap::iterator LinkMapIt;
-//typedef pair<LinkMapIt,LinkMapIt> LinkMapRange;
-
-//--Constants--//
-
-//Non-Public Interface IDs
-
-//const int SkinInstInternal = -2;
-//const int SkinDataInternal = -3;
-
-class AControllable : public NiObject {
-public:
-	static const Type TYPE;
-	AControllable();
-	void Init() {}
-	bool IsControllable() const { return true; }
-	~AControllable() {}
-};
-
-const Type AControllable::TYPE("AControllable", &NiObject::TYPE);
-
-class ANode : public AControllable {
-public:
-	static const Type TYPE;
-	ANode();
-	void Init() { 
-		//Start the bind pose at an identity matrix
-		bindPosition = Matrix44::IDENTITY;
-
-		//Start the flags at "Not a skin influence"
-		//GetAttr("Flags")->Set(8);
-	
-	};
-	~ANode() {};
-	void InitAttrs();
-	void Read( istream& in, unsigned int version ) {
-		NiObject::Read( in, version );
-		Matrix44 transform;
-		transform = GetLocalTransform();
-		SetWorldBindPos( transform );
-	}
-
-	/*! 
-	 * This is a conveniance function that allows you to retrieve the full 4x4 matrix transform of a node.  It accesses the "Rotation," "Translation," and "Scale" attributes and builds a complete 4x4 transformation matrix from them.
-	 * \return A 4x4 transformation matrix built from the node's transform attributes.
-	 * \sa INode::GetWorldTransform
-	 */
-	Matrix44 GetLocalTransform() const;
-
-	/*! 
-	 * This function will return a transform matrix that represents the location of this node in world space.  In other words, it concatenates all parent transforms up to the root of the scene to give the ultimate combined transform from the origin for this node.
-	 * \return The 4x4 world transform matrix of this node.
-	 * \sa INode::GetLocalTransform
-	 */
-	Matrix44 GetWorldTransform() const;
-
-	/*!
-	 * This function returns the bind position world matrix.  The bind position (also called the rest position) is the position of an object in a skin and bones system before any posing has been done.
-	 * \return The 4x4 world bind position matrix of this node.
-	 * \sa INode::GetLocalBindPos, INode::SetWorldBindPos
-	 */
-	Matrix44 GetWorldBindPos() const;
-
-	/*! This function returns the bind position world matrix of this node multiplied with the inverse of the bind position world matrix of its parent object if any.  Thus it returns the bind position of the object in local coordinates.  The bind position (also called the rest position) is the position of an object in a skin and bones system before any posing has been done.
-	 * \return The 4x4 local bind position matrix of this node.
-	 * \sa INode::SetWorldBindPos, INode::GetWorldBindPos
-	 */
-	Matrix44 GetLocalBindPos() const;
-
-	/*!
-	 * This function sets the bind position of this object relative to the origin.  The bind position (also called the rest position) is the position of an object in a skin and bones system before any posing has been done.  This function must be called on every object in a skin and bones system (the bones and the skinned shapes) in order for skinning information to be written to a Nif file.
-	 * \param m The 4x4 world bind position matrix of this node
-	 * \sa INode::GetLocalBindPos, INode::GetWorldBindPos
-	 */
-	void SetWorldBindPos( Matrix44 const & m );
-
-	void IncCrossRef( IBlock * block );
-	void DecCrossRef( IBlock * block );
-
-protected:
-	void ResetSkinnedFlag();
-	Matrix44 bindPosition;
-};
-
-/**
- * AParentNode
- */
-class AParentNode : public ANode {
-public:
-	AParentNode();
-	void Init() {}
-	~AParentNode() {}
-};
-
-/**
- * AFx
- */
-class AFx : public AParentNode {
-public:
-
-	AFx();
-	void Init() {}
-	~AFx() {}
-};
-
-class AShape : public ANode {
-public:
-	AShape();
-	void Init() {}
-	~AShape() {}
-};
-
-class AParticleNode : public AShape {
-public:
-	AParticleNode();
-	void Init() {}
-	~AParticleNode() {}
-};
-
-class AProperty : public AControllable {
-public:
-	AProperty();
-	void Init() {}
-	~AProperty() {}
-};
-
-class AController : public NiObject {
-public:
-	AController();
-	void Init() {}
-	bool IsController() { return true; }
-	~AController() {}
-};
-
-class AData : public NiObject {
-public:
-	AData() {}
-	void Init() {}
-	~AData() {}
-};
-
-class AInterpolator : public NiObject {
-public:
-	AInterpolator();
-	void Init() {}
-	~AInterpolator() {}
-};
-
-class AParticleModifier : public NiObject {
-public:
-	AParticleModifier();
-	void Init() {}
-	~AParticleModifier() {}
-};
-
-class APSysModifier : public NiObject {
-public:
-	APSysModifier();
-	void Init() {}
-	~APSysModifier() {}
-};
-
-class APSysEmitter : public APSysModifier {
-public:
-	APSysEmitter();
-	void Init() {}
-	~APSysEmitter() {}
-};
-
-class APSysVolumeEmitter : public APSysEmitter {
-public:
-	APSysVolumeEmitter();
-	void Init() {}
-	~APSysVolumeEmitter() {}
-};
-
-class AExtraData : public AData {
-public:
-	AExtraData() {
-		//AddAttr( attr_string, "Name", VER_10_0_1_0 );
-		//AddAttr( attr_link, "Next Extra Data", 0, VER_4_2_2_0 );
-	}
-	~AExtraData() {};
-	void Read( istream& in, unsigned int version ) {
-		//GetAttr("Name")->Read( in, version );
-		//GetAttr("Next Extra Data")->Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		//GetAttr("Name")->Write( out, version );
-		//GetAttr("Next Extra Data")->Write( out, version );
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << "Name:  " << GetAttr("Name")->asString() << endl
-			<< "Next Extra Data:  " << GetAttr("Next Extra Data")->asString() << endl;
-
-		return out.str();
-	}
-};
-
-
-class AParticleSystemController : public AController {
-public:
-	AParticleSystemController();
-	void Init() {}
-	~AParticleSystemController() {}
-};
-
-class ADynamicEffect   : public ANode {
-public:
-	ADynamicEffect();
-	void Init() {}
-	~ADynamicEffect() {}
-};
-
-class ALight   : public ADynamicEffect {
-public:
-	ALight();
-	void Init() {}
-	~ALight() {}
-};
-
-class APointLight   : public ALight {
-public:
-	APointLight();
-	void Init() {}
-	~APointLight() {}
-};
-
-/**
- * FxButton
- */
-class FxButton : public AFx {
-public:
-
-	FxButton();
-	void Init() {}
-	~FxButton() {}
-
-	string GetBlockType() const { return "FxButton"; }
-};
-
-/**
- * FxRadioButton
- */
-class FxRadioButton : public AFx {
-public:
-
-	FxRadioButton();
-	void Init() {}
-	~FxRadioButton() {}
-
-	string GetBlockType() const { return "FxRadioButton"; }
-};
-
-/**
- * FxWidget
- */
-class FxWidget : public AFx {
-public:
-
-	FxWidget();
-	void Init() {}
-	~FxWidget() {}
-
-	string GetBlockType() const { return "FxWidget"; }
-};
-
-/**
- * NiNode - Root of each model component.
- */
-class NiNode : public AParentNode {
-public:
-
-	NiNode();
-	void Init() {}
-	~NiNode() {}
-	string GetBlockType() const { return "NiNode"; }
-	string asString() const;
-};
-
-/**
- * RootCollisionNode
- */
-class RootCollisionNode : public AParentNode {
-public:
-
-	RootCollisionNode();
-	void Init() {}
-	~RootCollisionNode() {}
-
-	string GetBlockType() const { return "RootCollisionNode"; }
-};
-
-/**
- * AvoidNode
- */
-class AvoidNode : public AParentNode {
-public:
-
-	AvoidNode();
-	void Init() {}
-	~AvoidNode() {}
-
-	string GetBlockType() const { return "AvoidNode"; }
-};
-
-/**
- * NiBillboardNode
- */
-class NiBillboardNode : public AParentNode {
-public:
-	NiBillboardNode();
-	void Init() {}
-	~NiBillboardNode() {}
-
-	string GetBlockType() const { return "NiBillboardNode"; }
-};
-
-/**
- * NiBSAnimationNode
- */
-class NiBSAnimationNode : public AParentNode {
-public:
-	NiBSAnimationNode();
-	void Init() {}
-	~NiBSAnimationNode() {}
-
-	string GetBlockType() const { return "NiBSAnimationNode"; }
-};
-
-/**
- * NiBSParticleNode
- */
-class NiBSParticleNode : public AParentNode {
-public:
-	NiBSParticleNode();
-	void Init() {}
-	~NiBSParticleNode() {}
-
-	string GetBlockType() const { return "NiBSParticleNode"; }
-};
-
-/**
- * NiLODNode
- */
-class NiLODNode : public AParentNode {
-public:
-	NiLODNode();
-	void Init() {}
-	~NiLODNode() {}
-
-	string GetBlockType() const { return "NiLODNode"; }
-};
-
-/**
- * ZBuffer data.
- */
-class NiZBufferProperty : public AProperty {
-public:
-	NiZBufferProperty();
-	void Init() {}
-	~NiZBufferProperty() {}
-
-	string GetBlockType() const { return "NiZBufferProperty"; }
-};
-
-/**
- * NiShadeProperty
- */
-class NiShadeProperty : public AProperty {
-public:
-
-	NiShadeProperty();
-	void Init() {}
-	~NiShadeProperty() {}
-
-	string GetBlockType() const { return "NiShadeProperty"; }
-};
-
-/**
- * ZBuffer data.NiWireframeProperty
- */
-class NiWireframeProperty : public AProperty {
-public:
-
-	NiWireframeProperty();
-	void Init() {}
-	~NiWireframeProperty() {}
-
-	string GetBlockType() const { return "NiWireframeProperty"; }
-};
-
-/**
- * NiDitherProperty
- */
-class NiDitherProperty : public AProperty {
-public:
-
-	NiDitherProperty();
-	void Init() {}
-	~NiDitherProperty() {}
-
-	string GetBlockType() const { return "NiDitherProperty"; }
-};
-
-/**
- * NiFogProperty
- */
-class NiFogProperty : public AProperty {
-public:
-
-	NiFogProperty();
-	void Init() {}
-	~NiFogProperty() {}
-
-	string GetBlockType() const { return "NiFogProperty"; }
-};
-
-/**
- * NiSequenceStreamHelper 
- */
-class NiSequenceStreamHelper  : public AControllable {
-public:
-
-	NiSequenceStreamHelper ();
-	void Init() {}
-	~NiSequenceStreamHelper () {}
-
-	string GetBlockType() const { return "NiSequenceStreamHelper"; }
-};
-
-/**
- * NiVertexColorProperty - Vertex colour data.
- */
-class NiVertexColorProperty : public AProperty{
-public:
-
-	NiVertexColorProperty();
-	void Init() {}
-	~NiVertexColorProperty() {}
-
-	string GetBlockType() const { return "NiVertexColorProperty"; }
-};
-
-
-
-/**
- * NiTriShape - 
- */
-class NiTriShape : public AShape {
-public:
-	NiTriShape();
-	void Init() {}
-	~NiTriShape() {}
-
-	string GetBlockType() const { return "NiTriShape"; }
-};
-
-/**
- * NiTriStrips - 
- */
-class NiTriStrips : public AShape {
-public:
-	NiTriStrips();
-	void Init() {}
-	~NiTriStrips() {}
-
-	string GetBlockType() const { return "NiTriStrips"; }
-};
-
-/**
- * NiTexturingProperty - references all textures attatched to meshes which include it in their property list.
- */
-class NiTexturingProperty : public AProperty {
-public:
-	NiTexturingProperty( ) { //AddAttr( attr_flags, "Flags", 0, VER_10_0_1_0 ); 
-	}
-	void Init() {}
-	~NiTexturingProperty();
-	string GetBlockType() const { return "NiTexturingProperty"; }
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	void FixLinks( const vector<blk_ref> & blocks );
-	list<blk_ref> GetLinks() const;
-
-	/*! Retrieves the number of texture slots defined by this texturing propery.  Texture slots may or may not actually contain textures, but each slot has a different meaning so the way a texture is used is dependant upon which slot it is in.
-	 * \return The number of texture slots defined by this texturing property.
-	 * \sa ITexturingProperty::SetTextureCount
-	 */
-	int GetTextureCount() const { return int(textures.size()); }
-
-	/*! Sets the number of texture slots defined by this texturing propery.  Known valid values are 7 and 8.
-	 * \param n The new size of the texture slot array.
-	 * \sa ITexturingProperty::GetTextureCount
-	 */
-	void SetTextureCount( int new_count );
-
-	/*! Retrieves the number of extra texture slots defined by this texturing propery.  These only exist in later Nif versions and their function is unknown.
-	 * \return The number of extra texture slots defined by this texturing property.
-	 * \sa ITexturingProperty::SetExtraTextureCount
-	 */
-	int GetExtraTextureCount() const { return int(extra_textures.size()); }
-
-	/*! Sets the number of extra texture slots defined by this texturing propery.  Often zero.
-	 * \param n The new size of the extra texture slot array.
-	 * \sa ITexturingProperty::GetExtraTextureCount
-	 */
-	void SetExtraTextureCount( int new_count );
-
-	/*! Retrieves the current apply mode for this texturing propery.  This enum value affects the way the textures will be drawn.
-	 * \return The current apply mode for this texturing property.
-	 * \sa ITexturingProperty::SetApplyMode
-	 */
-	ApplyMode GetApplyMode() const { return appl_mode; }
-
-	/*! Sets the current apply mode for this texturing propery.  This enum value affects the way the textures will be drawn.
-	 * \param new_val The new apply mode for this texturing property.
-	 * \sa ITexturingProperty::GetApplyMode
-	 */
-	void SetApplyMode( ApplyMode new_val ) { appl_mode = new_val; }
-
-	/*! Retrieves the texture desription structure that describes a texture by slot number.  The TexType enum is provided to make it easy to select the texture slot with the specific qualities that you want.
-	 * \param n The slot number of the texture to get the texture description of.  This is a positive zero based index that must be less than the value returned by ITexturingProperty::GetTextureCount.
-	 * \sa ITexturingProperty::SetTexture, TexType
-	 */
-	TexDesc GetTexture( int n ) const { return textures[n]; }
-
-	/*! Sets a new description for the texture in the given slot number.  The TexType enum is provided to make it easy to select the texture slot with the specific qualities that you want.
-	 * \param n The slot number of the texture to set the texture description of.  This is a positive zero based index that must be less than the value returned by ITexturingProperty::GetTextureCount.
-	 * \param new_val Thew new texture descriptoin for the texture at the given slot number.
-	 * \sa ITexturingProperty::GetTexture, TexType
-	 */
-	void SetTexture( int n, TexDesc & new_val );
-
-	/*! Retrieves the texture desription structure that describes an extra texture by slot number.  These only exist in the later Nif versions and their function is unknown.
-	 * \param n The slot number of the extra texture to get the texture description of.  This is a positive zero based index that must be less than the value returned by ITexturingProperty::GetExtraTextureCount.
-	 * \sa ITexturingProperty::SetExtraTexture
-	 */
-	TexDesc GetExtraTexture( int n ) const { return extra_textures[n].first; }
-
-	/*! Sets a new description for the texture in the given slot number.  These only exist in the later Nif versions and their function is unknown.
-	 * \param n The slot number of the extra texture to set the texture description of.  This is a positive zero based index that must be less than the value returned by ITexturingProperty::GetTextureCount.
-	 * \param new_val Thew new texture descriptoin for the extra texture at the given slot number.
-	 * \sa ITexturingProperty::GetTexture, TexType
-	 */
-	void SetExtraTexture( int n, TexDesc & new_val );
-
-	/*! Retrieves the bump map luma offset.  This is only relevant if a texture is defined in the BUMP_MAP texture slot.  The function of this is unknown.
-	 * \return The bump map luma offset.
-	 * \sa ITexturingProperty::SetLumaOffset
-	 */
-	float GetLumaOffset() const { return bmLumaOffset; }
-
-	/*! Sets the bump map luma offset.  This is only relevant if a texture is defined in the BUMP_MAP texture slot.  The function of this is unknown.
-	 * \param new_val The new bump map luma offset.
-	 * \sa ITexturingProperty::GetLumaOffset
-	 */
-	void SetLumaOffset( float new_val ) { bmLumaOffset = new_val; }
-
-	/*! Retrieves the bump map luma scale.  This is only relevant if a texture is defined in the BUMP_MAP texture slot.  The function of this is unknown.
-	 * \return The bump map luma scale.
-	 * \sa ITexturingProperty::SetLumaScale
-	 */
-	float GetLumaScale() const { return bmLumaScale; }
-
-	/*! Sets the bump map luma scale.  This is only relevant if a texture is defined in the BUMP_MAP texture slot.  The function of this is unknown.
-	 * \param new_val The new bump map luma scale.
-	 * \sa ITexturingProperty::GetLumaScale
-	 */
-	void SetLumaScale( float new_val ) { bmLumaScale = new_val; }
-
-	/*! Retrieves the bump map matrix.  This is only relevant if a texture is defined in the BUMP_MAP texture slot.  The function of this is unknown.
-	 * \return the bump map matrix.
-	 * \sa ITexturingProperty::SetBumpMapMatrix
-	 */
-	Matrix22 GetBumpMapMatrix() const { return bmMatrix; }
-
-	/*! Sets the bump map matrix.  This is only relevant if a texture is defined in the BUMP_MAP texture slot.  The function of this is unknown.
-	 * \param new_val The new bump map matrix.
-	 * \sa ITexturingProperty::GetBumpMapMatrix
-	 */
-	void SetBumpMapMatrix( Matrix22 & new_val ) { bmMatrix = new_val; }
-
-private:
-	ApplyMode appl_mode;
-	vector<TexDesc> textures; //the main textures, base, gloss, glow, etc.
-	vector< pair<TexDesc,uint> > extra_textures; //extra texture group
-
-	//Bitmap info - only used if a bitmap texture is present
-	float bmLumaOffset; // The bitmap luma offset.  Unsure of function.
-	float bmLumaScale; // The bitmap luma scale.  Unsure of function.  
-	Matrix22 bmMatrix; // The bitmap 2x2 matrix.  Unsure of function.
-};
-
-/**
- * NiSourceTexture - Data for the associated texture, included in nif or external.
- */
-class NiSourceTexture : public AControllable{
-public:
-	NiSourceTexture();
-	void Init() {}
-	~NiSourceTexture() {}
-	string GetBlockType() const { return "NiSourceTexture"; }
-};
-
-
-/**
- * NiPixelData - Texture data for an included texture.
- */
-class NiPixelData : public AData {
-public:
-	NiPixelData() {
-		data = NULL;
-		dataSize = 0;
-		//AddAttr( attr_int, "Unknown Int" );
-		//AddAttr( attr_link, "Palette" );
-
-	}
-	~NiPixelData() { if (data != NULL) delete [] data; }
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiPixelData"; }
-
-	/*! Retrieves the height of the texture image stored in this block.
-	 * \return The height of the texture image stored in this block.
-	 * \sa IPixelData::GetWidth, IPixelData::GetPixelFormat
-	 */
-	int GetHeight() const;
-
-	/*! Retrieves the width of the texture image stored in this block.
-	 * \return The width of the texture image stored in this block.
-	 * \sa IPixelData::GetHeight, IPixelData::GetPixelFormat
-	 */
-	int GetWidth() const;
-
-    /*! Retrieves the pixel format of the texture image stored in this block.
-	 * \return The pixel format of the texture image stored in this block.
-	 * \sa IPixelData::GetWidth, IPixelData::GetHeight
-	 */
-	PixelFormat GetPixelFormat() const;
-
-    /*! Deletes all image data and sets a new size and format in preparation for new data to be provided.
-	 * \param new_width The width of the new texture image.
-	 * \param new_height The height of the new texture image.
-	 * \param px_fmt The pixel format of the new texture image.
-	 * \sa IPixelData::GetWidth, IPixelData::GetHeight
-	 */
-	void Reset( int new_width, int new_height, PixelFormat px_fmt );
-	
-	/*! Retrieves the the pixels of the texture image stored in this block.  This function does not work on palettized textures.
-	 * \return A vector containing the colors of each pixel in the texture image stored in this block, one row after another starting from the bottom of the image.  The width of the image must be used to interpret them correctly.
-	 * \sa IPixelData::SetColors, IPixelData::GetWidth
-	 */
-	vector<Color4> GetColors() const;
-
-	/*! Sets the the pixels of the texture image stored in this block and optionally generates mipmaps.  This function does not work for palettized textures.
-	 * \param new_pixels A vector containing the colors of each new pixel to be set in the texture image stored in this block, one row after another starting from the botom of the image.
-	 * \param generate_mipmaps If true, mipmaps will be generated for the new image and stored in the file.
-	 * \sa IPixelData::GetColors, IPixelData::GetWidth
-	 */
-	void SetColors( const vector<Color4> & new_pixels, bool generate_mipmaps );
-
-private:
-	struct MipMap {
-		uint width, height, offset;
-	};
-	
-	PixelFormat pxFormat;
-	uint redMask, blueMask, greenMask, alphaMask, bpp, unkInt;
-	byte unk8Bytes[8];
-	vector<MipMap> mipmaps;
-	uint dataSize;
-	byte * data;
-	byte unk54Bytes[54];
-	uint unkInt2;
-};
-
-/**
- * NiMaterialProperty - material properties
- */
-class NiMaterialProperty : public AProperty{
-public:
-	NiMaterialProperty();
-	void Init() {}
-	~NiMaterialProperty() {}
-	string GetBlockType() const { return "NiMaterialProperty"; };
-};
-
-/**
- * NiSpecularProperty -
- */
-class NiSpecularProperty : public AProperty {
-public:
-	NiSpecularProperty();
-	void Init() {}
-	~NiSpecularProperty() {}
-	string GetBlockType() const { return "NiSpecularProperty"; };
-};
-
-/**
- * NiStencilProperty -
- */
-class NiStencilProperty : public AProperty {
-public:
-	NiStencilProperty();
-	void Init() {}
-	~NiStencilProperty() {}
-	string GetBlockType() const { return "NiStencilProperty"; };
-};
-
-/**
- * NiAlphaProperty - Does the mesh have alpha-blending enabled?
- */
-class NiAlphaProperty : public AProperty {
-public:
-	NiAlphaProperty();
-	void Init() {}
-	~NiAlphaProperty() {}
-	string GetBlockType() const { return "NiAlphaProperty"; }
-};
-
-/**
- * AShapeData - Mesh data: vertices, vertex normals, etc.
- */
-class AShapeData : public AData {
-public:
-	AShapeData() {
-		//AddAttr( attr_string, "Name", VER_10_2_0_0 );
-		//AddAttr( attr_link, "Unknown Link", VER_20_0_0_4 );
-	}
-	~AShapeData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	//--Counts--//
-
-	/*! Returns the number of verticies that make up this mesh.  This is also the number of normals, colors, and UV coordinates if these are used.
-	 * \return The number of vertices that make up this mesh.
-	 * \sa IShapeData::SetVertexCount
-	 */
-	int GetVertexCount() const { return int(vertices.size()); }
-
-	/*! Returns the number of texture coordinate sets used by this mesh.  For each UV set, there is a pair of texture coordinates for every vertex in the mesh.  Each set corresponds to a texture entry in the NiTexturingPropery block.
-	 * \return The number of texture cooridnate sets used by this mesh.  Can be zero.
-	 * \sa IShapeData::SetUVSetCount, ITexturingProperty
-	 */
-	short GetUVSetCount() const { return short(uv_sets.size()); }
-
-	/*! Changes the number of vertices used by this mesh.  If the mesh already contains data, it will be retained so long as the new number is higher than the old number.  Otherwise any verticies above the new number will be deleted.  This also resizes any normal, color, or UV data associated with these verticies.  Triangles and triangle strips that may be attached via other interfaces are not culled of references to newly invalid vertices, however.
-	 * \param n The new size of the vertex array.
-	 * \sa IShapeData::GetVertexCount
-	 */
-	void SetVertexCount(int n);
-
-	/*! Changes the number of UV sets used by this mesh.  If he new size is smaller, data at the end of the array will be lost.  Otherwise it will be retained.  The number of UV sets must correspond with the number of textures defined in the corresponding NiTexturingProperty block.
-	 * \param n The new size of the uv set array.
-	 * \sa IShapeData::GetUVSetCount, ITexturingProperty
-	 */
-	void SetUVSetCount(int n);
-
-	//--Getters--//
-
-	/*! Used to retrive the vertices used by this mesh.  The size of the vector will be the same as the vertex count retrieved with the IShapeData::GetVertexCount function.
-	 * \return A vector cntaining the vertices used by this mesh.
-	 * \sa IShapeData::SetVertices, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	vector<Vector3> GetVertices() const { return vertices; }
-
-	/*! Used to retrive the normals used by this mesh.  The size of the vector will either be zero if no normals are used, or be the same as the vertex count retrieved with the IShapeData::GetVertexCount function.
-	 * \return A vector cntaining the normals used by this mesh, if any.
-	 * \sa IShapeData::SetNormals, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	vector<Vector3> GetNormals() const { return normals; }
-
-	/*! Used to retrive the vertex colors used by this mesh.  The size of the vector will either be zero if no vertex colors are used, or be the same as the vertex count retrieved with the IShapeData::GetVertexCount function.
-	 * \return A vector cntaining the vertex colors used by this mesh, if any.
-	 * \sa IShapeData::SetColors, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	vector<Color4> GetColors() const { return colors; }
-
-	/*! Used to retrive the texture coordinates from one of the texture sets used by this mesh.  The function will throw an exception if a texture set index that does not exist is specified.  The size of the vector will be the same as the vertex count retrieved with the IShapeData::GetVertexCount function.
-	 * \param index The index of the texture coordinate set to retrieve the texture coordinates from.  This index is zero based and must be a positive number smaller than that returned by the IShapeData::GetUVSetCount function.  If there are no texture coordinate sets, this function will throw an exception.
-	 * \return A vector cntaining the the texture coordinates used by the requested texture coordinate set.
-	 * \sa IShapeData::SetUVSet, IShapeData::GetUVSetCount, IShapeData::SetUVSetCount, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	vector<TexCoord> GetUVSet( int index ) const { return uv_sets[index]; }
-	
-//--Setters--//
-
-	/*! Used to set the vertex data used by this mesh.  The size of the vector must be the same as the vertex count retrieved with the IShapeData::GetVertexCount function or the function will throw an exception.
-	 * \param in A vector containing the vertices to replace those in the mesh with.  Note that there is no way to set vertices one at a time, they must be sent in one batch.
-	 * \sa IShapeData::GetVertices, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	void SetVertices( const vector<Vector3> & in );
-
-	/*! Used to set the normal data used by this mesh.  The size of the vector must either be zero, or the same as the vertex count retrieved with the IShapeData::GetVertexCount function or the function will throw an exception.
-	 * \param in A vector containing the normals to replace those in the mesh with.  Note that there is no way to set normals one at a time, they must be sent in one batch.  Use an empty vector to signify that this mesh will not be using normals.
-	 * \sa IShapeData::GetNormals, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	void SetNormals( const vector<Vector3> & in );
-
-	/*! Used to set the vertex color data used by this mesh.  The size of the vector must either be zero, or the same as the vertex count retrieved with the IShapeData::GetVertexCount function or the function will throw an exception.
-	 * \param in A vector containing the vertex colors to replace those in the mesh with.  Note that there is no way to set vertex colors one at a time, they must be sent in one batch.  Use an empty vector to signify that this mesh will not be using vertex colors.
-	 * \sa IShapeData::GetColors, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	void SetColors( const vector<Color4> & in );
-
-	/*! Used to set the texture coordinate data from one of the texture sets used by this mesh.  The function will throw an exception if a texture set index that does not exist is specified.  The size of the vector must be the same as the vertex count retrieved with the IShapeData::GetVertexCount function, or the function will throw an exception.
-	 * \param index The index of the texture coordinate set to retrieve the texture coordinates from.  This index is zero based and must be a positive number smaller than that returned by the IShapeData::GetUVSetCount function.  If there are no texture coordinate sets, this function will throw an exception.
-	 * \param in A vector containing the the new texture coordinates to replace those in the requested texture coordinate set.
-	 * \sa IShapeData::GetUVSet, IShapeData::GetUVSetCount, IShapeData::SetUVSetCount, IShapeData::GetVertexCount, IShapeData::SetVertexCount.
-	 */
-	void SetUVSet( int index, const vector<TexCoord> & in );
-
-protected:
-	vector<Vector3> vertices;
-	vector<Vector3> normals;
-	vector<Color4> colors;
-	vector<Vector3> unk_vects;
-	vector< vector<TexCoord> > uv_sets;
-	void CalcCentAndRad( Vector3 & center, float & radius ) const;
-};
-
-/**
- * AParticlesData - Generic particle system data block.
- */
-
-class AParticlesData : public AShapeData {
-public:
-	AParticlesData() {}
-	~AParticlesData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-protected:
-	bool hasSizes;
-	ushort numActive, unkShort;
-	float size;
-	vector<float> sizes;
-};
-
-/**
- * APSysData - New generic particle system data block.
- */
-
-class APSysData : public AShapeData {
-public:
-	APSysData() {}
-	~APSysData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-protected:
-	vector<float> unkFloats1;
-	ushort unkShort;
-	vector<float> unkFloats2;
-	byte unkByte;
-};
-
-/**
- * NiMeshPSysData
- */
-
-class NiMeshPSysData : public APSysData {
-public:
-	NiMeshPSysData() {
-		//AddAttr( attr_link, "Modifier" );
-		//AddAttr( attr_linkgroup, "Unknown Link Group", VER_10_2_0_0 );
-		//AddAttr( attr_link, "Unknown Link 2", VER_10_2_0_0 );
-	}
-	~NiMeshPSysData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiMeshPSysData"; };
-protected:
-	vector<float> unkFloats;
-	uint unkInt;
-	byte unkByte;
-	uint unk3Ints[3];
-};
-
-/**
- * NiPSysData
- */
-
-class NiPSysData : public APSysData {
-public:
-	NiPSysData() {}
-	~NiPSysData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiPSysData"; };
-protected:
-	vector<float> unkFloats;
-	uint unkInt;
-	bool unkBool1;
-	vector<byte> unkBytes1;
-	byte unkByte;
-	bool unkBool2;
-	vector<byte> unkBytes2;
-};
-
-/**
- * ARotatingParticlesData - Generic rotating particles data block. 
- */
-
-class ARotatingParticlesData : public AParticlesData {
-public:
-	ARotatingParticlesData() {}
-	~ARotatingParticlesData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-protected:
-	ushort numActiveRot;
-	vector<float> unkFloats;
-	vector<Quaternion> rotations;
-};
-
-/**
- * NiParticleMeshesData - Particle meshes data. 
- */
-
-class NiParticleMeshesData : public ARotatingParticlesData {
-public:
-	NiParticleMeshesData() {
-		//AddAttr( attr_link, "Unknown Link 2" );
-	}
-	~NiParticleMeshesData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiParticleMeshesData"; }
-protected:
-	
-};
-
-/**
- * NiAutoNormalParticlesData - Particle system data block (emits particles along vertex normals?).
- */
-
-class NiAutoNormalParticlesData : public AParticlesData {
-public:
-	NiAutoNormalParticlesData() {}
-	~NiAutoNormalParticlesData() {}
-	string GetBlockType() const { return "NiAutoNormalParticlesData"; }
-};
-
-/**
- * NiTriShapeData - Holds mesh data using a list of singular triangles.
- */
-class NiTriShapeData : public AShapeData {
-public:
-	NiTriShapeData() : match_group_mode(false) {}
-	~NiTriShapeData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiTriShapeData"; }
-
-	//--ITriShapeData--//
-
-	//--Match Detection--//
-
-	/*! Used to turn match detection mode on and off.  When match detection mode is on, a list of all the vertices that have identical positions are stored in the file.  This may improve performance but is not well understood.
-	 * \param choice True to enable match detection mode, false to disable it.
-	 * \sa ITriShapeData::GetMatchDetectionMode
-	 */
-	void SetMatchDetectionMode(bool choice) { match_group_mode = choice; }
-
-	/*! Used to query the current match detection mode.  When match detection mode is on, a list of all the vertices that have identical positions are stored in the file.  This may improve performance but is not well understood.
-	 * \return True if match detection mode is on, false otherwise.
-	 * \sa ITriShapeData::GetMatchDetectionMode
-	 */
-	bool GetMatchDetectionMode() const { return match_group_mode; }
-
-	//--Getters--//
-
-	/*! Returns the triangle faces that make up this mesh.
-	 * \return A vector containing the triangle faces that make up this mesh.
-	 * \sa ITriShapeData::SetTriangles
-	 */
-	vector<Triangle> GetTriangles() const { return triangles; }
-
-	//--Setters--//
-
-	/*! Replaces the triangle face data in this mesh with new data.
-	 * \param in A vector containing the new face data.  Maximum size is 65,535.
-	 * \sa ITriShapeData::GetTriangles
-	 */
-	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( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiTriStripsData"; }
-
-	//--Counts--//
-
-	/*! Used to get the number of triangle strips that this mesh is divided into.
-	 * \return The number of triangle strips used by this mesh.
-	 * \sa ITriStripData::SetStripCount
-	 */
-	short GetStripCount() const;
-
-	/*! Used to resize the triangle strips array.  If the new size is smaller, strips at the end of the array will be deleted.
-	 * \param n The new size of the triangle strips array.
-	 * \sa ITriStripData::GetStripCount
-	 */
-	void SetStripCount(int n);
-	
-	//--Getters--//
-
-	/*! Used to retrieve all the triangles from a specific triangle strip.
-	 * \param index The index of the triangle strip to retrieve the triangles from.  This is a zero-based index which must be a positive number less than that returned by NiTriStripsData::GetStripCount.
-	 * \return A vector containing all the triangle faces from the triangle strip specified by index.
-	 * \sa ITriStripsData::SetStrip, ITriStripsData::GetTriangles
-	 */
-	vector<short> GetStrip( int index ) const;
-
-	/*! This is a conveniance function which returns all triangle faces in all triangle strips that make up this mesh.  It is similar to the ITriShapeData::GetTriangles function.
-	 * \return A vector containing all the triangle faces from all the triangle strips that make up this mesh.
-	 * \sa ITriShapeData::GetTriangles, ITriStripsData::GetStrip, ITriStripsData::SetStrip
-	 */
-	vector<Triangle> GetTriangles() const;
-
-	//--Setter--/
-
-	/*! Used to set the triangle face data in a specific triangle strip.
-	 * \param index The index of the triangle strip to set the face data for.  This is a zero-based index which must be a positive number less than that returned by NiTriStripsData::GetStripCount.
-	 * \sa ITriStripsData::GetStrip, ITriStripsData::GetTriangles
-	 */
-	void SetStrip( int index, const vector<short> & in );
-
-private:
-	short GetTriangleCount() const;
-	vector< vector<short> > strips;
-};
-
-/**
- * NiBSplineBasisData - Collision box.
- */
-class NiBSplineBasisData : public AData {
-public:
-	NiBSplineBasisData() { 
-		//AddAttr( attr_int, "Unknown Int" );
-	}
-	~NiBSplineBasisData() {}
-
-	string GetBlockType() const { return "NiBSplineBasisData"; }
-};
-
-/**
- * NiBSplineData - Collision box.
- */
-class NiBSplineData : public AData {
-public:
-	NiBSplineData() {}
-	~NiBSplineData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiBSplineData"; }
-private:
-	uint unkInt;
-	vector<ushort> unkShorts;
-};
-
-/**
- * NiCollisionData - Collision box.
- */
-class NiCollisionData : public AData {
-public:
-	NiCollisionData() {}
-	~NiCollisionData() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiCollisionData"; }
-private:
-	int unknownInt1, collisionType, unknownInt2;
-	byte unknownByte;
-	fVector3 unknown3Floats;
-	float unknown15Floats[15];
-	float unknown8Floats[8]; 
-};
-
-
-
-/**
- * NiKeyframeController
- */
-class NiKeyframeController : public AController {
-public:
-	NiKeyframeController();
-	void Init() {}
-	~NiKeyframeController() {}
-	string GetBlockType() const { return "NiKeyframeController"; }
-};
-
-/**
- * NiLightColorController
- */
-class NiLightColorController : public AController {
-public:
-	NiLightColorController();
-	void Init() {}
-	~NiLightColorController() {}
-	string GetBlockType() const { return "NiLightColorController"; }
-};
-
-/**
- * NiLightDimmerController
- */
-class NiLightDimmerController : public AController {
-public:
-	NiLightDimmerController();
-	void Init() {}
-	~NiLightDimmerController() {}
-	string GetBlockType() const { return "NiLightDimmerController"; }
-};
-
-/**
- * NiKeyframeController
- */
-class NiLookAtController : public AController {
-public:
-	NiLookAtController();
-	void Init() {}
-	~NiLookAtController() {}
-	string GetBlockType() const { return "NiLookAtController"; }
-};
-
-/**
- * NiAlphaController
- */
-class NiAlphaController : public AController {
-public:
-	NiAlphaController();
-	void Init() {}
-	~NiAlphaController() {}
-	string GetBlockType() const { return "NiAlphaController"; }
-};
-
-/**
- * NiBoneLODController
- */
-class NiBoneLODController : public AController {
-public:
-	NiBoneLODController() {}
-	~NiBoneLODController();
-	string GetBlockType() const { return "NiBoneLODController"; }
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	void FixLinks( const vector<blk_ref> & blocks );
-	list<blk_ref> GetLinks() const;
-
-private:
-	uint unkInt1, unkInt2;
-	vector< vector<blk_ref> > _node_groups;
-	vector< vector< pair<blk_ref,blk_ref> > > _shape_groups;
-	vector<blk_ref> _shape_group2;
-};
-
-/**
- * NiRangeLODData
- */
-class NiRangeLODData : public AData {
-public:
-	NiRangeLODData() {}
-	~NiRangeLODData() {}
-	string GetBlockType() const { return "NiRangeLODData"; }
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-private:
-	Vector3 _center;
-	vector<LODRange> ranges;
-};
-
-class NiScreenLODData : public AData {
-public:
-	NiScreenLODData() {}
-	~NiScreenLODData() {}
-
-	string GetBlockType() const { return "NiScreenLODData"; }
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	private:
-	float unk_floats[8];
-	vector<float> unk_float_list;
-};
-
-
-/**
- * NiFlipController
- */
-class NiFlipController : public AController {
-public:
-	NiFlipController();
-	void Init() {}
-	~NiFlipController() {}
-	string GetBlockType() const { return "NiFlipController"; }
-};
-
-/**
- * NiFloatExtraDataController
- */
-class NiFloatExtraDataController : public AController {
-public:
-	NiFloatExtraDataController();
-	void Init() {}
-	~NiFloatExtraDataController() {}
-	string GetBlockType() const { return "NiFloatExtraDataController"; }
-};
-
-/**
- * NiVisController
- */
-class NiVisController : public AController {
-public:
-	NiVisController();
-	void Init() {}
-	~NiVisController() {}
-	string GetBlockType() const { return "NiVisController"; }
-};
-
-/**
- * NiMaterialColorController
- */
-class NiMaterialColorController : public AController {
-public:
-	NiMaterialColorController();
-	void Init() {}
-	~NiMaterialColorController() {}
-	string GetBlockType() const { return "NiMaterialColorController"; }
-};
-
-/**
- * NiMultiTargetTransformController
- */
-class NiMultiTargetTransformController : public AController {
-public:
-	NiMultiTargetTransformController();
-	void Init() {}
-	~NiMultiTargetTransformController() {}
-	string GetBlockType() const { return "NiMultiTargetTransformController"; }
-};
-
-/**
- * NiPSysEmitterCtlr
- */
-class NiPSysEmitterCtlr : public AController {
-public:
-	NiPSysEmitterCtlr();
-	void Init() {}
-	~NiPSysEmitterCtlr() {}
-	string GetBlockType() const { return "NiPSysEmitterCtlr"; }
-};
-
-/**
- * NiPSysEmitterDeclinationVarCtlr
- */
-class NiPSysEmitterDeclinationVarCtlr : public AController {
-public:
-	NiPSysEmitterDeclinationVarCtlr();
-	void Init() {}
-	~NiPSysEmitterDeclinationVarCtlr() {}
-	string GetBlockType() const { return "NiPSysEmitterDeclinationVarCtlr"; }
-};
-
-/**
- * NiPSysEmitterInitialRadiusCtlr
- */
-class NiPSysEmitterInitialRadiusCtlr : public AController {
-public:
-	NiPSysEmitterInitialRadiusCtlr();
-	void Init() {}
-	~NiPSysEmitterInitialRadiusCtlr() {}
-	string GetBlockType() const { return "NiPSysEmitterInitialRadiusCtlr"; }
-};
-
-/**
- * NiPSysResetOnLoopCtlr
- */
-class NiPSysResetOnLoopCtlr : public AController {
-public:
-	NiPSysResetOnLoopCtlr();
-	void Init() {}
-	~NiPSysResetOnLoopCtlr() {}
-	string GetBlockType() const { return "NiPSysResetOnLoopCtlr"; }
-};
-
-/**
- * NiPSysUpdateCtlr
- */
-class NiPSysUpdateCtlr : public AController {
-public:
-	NiPSysUpdateCtlr();
-	void Init() {}
-	~NiPSysUpdateCtlr() {}
-	string GetBlockType() const { return "NiPSysUpdateCtlr"; }
-};
-
-/**
- * NiTextureTransformController
- */
-class NiTextureTransformController : public AController {
-public:
-	NiTextureTransformController();
-	void Init() {}
-	~NiTextureTransformController() {}
-	string GetBlockType() const { return "NiTextureTransformController"; }
-};
-
-/**
- * NiTransformController
- */
-class NiTransformController : public AController {
-public:
-	NiTransformController();
-	void Init() {}
-	~NiTransformController() {}
-	string GetBlockType() const { return "NiTransformController"; }
-};
-
-/**
- * NiUVController 
- */
-class NiUVController : public AController {
-public:
-	NiUVController();
-	void Init() {}
-	~NiUVController() {}
-	string GetBlockType() const { return "NiUVController"; }
-};
-
-/**
- * NiPathController
- */
-
-class NiPathController : public AController {
-public:
-	NiPathController();
-	void Init() {}
-	~NiPathController() {}
-	string GetBlockType() const { return "NiPathController"; }
-};
-
-/**
- * NiAmbientLight - Not found in any official NIF files
- */
-
-class NiAmbientLight : public ALight {
-public:
-	NiAmbientLight();
-	void Init() {}
-	~NiAmbientLight() {}
-	string GetBlockType() const { return "NiAmbientLight"; }
-};
-
-/**
- * NiDirectionalLight - Not found in any official NIF files
- */
-
-class NiDirectionalLight : public ALight {
-public:
-	NiDirectionalLight();
-	void Init() {}
-	~NiDirectionalLight() {}
-	string GetBlockType() const { return "NiDirectionalLight"; }
-};
-
-/**
- * NiPointLight
- */
-
-class NiPointLight : public APointLight {
-public:
-	NiPointLight();
-	void Init() {}
-	~NiPointLight() {}
-	string GetBlockType() const { return "NiPointLight"; }
-};
-
-/**
- * NiSpotLight
- */
-
-class NiSpotLight : public APointLight {
-public:
-	NiSpotLight();
-	void Init() {}
-	~NiSpotLight() {}
-	string GetBlockType() const { return "NiSpotLight"; }
-};
-
-/**
- * NiParticles
- */
-
-class NiParticles : public AParticleNode {
-public:
-	NiParticles();
-	void Init() {}
-	~NiParticles() {}
-	string GetBlockType() const { return "NiParticles"; }
-};
-
-/**
- * NiParticleSystem
- */
-
-class NiParticleSystem : public AParticleNode {
-public:
-	NiParticleSystem();
-	void Init() {}
-	~NiParticleSystem() {}
-	string GetBlockType() const { return "NiParticleSystem"; }
-};
-
-/**
- * NiAutoNormalParticles
- */
-
-class NiAutoNormalParticles : public AParticleNode {
-public:
-	NiAutoNormalParticles();
-	void Init() {}
-	~NiAutoNormalParticles() {}
-	string GetBlockType() const { return "NiAutoNormalParticles"; }
-};
-
-/**
- * NiMeshParticleSystem
- */
-
-class NiMeshParticleSystem : public AParticleNode {
-public:
-	NiMeshParticleSystem();
-	void Init() {}
-	~NiMeshParticleSystem() {}
-	string GetBlockType() const { return "NiMeshParticleSystem"; }
-};
-
-/**
- * NiRotatingParticles
- */
-
-class NiRotatingParticles : public AParticleNode {
-public:
-	NiRotatingParticles();
-	void Init() {}
-	~NiRotatingParticles() {}
-	string GetBlockType() const { return "NiRotatingParticles"; }
-}; 
-
-/**
- * NiTextureEffect
- */
-
-class NiTextureEffect : public ADynamicEffect {
-public:
-	NiTextureEffect();
-	void Init() {}
-	~NiTextureEffect() {}
-	string GetBlockType() const { return "NiTextureEffect"; }
-}; 
-
-/**
- * NiCamera
- */
-
-class NiCamera : public ANode {
-public:
-	NiCamera();
-	void Init() {}
-	~NiCamera() {}
-	string GetBlockType() const { return "NiCamera"; }
-}; 
-
-/**
- * NiParticleMeshes
- */
-
-class NiParticleMeshes : public AParticleNode {
-public:
-	NiParticleMeshes();
-	void Init() {}
-	~NiParticleMeshes() {}
-	string GetBlockType() const { return "NiParticleMeshes"; }
-}; 
-
-/**
- * NiGravity
- */
-
-class NiGravity : public AParticleModifier {
-public:
-	NiGravity();
-	void Init() {}
-	~NiGravity() {}
-	string GetBlockType() const { return "NiGravity"; }
-}; 
-
-/**
- * NiParticleBomb
- */
-
-class NiParticleBomb : public AParticleModifier {
-public:
-	NiParticleBomb();
-	void Init() {}
-	~NiParticleBomb() {}
-	string GetBlockType() const { return "NiParticleBomb"; }
-}; 
-
-/**
- * NiPlanarCollider
- */
-
-class NiPlanarCollider : public AParticleModifier {
-public:
-	NiPlanarCollider();
-	void Init() {}
-	~NiPlanarCollider() {}
-	string GetBlockType() const { return "NiPlanarCollider"; }
-}; 
-
-/**
- * NiSphericalCollider
- */
-
-class NiSphericalCollider : public AParticleModifier {
-public:
-	NiSphericalCollider();
-	void Init() {}
-	~NiSphericalCollider() {}
-	string GetBlockType() const { return "NiSphericalCollider"; }
-}; 
-
-/**
- * NiParticleGrowFade
- */
-
-class NiParticleGrowFade : public AParticleModifier {
-public:
-	NiParticleGrowFade();
-	void Init() {}
-	~NiParticleGrowFade() {}
-	string GetBlockType() const { return "NiParticleGrowFade"; }
-}; 
-
-/**
- * NiParticleMeshModifier
- */
-
-class NiParticleMeshModifier : public AParticleModifier {
-public:
-	NiParticleMeshModifier();
-	void Init() {}
-	~NiParticleMeshModifier() {}
-	string GetBlockType() const { return "NiParticleMeshModifier"; }
-}; 
-
-/**
- * NiParticleColorModifier
- */
-
-class NiParticleColorModifier : public AParticleModifier {
-public:
-	NiParticleColorModifier();
-	void Init() {}
-	~NiParticleColorModifier() {}
-	string GetBlockType() const { return "NiParticleColorModifier"; }
-}; 
-
-/**
- * NiGravity
- */
-
-class NiParticleRotation : public AParticleModifier {
-public:
-	NiParticleRotation();
-	void Init() {}
-	~NiParticleRotation() {}
-	string GetBlockType() const { return "NiParticleRotation"; }
-}; 
-
-/**
- * NiPSysPlanarCollider
- */
-
-class NiPSysPlanarCollider : public NiObject {
-public:
-	NiPSysPlanarCollider();
-	void Init() {}
-	~NiPSysPlanarCollider() {}
-	string GetBlockType() const { return "NiPSysPlanarCollider"; }
-};
-
-/**
- * NiPSysAgeDeathModifier
- */
-
-class NiPSysAgeDeathModifier : public APSysModifier {
-public:
-	NiPSysAgeDeathModifier();
-	void Init() {}
-	~NiPSysAgeDeathModifier() {}
-	string GetBlockType() const { return "NiPSysAgeDeathModifier"; }
-}; 
-
-/**
- * NiPSysBoundUpdateModifier
- */
-
-class NiPSysBoundUpdateModifier : public APSysModifier {
-public:
-	NiPSysBoundUpdateModifier();
-	void Init() {}
-	~NiPSysBoundUpdateModifier() {}
-	string GetBlockType() const { return "NiPSysBoundUpdateModifier"; }
-}; 
-
-/**
- * NiPSysBoxEmitter
- */
-
-class NiPSysBoxEmitter : public APSysVolumeEmitter {
-public:
-	NiPSysBoxEmitter();
-	void Init() {}
-	~NiPSysBoxEmitter() {}
-	string GetBlockType() const { return "NiPSysBoxEmitter"; }
-}; 
-
-/**
- * NiPSysColliderManager
- */
-
-class NiPSysColliderManager : public APSysModifier {
-public:
-	NiPSysColliderManager();
-	void Init() {}
-	~NiPSysColliderManager() {}
-	string GetBlockType() const { return "NiPSysColliderManager"; }
-}; 
-
-/**
- * NiPSysColorModifier
- */
-
-class NiPSysColorModifier : public APSysModifier {
-public:
-	NiPSysColorModifier();
-	void Init() {}
-	~NiPSysColorModifier() {}
-	string GetBlockType() const { return "NiPSysColorModifier"; }
-}; 
-
-/**
- * NiPSysCylinderEmitter
- */
-
-class NiPSysCylinderEmitter : public APSysVolumeEmitter {
-public:
-	NiPSysCylinderEmitter();
-	void Init() {}
-	~NiPSysCylinderEmitter() {}
-	string GetBlockType() const { return "NiPSysCylinderEmitter"; }
-}; 
-
-/**
- * NiPSysGravityModifier
- */
-
-class NiPSysGravityModifier : public APSysModifier {
-public:
-	NiPSysGravityModifier();
-	void Init() {}
-	~NiPSysGravityModifier() {}
-	string GetBlockType() const { return "NiPSysGravityModifier"; }
-}; 
-
-/**
- * NiPSysGrowFadeModifier
- */
-
-class NiPSysGrowFadeModifier : public APSysModifier {
-public:
-	NiPSysGrowFadeModifier();
-	void Init() {}
-	~NiPSysGrowFadeModifier() {}
-	string GetBlockType() const { return "NiPSysGrowFadeModifier"; }
-}; 
-
-/**
- * NiPSysMeshUpdateModifier
- */
-
-class NiPSysMeshUpdateModifier : public APSysModifier {
-public:
-	NiPSysMeshUpdateModifier();
-	void Init() {}
-	~NiPSysMeshUpdateModifier() {}
-	string GetBlockType() const { return "NiPSysMeshUpdateModifier"; }
-}; 
-
-/**
- * NiPSysPositionModifier
- */
-
-class NiPSysPositionModifier : public APSysModifier {
-public:
-	NiPSysPositionModifier();
-	void Init() {}
-	~NiPSysPositionModifier() {}
-	string GetBlockType() const { return "NiPSysPositionModifier"; }
-}; 
-
-/**
- * NiPSysRotationModifier
- */
-
-class NiPSysRotationModifier : public APSysModifier {
-public:
-	NiPSysRotationModifier();
-	void Init() {}
-	~NiPSysRotationModifier() {}
-	string GetBlockType() const { return "NiPSysRotationModifier"; }
-}; 
-
-/**
- * NiPSysSpawnModifier
- */
-
-class NiPSysSpawnModifier : public APSysModifier {
-public:
-	NiPSysSpawnModifier();
-	void Init() {}
-	~NiPSysSpawnModifier() {}
-	string GetBlockType() const { return "NiPSysSpawnModifier"; }
-}; 
-
-/**
- * NiPSysSphereEmitter
- */
-
-class NiPSysSphereEmitter : public APSysVolumeEmitter {
-public:
-	NiPSysSphereEmitter();
-	void Init() {}
-	~NiPSysSphereEmitter() {}
-	string GetBlockType() const { return "NiPSysSphereEmitter"; }
-}; 
-
-/**
- * NiPSysEmitterCtlrData
- */
-class NiPSysEmitterCtlrData : public AData {
-public:
-	NiPSysEmitterCtlrData() {}
-	~NiPSysEmitterCtlrData() {}
-
-	string GetBlockType() const { return "NiPSysEmitterCtlrData"; }
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-private:
-	KeyType f_key_type;
-	vector< Key<float> > float_keys;
-	vector< Key<byte> > byte_keys;
-};
-
-/**
- * AKeyframeData -
- */
-
-class AKeyframeData : public AData {
-
-public:
-
-	AKeyframeData() {}
-	~AKeyframeData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "AKeyframeData"; }
-
-	//--Rotate--//
-
-	/*! Retrieves the type of rotation interpolation being used.
-		* \return The rotation key type specifing the type of interpolation being used.
-		* \sa IKeyframeData::SetRotateType
-		*/
-	KeyType GetRotateType() const { return rotationType; }
-
-	/*! Sets the type of rotation interpolation being used.  Does not affect existing key data.
-	 * \param t The new rotation key type specifing the type of interpolation to be used.
-	 * \sa IKeyframeData::GetRotateType
-	 */
-	void SetRotateType( KeyType t ) { rotationType = t; }
-
-	/*! Retrieves the rotation key data.
-	 * \return A vector containing Key<Quaternion> data which specify rotation over time.
-	 * \sa IKeyframeData::SetRotateKeys, Key
-	 */
-	vector< Key<Quaternion> > GetRotateKeys() const { return rotKeys; }
-
-	/*! Sets the rotation key data.
-	 * \param keys A vector containing new Key<Quaternion> data which will replace any existing data.
-	 * \sa IKeyframeData::GetRotateKeys, Key
-	 */
-	void SetRotateKeys( const vector< Key<Quaternion> > & keys ) { rotKeys = keys; }
-
-	//--Translate--//
-
-	/*! Retrieves the type of translation interpolation being used.
-	 * \return The translation key type specifing the type of interpolation being used.
-	 * \sa IKeyframeData::SetTranslateType
-	 */
-	KeyType GetTranslateType() const { return translationType; }
-
-	/*! Sets the type of translation interpolation being used.  Does not affect existing key data.
-	 * \param t The new translation key type specifing the type of interpolation to be used.
-	 * \sa IKeyframeData::GetTranslateType
-	 */
-	void SetTranslateType( KeyType t ) { translationType = t; }
-
-	/*! Retrieves the translation key data.
-	 * \return A vector containing Key<Vector3> data which specify translation over time.
-	 * \sa IKeyframeData::SetTranslateKeys, Key
-	 */
-	vector< Key<Vector3> > GetTranslateKeys() const { return transKeys; }
-
-	/*! Sets the translation key data.
-	 * \param keys A vector containing new Key<Vector3> data which will replace any existing data.
-	 * \sa IKeyframeData::GetTranslateKeys, Key
-	 */
-	void SetTranslateKeys( vector< Key<Vector3> > const & keys ) { transKeys = keys; }
-
-	//--Scale--//
-
-	/*! Retrieves the type of scale interpolation being used.
-	 * \return The scale key type specifing the type of interpolation being used.
-	 * \sa IKeyframeData::SetTranslateType
-	 */
-	KeyType GetScaleType() const { return scaleType; }
-
-	/*! Sets the type of scale interpolation being used.  Does not affect existing key data.
-	 * \param t The new scale key type specifing the type of interpolation to be used.
-	 * \sa IKeyframeData::GetScaleType
-	 */
-	void SetScaleType( KeyType t ) { scaleType = t; }
-
-	/*! Retrieves the scale key data.
-	 * \return A vector containing Key<float> data which specify scale over time.
-	 * \sa IKeyframeData::SetScaleKeys, Key
-	 */
-	vector< Key<float> > GetScaleKeys() const { return scaleKeys; }
-
-	/*! Sets the scale key data.
-	 * \param keys A vector containing new Key<float> data which will replace any existing data.
-	 * \sa IKeyframeData::GetScaleKeys, Key
-	 */
-	void SetScaleKeys( vector< Key<float> > const & keys ) { scaleKeys = keys; }
-
-private:
-	KeyType rotationType;
-	vector< Key<Quaternion> > rotKeys;
-
-	KeyType translationType;
-	vector< Key<Vector3> >	transKeys;
-
-	KeyType scaleType;
-	vector< Key<float> > scaleKeys;
-
-	KeyType xyzTypes[3];
-	vector< Key<float> > xyzKeys[3];
-};
-
-/**
- * NiKeyframeData
- */
-
-class NiKeyframeData : public AKeyframeData {
-public:
-	NiKeyframeData() {}
-	~NiKeyframeData() {}
-
-	string GetBlockType() const { return "NiKeyframeData"; }
-};
-
-/**
- * NiTransformData
- */
-
-class NiTransformData : public AKeyframeData {
-public:
-	NiTransformData() {}
-	~NiTransformData() {}
-
-	string GetBlockType() const { return "NiTransformData"; }
-};
-
-/**
- * NiPalette object.  Contains a color palette for internally stored paletized textures.
- */
-class NiPalette : public AData {
-public:
-	NiPalette() {}
-	void Init() {}
-	~NiPalette() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiPalette"; }
-
-	/*! Retrieves the palette data from this palette block.
-	 * \return A vector containing the the colors stored in the palette.
-	 * \sa IPalette::SetPalette
-	 */
-	vector<Color4> GetPalette() const;
-
-	/*! Sets the palette data for this palette block.
-	 * \param new_apl A vector containing the the new colors to be stored in the palette.
-	 * \sa IPalette::GetPalette
-	 */
-	void SetPalette( const vector<Color4> & new_pal );
-
-private:
-	byte unkByte;
-	uint numEntries;
-	byte palette[256][4];
-};
-
-/**
- * NiSkinPartition
- */
-
-class NiSkinPartition : public AData {
-public:
-	NiSkinPartition() {}
-	void Init() {}
-	~NiSkinPartition() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiSkinPartition"; }
-private:
-	struct SkinPartition {
-		vector<ushort> bones;
-		vector<ushort> vertexMap;
-		vector< vector<float> > vertexWeights;
-		vector< vector<ushort> > strips;
-		vector<Triangle> triangles;
-		vector< vector<byte> > boneIndices;
-	};
-	vector<SkinPartition> partitions;
-};
-
-/**
- * NiStringPalette
- */
-
-class NiStringPalette : public AData {
-public:
-	NiStringPalette() { 
-		//AddAttr( attr_string, "Palette" ); 
-	}
-	void Init() {}
-	~NiStringPalette() {}
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiStringPalette"; }
-};
-
-
-/**
- * NiSkinInstance
- */
-
-//Non-Public interface to allow NiSkinData to get the bone list read by NiSkinInstance
-class ISkinInstInternal {
-public:
-	virtual vector<int> GetBoneList() const = 0;
-	virtual void ReadBoneList( istream& in ) = 0;
-};
-
-class NiSkinInstance : public AData {
-public:
-	NiSkinInstance(){
-		//AddAttr( attr_link, "Data" );
-		//AddAttr( attr_link, "Skin Partition", VER_10_2_0_0 );
-		//AddAttr( attr_skeletonroot, "Skeleton Root" );
-		//AddAttr( attr_bones, "Bones" );
-	}
-	~NiSkinInstance() {}
-	string GetBlockType() const { return "NiSkinInstance"; }
-
-	//ISkinInstInternal
-
-	vector<int> GetBoneList() const { return bones; }
-
-	void ReadBoneList( istream& in ) {
-		int len = ReadUInt( in );
-		bones.resize( len );
-		for (int i = 0; i < len; ++i ) {
-			bones[i] = ReadUInt( in );
-		}
-	}
-private:
-	vector<int> bones;
-};
-
-class NiSkinData : public AData {
-
-public:
-
-	NiSkinData() { 
-		//AddAttr( attr_link, "Skin Partition", 0, VER_10_1_0_0 );
-		rotation = Matrix33::IDENTITY;
-		translation[0] = 0.0f;
-		translation[1] = 0.0f;
-		translation[2] = 0.0f;
-		scale = 1.0f;
-		unknownInt = -1;
-		unknownByte = 1;
-	}
-	~NiSkinData();
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiSkinData"; }
-
-	//IBlockInternal
-	void FixLinks( const vector<blk_ref> & blocks ); // This version of the function will copy the bones from the parent Skin Instance block and fix the links at the same time.
-	void RemoveCrossLink( IBlock * block_to_remove );
-
-	//ISkinDataInternal
-	void RepositionTriShape( blk_ref & tri_shape );
-
-	/*! Used to retrieve a list of all the bones that influence this skin
-		* \return A vector containing references to all the node blocks which act as bone influences on this skin.
-		* \sa ISkinData::AddBone, ISkinData::RemoveBone, ISkinData::GetWeights
-		*/
-	vector<blk_ref> GetBones() const;
-
-	/*! Used to retrieve the skin weights associated with a specific bone
-		* \param bone A blk_ref pointing to one of the node blocks which acts as a bone influence on this skin that's related weights are to be retrieved.
-		* \return A map of ints to floats.  The integers are the vertex number and the floats are the percentage influence (between 0.0 and 1.0) that the specified bone has on that vertex.  Not all vertices will be influenced by all bones.
-		* \sa ISkinData::GetBones, ISkinData::AddBone, ISkinData::RemoveBone
-		*/
-	map<int, float> GetWeights( const blk_ref & bone ) const;
-
-	/*! Adds a new bone influence to this skin alone with all its weight information.  If a bone that already influences the skin is specified, the weight data will be overwritten.
-	 * \param bone A blk_ref pointing to a node blocks which is to be added as a bone influence on this skin.
-	 * \param in A map of ints to floats.  The integers are the vertex number and the floats are the percentage influence (between 0.0 and 1.0) that the specified bone has on that vertex.  Not all vertices need to be influenced by all bones.
-	 * \sa ISkinData::RemoveBone, ISkinData::GetBones, ISkinData::GetWeights
-	 */
-	void AddBone( const blk_ref & bone, map<int, float> const & in );
-
-	/*! Removes a bone influence and deletes associated vertex weight information.
-	 * \param bone A blk_ref pointing to a node blocks which is to be removed as a bone influence on this skin.
-	 * \sa ISkinData::AddBone, ISkinData::GetBones
-	 */
-	void RemoveBone( const blk_ref & bone );
-
-	void ReassignCrossRefs( const map<string,blk_ref> & name_map );
-private:
-	void SetBones( const vector<blk_ref> & bone_blocks );
-	void StraightenSkeleton();
-	struct Bone {
-		Matrix33 rotation;
-		fVector3 translation;
-		float scale;
-		fVector4 unknown4Floats;
-		map<int, float> weights;
-	};
-
-	INode * GetNodeParent() const;
-	void CalculateOverallOffset( Matrix33 & rot, fVector3 & tr, float & sc ) const;
-	void CalculateBoneOffset( const INode * const par_node, const IBlock * const bone_block, Bone & result ) const;
-	Matrix33 rotation;
-	fVector3 translation;
-	float  scale;
-	int unknownInt;
-	byte unknownByte;
-	map<IBlock *, Bone > bone_map;
-	vector<Bone> bones;
-};
-
-//-- New Nodes--//
-
-class NiGeomMorpherController : public AController{
-public:
-	NiGeomMorpherController();
-	void Init() {}
-	~NiGeomMorpherController() {}
-
-	string asString() const;
-	string GetBlockType() const { return "NiGeomMorpherController"; }
-};
-
-/*! Contain an array of bool keys. */
-class NiBoolData : public AData {
-public:
-	NiBoolData() {}
-	~NiBoolData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiBoolData"; };
-
-	/*! Retrieves the type of boolean interpolation being used.
-	 * \return The boolean key type specifing the type of interpolation being used.
-	 * \sa IBoolData::SetKeyType
-	 */
-	KeyType GetKeyType() const { return _type; }
-
-	/*! Sets the type of boolean interpolation being used.  Does not affect existing key data.
-	 * \param t The new boolean key type specifing the type of interpolation to be used.
-	 * \sa IBoolData::GetKeyType
-	 */
-	void SetKeyType( KeyType t ) { _type = t; }
-
-	/*! Retrieves the boolean key data.
-	 * \return A vector containing Key<unsigned char> data which specify boolean values over time.
-	 * \sa IBoolData::SetKeys, Key
-	 */
-	vector< Key<unsigned char> > GetKeys() const { return _keys; }
-
-	/*! Sets the boolean key data.
-	 * \param keys A vector containing new Key<unsigned char> data which will replace any existing data.
-	 * \sa IBoolData::GetKeys, Key
-	 */
-	void SetKeys( vector< Key<unsigned char> > const & keys ) { _keys = keys; }
-
-private:
-	KeyType _type;
-	vector< Key<byte> > _keys;
-};
-
-/*! Contains an array of color keys. */
-class NiColorData : public AData {
-public:
-	NiColorData() {}
-	~NiColorData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiColorData"; };
-
-	/*! Retrieves the type of color interpolation being used.
-	 * \return The color key type specifing the type of interpolation being used.
-	 * \sa IColorData::SetKeyType
-	 */
-	KeyType GetKeyType() const { return _type; }
-
-	/*! Sets the type of color interpolation being used.  Does not affect existing key data.
-	 * \param t The new color key type specifing the type of interpolation to be used.
-	 * \sa IColorData::GetKeyType
-	 */
-	void SetKeyType( KeyType t ) { _type = t; }
-
-	/*! Retrieves the color key data.
-	 * \return A vector containing Key<Color4> data which specify color over time.
-	 * \sa IColorData::SetKeys, Key
-	 */
-	vector< Key<Color4> > GetKeys() const { return _keys; }
-
-	/*! Sets the color key data.
-	 * \param keys A vector containing new Key<Color4> data which will replace any existing data.
-	 * \sa IColorData::GetKeys, Key
-	 */
-	void SetKeys( vector< Key<Color4> > const & keys ) { _keys = keys; }
-
-private:
-	KeyType _type;
-	vector<Key<Color4> > _keys;
-};
-
-/**
- * NiControllerSequence - Root node in .kf files (version 10.0.1.0 and up).
- */
-class NiControllerSequence : public AData {
-public:
-	NiControllerSequence() {
-		//AddAttr( attr_string, "Name" );
-		//AddAttr( attr_link, "String Palette", VER_10_2_0_0 );
-	}
-	~NiControllerSequence();
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-
-	string GetBlockType() const { return "NiControllerSequence"; }
-
-	void FixLinks( const vector<blk_ref> & blocks );
-	list<blk_ref> GetLinks() const;
-
-	/*! Sets the name and block reference to the NiTextKeyExtraData block which will be used by this controller sequence to specify the keyframe labels or "notes."
-	 * \param new_name The name of the NiTextKeyExtraData block to use.
-	 * \param new_link The block reference of the NiTextKeyExtraData block to use.
-	 * \sa ITextKeyExtraData
-	 */
-	void SetTextKey( string new_name, blk_ref new_link );
-
-	/*! Sets the name, block reference, and controller type to use as a new child to this Kf root node.  The controller type is unnecessary before version 10.2.0.0.  From that version on, these children must be interpolators.  Before that version they will be keyframe controllers.
-	 * \param new_name The name to re-link this Kf file child to when it is merged with a Nif file.
-	 * \param new_link The block reference of the new Kf file child.
-	 * \param controller_type The original controller type that this Kf file child was connected to.  Only applies to versions which use interpolators.
-	 * \sa IControllerSequence::ClearKfChildren
-	 */
-	void AddKfChild( string new_name, blk_ref new_link, string controller_type);
-
-	/*! Removes all Kf file children from this Kf file root block.
-	 * \sa IControllerSequence::AddKfChild
-	 */
-	void ClearKfChildren();
-private:
-	string GetSubStr( const string & pal, short offset ) const;
-
-	struct KfChild {
-		blk_ref block;
-		string name;
-		blk_ref unk_link;
-		short name_offset;
-		short property_offset;
-		short controller_offset;
-		short var1_offset;
-		short var2_offset;
-		short unk_short1, unk_short2, unk_short3, unk_short4, unk_short5;
-		KfChild() : block(), name(), unk_link(),
-		name_offset(-1), controller_offset(-1), property_offset(-1), var1_offset(-1), var2_offset(-1),
-		unk_short1(0), unk_short2(0), unk_short3(0), unk_short4(0), unk_short5(0) {};
-	};
-	string txt_key_name;
-	blk_ref txt_key_blk;
-	vector< KfChild > children;
-
-	uint unk_int1, unk_int2;
-	float unk_float1, unk_float2, unk_4_floats[4];
-	string unk_string;
-};
-
-/* Contains an array of float keys. */
-class NiFloatData : public AData {
-public:
-	NiFloatData() {}
-	~NiFloatData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiFloatData"; };
-
-	/*! Retrieves the type of float interpolation being used.
-	 * \return The float key type specifing the type of interpolation being used.
-	 * \sa IFloatData::SetKeyType
-	 */
-	KeyType GetKeyType() const { return _type; }
-
-	/*! Sets the type of float interpolation being used.  Does not affect existing key data.
-	 * \param t The new float key type specifing the type of interpolation to be used.
-	 * \sa IFloatData::GetKeyType
-	 */
-	void SetKeyType( KeyType t ) { _type = t; }
-
-	/*! Retrieves the float key data.
-	 * \return A vector containing Key<float> data which specify float values over time.
-	 * \sa IFloatData::SetKeys, Key
-	 */
-	vector< Key<float> > GetKeys() const { return _keys; }
-
-	/*! Sets the float key data.
-	 * \param keys A vector containing new Key<float> data which will replace any existing data.
-	 * \sa IFloatData::GetKeys, Key
-	 */
-	void SetKeys( vector< Key<float> > const & keys ) { _keys = keys; }
-
-private:
-	KeyType _type;
-	vector<Key<float> > _keys;
-};
-
-class NiStringsExtraData : public AExtraData {
-public:
-	NiStringsExtraData() {}
-	~NiStringsExtraData() {}
-	string GetBlockType() const { return "NiStringsExtraData"; };
-
-	void Read( istream& file, unsigned int version ) {
-		AExtraData::Read( file, version );
-		uint count = ReadUInt( file );
-		string_data.resize( count );
-		NifStream( string_data, file );
-	}
-	void Write( ostream& file, unsigned int version ) const {
-		AExtraData::Write( file, version );
-		WriteUInt( uint(string_data.size()), file );
-		NifStream( string_data, file );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Strings:  " << uint(string_data.size()) << endl;
-
-		for ( uint i = 0; i < string_data.size(); ++i ) {
-			out << "   " << i << ":  " << string_data[i] << endl;
-		}
-
-		return out.str();
-	}
-private:
-	vector<string> string_data;
-};
-
-class NiStringExtraData : public AExtraData {
-public:
-	NiStringExtraData() {
-		//AddAttr( attr_string, "String Data" );
-	}
-	~NiStringExtraData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiStringExtraData"; }
-};
-
-class NiBooleanExtraData : public AExtraData {
-public:
-	NiBooleanExtraData() {
-		//AddAttr( attr_byte, "Boolean Data" );
-	}
-	~NiBooleanExtraData() {}
-	string GetBlockType() const { return "NiBooleanExtraData"; };
-
-	void Read( istream& in, unsigned int version ) {
-		AExtraData::Read( in, version );
-		//GetAttr("Boolean Data")->Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		AExtraData::Write( out, version );
-		//GetAttr("Boolean Data")->Write( out, version );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Boolean Data:  " << GetAttr("Boolean Data")->asString() << endl;
-
-		return out.str();
-	}
-	
-};
-
-class NiBinaryExtraData : public AExtraData {
-public:
-	NiBinaryExtraData() {}
-	~NiBinaryExtraData() {}
-	string GetBlockType() const { return "NiBinaryExtraData"; };
-
-	void Read( istream& in, unsigned int version ) {
-		AExtraData::Read( in, version );
-		
-		uint numBytes = ReadUInt( in );
-		binData.resize( numBytes );
-		NifStream( binData, in );
-
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		AExtraData::Write( out, version );
-		
-		WriteUInt( uint(binData.size()), out );
-		NifStream( binData, out );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Binary Data (" << uint(binData.size()) << " bytes):" << endl;
-
-		for ( uint i = 0; i < binData.size(); ++i ) {
-			out << "   " << i << ":  " << binData[i] << endl;
-		}
-
-		return out.str();
-	}
-private:
-	vector<byte> binData;
-	
-};
-
-class NiVectorExtraData : public AExtraData {
-public:
-	NiVectorExtraData() {
-		//AddAttr( attr_vector3, "Vector Data" );
-		//AddAttr( attr_float, "Unknown Float" );
-	}
-	~NiVectorExtraData() {}
-	string GetBlockType() const { return "NiVectorExtraData"; };
-
-	void Read( istream& in, unsigned int version ) {
-		AExtraData::Read( in, version );
-		//GetAttr("Vector Data")->Read( in, version );
-		//GetAttr("Unknown Float")->Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		//GetAttr("Vector Data")->Write( out, version );
-		//GetAttr("Unknown Float")->Write( out, version );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Vector Data:  " << GetAttr("Vector Data")->asString() << endl
-			<< "Unknown Float:  " << GetAttr("Unknown Float")->asString() << endl;
-
-		return out.str();
-	}
-	
-};
-
-class NiColorExtraData : public AExtraData {
-public:
-	NiColorExtraData() {}
-	~NiColorExtraData() {}
-	string GetBlockType() const { return "NiColorExtraData"; };
-
-	void Read( istream& file, unsigned int version ) {
-		AExtraData::Read( file, version );
-		NifStream( color, file );
-	}
-	void Write( ostream& file, unsigned int version ) const {
-		AExtraData::Write( file, version );
-		NifStream( color, file );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Color Data:  (" << color.r << ", " << color.g << ", " << color.b << ", " << color.a << ")" << endl;
-
-		return out.str();
-	}
-private:
-	Color4 color;	
-};
-
-class NiFloatExtraData : public AExtraData {
-public:
-	NiFloatExtraData() {
-		//AddAttr( attr_float, "Float Data" );
-	}
-	~NiFloatExtraData() {}
-	string GetBlockType() const { return "NiFloatExtraData"; };
-
-	void Read( istream& in, unsigned int version ) {
-		AExtraData::Read( in, version );
-		//GetAttr("Float Data")->Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		AExtraData::Write( out, version );
-		//GetAttr("Float Data")->Write( out, version );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Float Data:  " << GetAttr("Float Data")->asString() << endl;
-
-		return out.str();
-	}
-	
-};
-
-class NiFloatsExtraData : public AExtraData {
-public:
-	NiFloatsExtraData() {}
-	~NiFloatsExtraData() {}
-	string GetBlockType() const { return "NiFloatsExtraData"; };
-
-	void Read( istream& file, unsigned int version ) {
-		AExtraData::Read( file, version );
-		uint count = ReadUInt( file );
-		float_data.resize( count );
-		NifStream( float_data, file );
-	}
-	void Write( ostream& file, unsigned int version ) const {
-		AExtraData::Write( file, version );
-		WriteUInt( uint(float_data.size()), file );
-		NifStream( float_data, file );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Floats:  " << uint(float_data.size()) << endl;
-
-		for ( uint i = 0; i < float_data.size(); ++i ) {
-			out << "   " << i << ":  " << float_data[i] << endl;
-		}
-
-		return out.str();
-	}
-private:
-	vector<float> float_data;
-};
-
-class NiIntegersExtraData : public AExtraData {
-public:
-	NiIntegersExtraData() {}
-	~NiIntegersExtraData() {}
-	string GetBlockType() const { return "NiIntegersExtraData"; };
-
-	void Read( istream& file, unsigned int version ) {
-		AExtraData::Read( file, version );
-		uint count = ReadUInt( file );
-		int_data.resize( count );
-		NifStream( int_data, file );
-	}
-	void Write( ostream& file, unsigned int version ) const {
-		AExtraData::Write( file, version );
-		WriteUInt( uint(int_data.size()), file );
-		NifStream( int_data, file );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Ints:  " << uint(int_data.size()) << endl;
-
-		for ( uint i = 0; i < int_data.size(); ++i ) {
-			out << "   " << i << ":  " << int_data[i] << endl;
-		}
-
-		return out.str();
-	}
-private:
-	vector<uint> int_data;
-};
-
-class NiIntegerExtraData : public AExtraData {
-public:
-	NiIntegerExtraData() {
-		//AddAttr( attr_int, "Integer Data" );
-	}
-	~NiIntegerExtraData() {}
-
-	string GetBlockType() const { return "NiIntegerExtraData"; };
-
-	void Read( istream& in, unsigned int version ) {
-		AExtraData::Read( in, version );
-		//GetAttr("Integer Data")->Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		AExtraData::Write( out, version );
-		//GetAttr("Integer Data")->Write( out, version );
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << AExtraData::asString()
-			<< "Integer Data:  " << GetAttr("Integer Data")->asString() << endl;
-
-		return out.str();
-	}
-};
-
-/*! Contains morphing animation data. */
-class NiMorphData : public AData {
-public:
-	NiMorphData() {
-		//AddAttr( attr_byte, "Unknown Byte" );
-	}
-	~NiMorphData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiMorphData"; };
-
-	/*! Retrieves the number of verticies used in the morph targets.  This must be the same as the number of verticies in the base mesh that the morph controller for which this block stores data is attatched.  This is not done automatically by Niflib.
-	 * \return The number of vertices used in the morph target meshes.
-	 * \sa IMorphData::SetVertexCount
-	 */
-	int GetVertexCount() const { return vertCount; }
-
-	/*! Sets the number of verticies used in the morph targets.  This must be the same as the number of verticies in the base mesh that the morph controller for which this block stores data is attatched.  This is not done automatically by Niflib.  If the new size is smaller, vertices at the ends of the morph targets will be lost.
-	 * \param n The new size of the morph target's vertex arrays.
-	 * \sa IMorphData::GetVertexCount
-	 */
-	void SetVertexCount( int n );
-
-	/*! Retrieves the number of morph targets used by this morph controller data.
-	 * \return The number of morph targets used by this morph controller data.
-	 * \sa IMorphData::SetMorphCount
-	 */
-	int GetMorphCount() const { return int(morphs.size()); }
-
-	/*! Resizes the morph target array used by this morph controller data.  If the new size is smaller, morph targets at the end of the array and all associated data will be lost.
-	 * \param n The new size of the morph target array.
-	 * \sa IMorphData::GetMorphCount
-	 */
-	void SetMorphCount( int n ) { morphs.resize( n ); }
-
-	/*! Retrieves the type of morph interpolation being used by a specific morph target.
-	 * \param n The index of the morph to get the interpolation key type from.  A zero-based positive value which must be less than that returned by IMoprhData::GetMorphCount.
-	 * \return The morph key type specifing the type of interpolation being used by the specified morph target.
-	 * \sa IMorphData::SetMorphKeyType
-	 */
-	KeyType GetMorphKeyType( int n ) const { return morphs[n]._type; }
-	
-	/*! Sets the type of morph interpolation being used by a specific morph target.  Does not affect existing key data.
-	 * \param n The index of the morph to get the interpolation key type from.  A zero-based positive value which must be less than that returned by IMoprhData::GetMorphCount.
-	 * \param t The new morph key type specifing the type of interpolation to be used by the specified morph target.
-	 * \sa IMorphData::GetMorphKeyType
-	 */
-	void SetMorphKeyType( int n, KeyType t ) { morphs[n]._type = t; }
-
-	/*! Retrieves the morph key data for a specified morph target.
-	 * \return A vector containing Key<float> data which specify the influence of this morph target over time.
-	 * \sa IMorphData::SetMorphKeys, Key
-	 */
-	vector< Key<float> > GetMorphKeys( int n ) const { return morphs[n].keys; }
-
-	/*! Sets the morph key data.
-	 * \param keys A vector containing new Key<float> data which will replace any existing data for this morph target.
-	 * \sa IMorphData::GetMorphKeys, Key
-	 */
-	void SetMorphKeys( int n, vector< Key<float> > const & keys ) { morphs[n].keys = keys; }
-
-	/*! Retrieves the vertex data from the specified morph target
-	 * \param n The index of the morph target to retrieve vertex data for.  This is a zero-based index whoes value that must be less than that returned by IMorphData::GetMorphCount.
-	 * \return A vector containing the vertices used by this morph target.  The size will be equal to the value returned by IMorphData::GetVertexCount.
-	 * \sa IMorphData::SetMorphVerts
-	 */
-	vector<Vector3> GetMorphVerts( int n) const { return morphs[n].morph; }
-
-	/*! Sets the vertex data for a specified morph target
-	 * \param n The index of the morph target to set vertex data for.  This is a zero-based index whoes value that must be less than that returned by IMorphData::GetMorphCount.
-	 * \param in A vector containing the new vertices to be used by this morph target.  The size will be equal to the value returned by IMorphData::GetVertexCount.
-	 * \sa IMorphData::SetMorphVerts
-	 */
-	void SetMorphVerts( int n, const vector<Vector3> & in );
-
-private:
-	struct Morph {
-		Morph() :  _type(QUADRATIC_KEY) {}
-		~Morph() {}
-		KeyType _type;
-		vector< Key<float> > keys;
-		vector< Vector3 > morph;
-	};
-	
-	uint vertCount;
-	vector<Morph> morphs;
-};
-
-/*! Contains an array of position keys. */
-class NiPosData : public AData {
-public:
-	NiPosData() {}
-	~NiPosData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiPosData"; }
-
-	/*! Retrieves the type of position interpolation being used.
-	 * \return The position key type specifing the type of interpolation being used.
-	 * \sa IPosData::SetKeyType
-	 */
-	KeyType GetKeyType() const { return _type; }
-
-	/*! Sets the type of position interpolation being used.  Does not affect existing key data.
-	 * \param t The new position key type specifing the type of interpolation to be used.
-	 * \sa IPosData::GetKeyType
-	 */
-	void SetKeyType( KeyType t ) { _type = t; }
-
-	/*! Retrieves the position key data.
-	 * \return A vector containing Key<Vector3> data which specify position over time.
-	 * \sa IPosData::SetKeys, Key
-	 */
-	vector< Key<Vector3> > GetKeys() const { return _keys; }
-
-	/*! Sets the position key data.
-	 * \param keys A vector containing new Key<Vector3> data which will replace any existing data.
-	 * \sa IPosData::GetKeys, Key
-	 */
-	void SetKeys( vector< Key<Vector3> > const & keys ) { _keys = keys; }
-
-private:
-	KeyType _type;
-	vector<Key<Vector3> > _keys;
-};
-
-class NiRotatingParticlesData : public ARotatingParticlesData {
-public:
-	NiRotatingParticlesData() {}
-	~NiRotatingParticlesData() {}
-
-	string GetBlockType() const { return "NiRotationparticlesData"; }
-};
-
-class NiParticlesData : public ARotatingParticlesData {
-public:
-	NiParticlesData() {}
-	~NiParticlesData() {}
-
-	string GetBlockType() const { return "NiParticlesData"; }
-};
-
-/*! Holds a list of textual notes and at which time they take effect which are used for designating the start and stop of animations and the triggering of sounds. */
-class NiTextKeyExtraData : public AExtraData {
-public:
-	NiTextKeyExtraData() {
-		//AddAttr( attr_int, "Unknown Int", 0, VER_4_2_2_0 );
-	}
-	~NiTextKeyExtraData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiTextKeyExtraData"; }
-
-	//--ITextKeyExtraData Functions--//
-	
-	/*! Retrieves the text note key data.
-	 * \return A vector containing Key<string> data which specify text note over time.
-	 * \sa IKeyframeData::SetKeys, Key
-	 */
-	virtual vector< Key<string> > GetKeys() const { return _keys; }
-
-	/*! Sets the text note key data.
-	 * \param keys A vector containing new Key<string> data which will replace any existing data.
-	 * \sa IKeyframeData::GetKeys, Key
-	 */
-	virtual void SetKeys( vector< Key<string> > const & keys ) { _keys = keys; }
-
-private:
-	vector< Key<string> > _keys;
-};
-
-class NiUVData : public AData {
-public:
-	NiUVData() {}
-	~NiUVData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiUVData"; }
-
-private:
-	struct UVGroup {
-		uint keyType;
-		vector<Key<float> > keys;
-	};
-	UVGroup groups[4];
-};
-
-class NiVertWeightsExtraData : public AExtraData{
-public:
-	NiVertWeightsExtraData() {}
-	~NiVertWeightsExtraData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiVertWeightsExtraData"; }
-
-private:
-	uint bytes;
-	vector<float> weights;
-};
-
-class NiVisData : public AData {
-public:
-	NiVisData() {}
-	~NiVisData() {}
-
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return "NiVisData"; }
-
-private:
-	vector<Key<byte> > keys;
-};
-
-class UnknownMixIn {
-public:
-	UnknownMixIn( string block_type ){
-		data = NULL;
-		_block_type = block_type;
-	}
-	~UnknownMixIn() { if (data != NULL) delete [] data; }
-	void Read( istream& in, unsigned int version );
-	void Write( ostream& out, unsigned int version ) const;
-	string asString() const;
-	string GetBlockType() const { return _block_type; }
-
-private:
-	string _block_type;
-	int len;
-	byte * data;
-};
-
-class UnknownBlock : public NiObject, public UnknownMixIn {
-public:
-	UnknownBlock( string block_type ) : UnknownMixIn(block_type) {}
-	~UnknownBlock() {}
-	void Read( istream& in, unsigned int version ) {
-		//cout << endl << "Unknown Block Type found:  " << GetBlockType() << "\a" << endl;
-		NiObject::Read( in, version );
-		UnknownMixIn::Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		NiObject::Write( out, version );
-		UnknownMixIn::Write( out, version );
-	}
-	void asString( ostream & out ) {
-		out << NiObject::asString();
-		out << UnknownMixIn::asString();
-	}
-	string GetBlockType() const { return UnknownMixIn::GetBlockType(); }
-};
-
-class UnknownControllerBlock : public AController, public UnknownMixIn {
-public:
-	UnknownControllerBlock( string block_type ) : UnknownMixIn(block_type) {}
-	~UnknownControllerBlock() {}
-	void Read( istream& in, unsigned int version ) {
-		NiObject::Read( in, version );
-		UnknownMixIn::Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		NiObject::Write( out, version );
-		UnknownMixIn::Write( out, version );
-	}
-	string asString() {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << NiObject::asString();
-		out << UnknownMixIn::asString();
-
-		return out.str();
-	}
-	string GetBlockType() const { return UnknownMixIn::GetBlockType(); }
-};
-
-class UnknownPropertyBlock : public AProperty, public UnknownMixIn {
-public:
-	UnknownPropertyBlock( string block_type ) : UnknownMixIn(block_type) {}
-	~UnknownPropertyBlock() {}
-	void Read( istream& in, unsigned int version ) {
-		NiObject::Read( in, version );
-		UnknownMixIn::Read( in, version );
-	}
-	void Write( ostream& out, unsigned int version ) const {
-		NiObject::Write( out, version );
-		UnknownMixIn::Write( out, version );
-	}
-	string asString() {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << NiObject::asString();
-		out << UnknownMixIn::asString();
-
-		return out.str();
-	}
-	string GetBlockType() const { return UnknownMixIn::GetBlockType(); }
-};
-
-/**
- * NiParticleSystemController
- */
-class NiParticleSystemController : public AParticleSystemController {
-public:
-	NiParticleSystemController();
-	void Init() {}
-	~NiParticleSystemController() {}
-	string GetBlockType() const { return "NiParticleSystemController"; }
-};
-
-/**
- * NiBSPArrayController
- */
-class NiBSPArrayController : public AParticleSystemController {
-public:
-	NiBSPArrayController();
-	void Init() {}
-	~NiBSPArrayController() {}
-	string GetBlockType() const { return "NiBSPArrayController"; }
-};
-
-/**
- * NiBoolInterpolator
- */
-class NiBoolInterpolator : public AInterpolator {
-public:
-	NiBoolInterpolator();
-	void Init() {}
-	~NiBoolInterpolator() {}
-	string GetBlockType() const { return "NiBoolInterpolator"; }
-};
-
-/**
- * NiBSplineCompFloatInterpolator
- */
-class NiBSplineCompFloatInterpolator : public AInterpolator {
-public:
-	NiBSplineCompFloatInterpolator();
-	void Init() {}
-	~NiBSplineCompFloatInterpolator() {}
-	string GetBlockType() const { return "NiBSplineCompFloatInterpolator"; }
-};
-
-/**
- * NiBSplineCompPoint3Interpolator
- */
-class NiBSplineCompPoint3Interpolator : public AInterpolator {
-public:
-	NiBSplineCompPoint3Interpolator();
-	void Init() {}
-	~NiBSplineCompPoint3Interpolator() {}
-	string GetBlockType() const { return "NiBSplineCompPoint3Interpolator"; }
-};
-
-/**
- * NiBSplineCompTransformInterpolator
- */
-class NiBSplineCompTransformInterpolator : public AInterpolator {
-public:
-	NiBSplineCompTransformInterpolator();
-	void Init() {}
-	~NiBSplineCompTransformInterpolator() {}
-	string GetBlockType() const { return "NiBSplineCompTransformInterpolator"; }
-};
-
-/**
- * NiFloatInterpolator
- */
-class NiFloatInterpolator : public AInterpolator {
-public:
-	NiFloatInterpolator();
-	void Init() {}
-	~NiFloatInterpolator() {}
-	string GetBlockType() const { return "NiFloatInterpolator"; }
-};
-
-/**
- * NiLookAtInterpolator
- */
-class NiLookAtInterpolator : public AInterpolator {
-public:
-	NiLookAtInterpolator();
-	//	AddAttr( attr_short, "Unknown Short", 0, 0xFFFFFFFF );
-
-	//	AddAttr( attr_link, "Unknown Link", 0, 0xFFFFFFFF );
-	//	Init();
-	//}
-
-	//void Read( istream& in, unsigned int version );
-	//void Write( ostream& out, unsigned int version ) const;
-	//string asString() const;
-
-	void Init() {}
-	~NiLookAtInterpolator() {}
-	string GetBlockType() const { return "NiLookAtInterpolator"; }
-private:
-	vector<float> unkFloats;
-	byte unkBytes[8];
-
-};
-
-/**
- * NiPoint3Interpolator
- */
-class NiPoint3Interpolator : public AInterpolator {
-public:
-	NiPoint3Interpolator();
-	void Init() {}
-	~NiPoint3Interpolator() {}
-	string GetBlockType() const { return "NiPoint3Interpolator"; }
-};
-
-/**
- * NiTransformInterpolator
- */
-class NiTransformInterpolator : public AInterpolator {
-public:
-	NiTransformInterpolator();
-	void Init() {}
-	~NiTransformInterpolator() {}
-	string GetBlockType() const { return "NiTransformInterpolator"; }
-};
-
-#endif
diff --git a/ComplexShape.h b/include/ComplexShape.h
similarity index 100%
rename from ComplexShape.h
rename to include/ComplexShape.h
diff --git a/Key.h b/include/Key.h
similarity index 100%
rename from Key.h
rename to include/Key.h
diff --git a/NIF_IO.h b/include/NIF_IO.h
similarity index 100%
rename from NIF_IO.h
rename to include/NIF_IO.h
diff --git a/Ref.h b/include/Ref.h
similarity index 100%
rename from Ref.h
rename to include/Ref.h
diff --git a/Type.h b/include/Type.h
similarity index 100%
rename from Type.h
rename to include/Type.h
diff --git a/dll_export.h b/include/dll_export.h
similarity index 100%
rename from dll_export.h
rename to include/dll_export.h
diff --git a/kfm.h b/include/kfm.h
similarity index 100%
rename from kfm.h
rename to include/kfm.h
diff --git a/nif_basic_types.h b/include/nif_basic_types.h
similarity index 100%
rename from nif_basic_types.h
rename to include/nif_basic_types.h
diff --git a/nif_math.h b/include/nif_math.h
similarity index 100%
rename from nif_math.h
rename to include/nif_math.h
diff --git a/nif_versions.h b/include/nif_versions.h
similarity index 100%
rename from nif_versions.h
rename to include/nif_versions.h
diff --git a/niflib.h b/include/niflib.h
similarity index 100%
rename from niflib.h
rename to include/niflib.h
diff --git a/pch.h b/include/pch.h
similarity index 100%
rename from pch.h
rename to include/pch.h
diff --git a/nif_attrs.h b/nif_attrs.h
deleted file mode 100644
index 8f9c774668182ef6e1174094de477e6b137fe30f..0000000000000000000000000000000000000000
--- a/nif_attrs.h
+++ /dev/null
@@ -1,1381 +0,0 @@
-/* Copyright (c) 2005, NIF File Format Library and Tools
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-   * Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-   * Redistributions in binary form must reproduce the above
-     copyright notice, this list of conditions and the following
-     disclaimer in the documentation and/or other materials provided
-     with the distribution.
-
-   * Neither the name of the NIF File Format Library and Tools
-     project nor the names of its contributors may be used to endorse
-     or promote products derived from this software without specific
-     prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE. */
-
-#ifndef _NIF_ATTRS_H_
-#define _NIF_ATTRS_H_
-
-#include <sstream>
-#include <list>
-#include "niflib_internal.h"
-
-#define endl "\r\n"
-
-
-class IntAttr : public AAttr {
-public:
-	IntAttr( string const & name, IBlock * owner, int * data ) : AAttr( name, owner ), _data(data) {}
-	~IntAttr() {}
-	AttrType GetType() const { return attr_int; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	int asInt() const { return *_data; }
-	void Set(int n ) { *_data = n; }
-protected:
-	int * _data;
-};
-
-class UIntAttr : public AAttr {
-public:
-	UIntAttr( string const & name, IBlock * owner, uint * data ) : AAttr( name, owner ), _data(data) {}
-	~UIntAttr() {}
-	AttrType GetType() const { return attr_int; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	int asInt() const { return int(*_data); }
-	void Set(int n ) { *_data = uint(n); }
-protected:
-	uint * _data;
-};
-
-class ShortAttr : public AAttr {
-public:
-	ShortAttr( string const & name, IBlock * owner, short * data ) : AAttr( name, owner ), _data(data) {}
-	~ShortAttr() {}
-	AttrType GetType() const { return attr_short; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	int asInt() const { return int(*_data); }
-	void Set(int n ) { *_data = short(n); }
-protected:
-	short * _data;
-};
-
-class UShortAttr : public AAttr {
-public:
-	UShortAttr( string const & name, IBlock * owner, ushort * data ) : AAttr( name, owner ), _data(data) {}
-	~UShortAttr() {}
-	AttrType GetType() const { return attr_short; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	int asInt() const { return int(*_data); }
-	void Set(int n ) { *_data = ushort(n); }
-protected:
-	ushort * _data;
-};
-
-class ByteAttr : public AAttr {
-public:
-	ByteAttr( string const & name, IBlock * owner, byte * data ) : AAttr( name, owner ), _data(data) {}
-	~ByteAttr() {}
-	AttrType GetType() const { return attr_byte; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << int(*_data);
-
-		return out.str();
-	}
-	int asInt() const { return int(*_data); }
-	void Set(int n ) { *_data = byte(n); }
-protected:
-	byte * _data;
-};
-
-class FloatAttr : public AAttr {
-public:
-	FloatAttr( string const & name, IBlock * owner, float * data ) : AAttr( name, owner ), _data(data) {}
-	~FloatAttr() {}
-	AttrType GetType() const { return attr_float; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	float asFloat() const { return *_data; }
-	void Set(float n ) { *_data = n; }
-protected:
-	float * _data;
-};
-
-class Float3Attr : public AAttr {
-public:
-	Float3Attr( string const & name, IBlock * owner, Float3 * data ) : AAttr( name, owner ), _data(data) {}
-	~Float3Attr() {}
-	AttrType GetType() const { return attr_float3; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	Float3 asFloat3() const { return *_data; }
-	void Set(Float3 const & n ) { *_data = n; }
-protected:
-	Float3 * _data;
-};
-
-class Float4Attr : public AAttr {
-public:
-	Float4Attr( string const & name, IBlock * owner, Float4 * data ) : AAttr( name, owner ), _data(data) {}
-	~Float4Attr() {}
-	AttrType GetType() const { return attr_float4; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	Float4 asFloat4() const { return *_data; }
-	void Set(Float4 const & n ) { *_data = n; }
-protected:
-	Float4 * _data;
-};
-
-class Vector3Attr : public AAttr {
-public:
-	Vector3Attr( string const & name, IBlock * owner, Vector3 * data ) : AAttr( name, owner ), _data(data) {}
-	~Vector3Attr() {}
-	AttrType GetType() const { return attr_vector3; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	Float3 asFloat3() const { return Float3(_data->x, _data->y, _data->z); }
-	void Set(Float3 const & n ) { 
-		_data->x = n.data[0];
-		_data->y = n.data[1];
-		_data->z = n.data[2];
-	}
-protected:
-	Vector3 * _data;
-};
-
-//class Color3Attr : public AAttr {
-//public:
-//	Color3Attr( string const & name, IBlock * owner, Color3 * data ) : AAttr( name, owner ), _data(data) {}
-//	~Color3Attr() {}
-//	AttrType GetType() const { return attr_color3; }
-//	string asString() const {
-//		stringstream out;
-//		out.setf(ios::fixed, ios::floatfield);
-//		out << setprecision(1);
-//
-//		out << *data;
-//
-//		return out.str();
-//	}
-//	Float3 asFloat3() const { return Float3(_data->r, _data->g, _data->b); }
-//	void Set(Float3 const & n ) { 
-//		_data->r = n.data[0];
-//		_data->g = n.data[1];
-//		_data->b = n.data[2];
-//	}
-//protected:
-//	Color3 * _data;
-//};
-
-//class Color4Attr : public AAttr {
-//public:
-//	Color4Attr( string const & name, IBlock * owner, Color4 * data ) : AAttr( name, owner ), _data(data) {}
-//	~Color4Attr() {}
-//	AttrType GetType() const { return attr_color4; }
-//	string asString() const {
-//		stringstream out;
-//		out.setf(ios::fixed, ios::floatfield);
-//		out << setprecision(1);
-//
-//		out << *data;
-//
-//		return out.str();
-//	}
-//	Float3 asFloat4() const { return Float3(_data->r, _data->g, _data->b, _data->a); }
-//	void Set(Float4 const & n ) { 
-//		_data->r = n.data[0];
-//		_data->g = n.data[1];
-//		_data->b = n.data[2];
-//		_data->a = n.data[2];
-//	}
-//protected:
-//	Color4 * _data;
-//};
-
-class QuaternionAttr : public AAttr {
-public:
-	QuaternionAttr( string const & name, IBlock * owner, Quaternion * data ) : AAttr( name, owner ), _data(data) {}
-	~QuaternionAttr() {}
-	AttrType GetType() const { return attr_quaternion; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	Float4 asFloat4() const { return Float4(_data->w, _data->x, _data->y, _data->z); }
-	void Set(Float4 const & n ) { 
-		_data->w = n.data[0];
-		_data->x = n.data[1];
-		_data->y = n.data[2];
-		_data->z = n.data[2];
-	}
-protected:
-	Quaternion * _data;
-};
-
-class StringAttr : public AAttr {
-public:
-	StringAttr( string const & name, IBlock * owner, string * data ) : AAttr( name, owner ), _data(data) {}
-	~StringAttr() {}
-	AttrType GetType() const { return attr_string; }
-	string asString() const { return *_data; }
-	void Set(string const & n) { *_data = n; }
-private:
-	string * _data;
-};
-
-class LinkAttr : public AAttr {
-public:
-	LinkAttr( string const & name, IBlock * owner, Link * data ) : AAttr( name, owner ), _data(data) {}
-	~LinkAttr() {}
-	AttrType GetType() const { return attr_link; }
-	
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	bool HasLinks() const { return true; }
-	list<blk_ref> asLinkList() const { 
-		list<blk_ref> out;
-
-		out.push_back( _data->GetLink() );
-
-		return out; 
-	}
-	void ClearLinks() { _data->Nullify(); }
-	void AddLinks( list<blk_ref> const & new_links ) {
-		//Just take the first one
-		_data->SetLink( *(new_links.begin()) );
-	}
-
-	blk_ref asLink() const { return _data->GetLink(); }
-	void Set( blk_ref const & n ) { _data->SetLink(n); }
-private:
-	Link * _data;
-};
-
-class CrossRefAttr : public AAttr {
-public:
-	CrossRefAttr( string const & name, IBlock * owner, CrossRef * data ) : AAttr( name, owner ), _data(data) {}
-	~CrossRefAttr() {}
-	AttrType GetType() const {
-		return attr_link;
-	}
-	string asString() const {
-		//cout << endl << "CrossRefAttr::asString()";
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	bool HasLinks() const { 
-		//cout << endl << "CrossRefAttr::HasLinks()";
-		return true;
-	}
-	list<blk_ref> asLinkList() const { 
-		//cout << endl << "CrossRefAttr::asLinkList()";
-		list<blk_ref> out;
-
-		out.push_back( blk_ref(_data->GetCrossRef()) );
-
-		return out; 
-	}
-	void ClearLinks() { 
-		//cout << endl << "CrossRefAttr::ClearLinks()";
-		_data->Nullify();
-	}
-	void AddLinks( list<blk_ref> const & new_links ) {
-		//cout << endl << "CrossRefAttr::AddLinks()";
-		//Just take the first one
-		_data->SetCrossRef( new_links.begin()->get_block() );
-	}
-
-	blk_ref asLink() const { return blk_ref(_data->GetCrossRef()); }
-	void Set( blk_ref const & n ) { 
-		_data->SetCrossRef( n.get_block() );
-	}
-private:
-	CrossRef * _data;
-};
-
-
-class FlagsAttr : public AAttr {
-public:
-	FlagsAttr( string const & name, IBlock * owner ) : AAttr( name, owner ), data(0) {}
-	~FlagsAttr() {}
-	AttrType GetType() const { return attr_flags; }
-	void ReadAttr( istream& in, unsigned int version ) { data = ReadUShort( in ); }
-	void WriteAttr( ostream& out, unsigned int version ) const { WriteUShort( data, out ); }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << data;
-		short t = data;
-		for (uint i = 0; i < 16; ++i) {
-			if((t & 1) !=0) {
-				out << endl << "   Bit " << i + 1 << " set ";
-			}
-			t >>= 1;
-		}
-
-		return out.str();
-	}
-	int asInt() const { return int(data); }
-	void Set(int n ) { data = short(n); }
-private:
-	short data;
-};
-
-class Matrix33Attr : public AAttr {
-public:
-	Matrix33Attr( string const & name, IBlock * owner, Matrix33 * data ) : AAttr( name, owner ), _data(data) {}
-	~Matrix33Attr() {}
-	AttrType GetType() const { return attr_matrix33; }
-	
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data << endl;
-
-		return out.str();
-	}
-	Matrix33 asMatrix33() const {
-		return *_data;
-	}
-	void Set( Matrix33 const & n ) {
-		*_data = n;
-	}
-private:
-	Matrix33 * _data;
-};
-
-class BoneAttr : public AAttr {
-public:
-	BoneAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~BoneAttr() {}
-	AttrType GetType() const { return attr_bones; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		ISkinInstInternal * data = (ISkinInstInternal*)_owner->QueryInterface( SkinInstInternal );
-		if ( data != NULL ) {
-			data->ReadBoneList( in );
-		} else {
-			throw runtime_error ("Attempted to use a bone list attribute on a block that doesn't support it.");
-		}
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		//ISkinInstInternal * data = (ISkinInstInternal*)_owner->QueryInterface( SkinInstInternal );
-		blk_ref data_blk = _owner->GetAttr("Data")->asLink();
-		if ( data_blk.is_null() == false )  {
-			//Get Bone data from data block
-			ISkinData * data = (ISkinData*)data_blk->QueryInterface( ID_SKIN_DATA );
-			vector<blk_ref> bones = data->GetBones();
-
-			//Write bone indices to file
-			WriteUInt( uint(bones.size()), out );
-			for (uint i = 0; i < bones.size(); ++i ) {
-				WriteUInt( bones[i].get_index(), out );
-			}
-		}
-		else {
-			//No data, so write zero for no bones
-			WriteUInt( 0, out );
-		}
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		//See if there is a data block
-		blk_ref data_blk = _owner->GetAttr("Data")->asLink();
-		if ( data_blk.is_null() == false && data_blk.is_fixed() == true )  {
-			//Get Bone data from data block
-			ISkinData * data = (ISkinData*)data_blk->QueryInterface( ID_SKIN_DATA );
-			vector<blk_ref> bones = data->GetBones();
-
-			//Print Bone List
-			for (uint i = 0; i < bones.size(); ++i ) {
-				out << endl << "   " << bones[i];
-			}
-			if ( bones.size() == 0 ) {
-				out << "None";
-			}
-		}
-		else {
-			//No data, so write none
-			out << "None";
-		}
-
-		return out.str();
-	}
-};
-*/
-
-class LinkGroupAttr : public AAttr {
-public:
-	LinkGroupAttr( string const & name, IBlock * owner, vector<Link> * data ) : AAttr( name, owner ), _data(data) {}
-	~LinkGroupAttr() {}
-	AttrType GetType() const { return attr_linkgroup; }
-	
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-		for (vector<Link>::const_iterator it = _data->begin(); it != _data->end(); ++it ) {
-			out << endl << "   " << it->GetLink();
-		}
-		if (_data->size() == 0 ) {
-			out << "None";
-		}
-
-		return out.str();
-	}
-
-	bool HasLinks() const { return true; }
-
-	list<blk_ref> asLinkList() const { 
-		list<blk_ref> out;
-
-		for (vector<Link>::iterator it = _data->begin(); it != _data->end(); ++it ) {
-			out.push_back( it->GetLink() );
-		}
-
-		return out; 
-	}
-
-	void AddLink( blk_ref const & block ) {
-		Link new_link(_owner);
-		new_link.SetLink( block );
-		_data->push_back( new_link);
-	}
-
-	void AddLinks( list<blk_ref> const & new_links ) {
-		//Add new list of links
-		for (list<blk_ref>::const_iterator it = new_links.begin(); it != new_links.end(); ++it ) {
-			Link new_link(_owner);
-			new_link.SetLink( *it );
-			try {
-				_data->push_back( new_link );
-			}
-			catch( exception & e ) {
-				cout << "Error:  " << e.what() << endl;
-			}
-		}
-	}
-
-	blk_ref FindLink( string const & block_type ) const {
-		//Find the first link with the requested block type
-		for (vector<Link>::const_iterator it = _data->begin(); it != _data->end(); ++it ) {
-			if ( it->GetLink()->GetBlockType() == block_type )
-				return it->GetLink();
-		}
-
-		//No block was found, so return a null one
-		return blk_ref(-1);
-	}
-
-	void ClearLinks() { _data->clear(); }
-
-	void RemoveLinks( blk_ref const & block ) {
-		//Remove all links that match this block
-		for (vector<Link>::iterator it = _data->begin(); it != _data->end(); ++it ) {
-			if ( it->GetLink() == block ) {
-				_data->erase( it );
-				return;
-			}
-		}
-	}
-protected:
-	vector<Link> * _data;
-};
-/*
-class TargetGroupAttr : public AAttr {
-public:
-	TargetGroupAttr( string const & name, IBlock * owner ) : AAttr( name, owner ) {
-		//cout << endl << "TargetGroupAttr()";
-	}
-	~TargetGroupAttr() {
-		//cout << endl << "~TargetGroupAttr()";
-		ClearLinks();
-	}
-
-	AttrType GetType() const { 
-		//cout << endl << "GetType()";
-		return attr_targetgroup;
-	}
-
-	void ReadAttr( istream& in, unsigned int version ) {
-		//cout << endl << "ReadAttr()";
-		int len = ReadUShort( in );
-
-		if ( len > 1000 ) {
-			throw runtime_error("Unlikley number of links found. (>1000)");
-		}
-
-		for (int i = 0; i < len; ++i ) {
-			CrossRefAttr * new_attr = new CrossRefAttr( "", _owner, 0, 0xFFFFFFFF );
-			new_attr->Read( in, version );
-			links.push_back( new_attr );
-		}
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		//cout << endl << "WriteAttr()";
-		//Write the number of links
-		WriteUShort( ushort(links.size()), out );
-		//cout << "Link Group Size:  " << uint(links.size()) << endl;
-
-		if ( links.size() > 1000 ) {
-			throw runtime_error("You probably shouldn't write more than 1000 links");
-		}
-
-		//Write the block indices
-		for ( uint i = 0; i < links.size(); ++i ) {
-			links[i]->Write( out, version );
-		}
-	}
-	string asString() const {
-		//cout << endl << "asString()";
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		for ( uint i = 0; i < links.size(); ++i ) {
-			out << endl << "   " << links[i]->asString();
-		}
-		if (links.size() == 0 ) {
-			out << "None";
-		}
-
-		return out.str();
-	}
-
-	bool HasLinks() const { 
-		//cout << endl << "HasLinks()";
-		return true;
-	}
-
-	list<blk_ref> asLinkList() const { 
-		//cout << endl << "asLinkList()";
-		list<blk_ref> out;
-
-		//cout << endl << "Link List size:  " << uint(links.size());
-		for ( uint i = 0; i < links.size(); ++i ) {
-			//cout << endl << "Link:  " << links[i]->asLink();
-			out.push_back( links[i]->asLink() );
-		}
-
-		//cout << endl << "Out List Size:  " << uint(out.size());
-
-		return out; 
-	}
-
-	void AddLink( blk_ref const & block ) {
-		//cout << endl << "AddLink()";
-		CrossRefAttr * new_attr = new CrossRefAttr( "", _owner, 0, 0xFFFFFFFF );
-		new_attr->Set( block );
-		links.push_back( new_attr );
-	}
-
-	void AddLinks( list<blk_ref> const & new_links ) {
-		//cout << endl << "AddLinks()";
-		//Add new list of links
-		list<blk_ref>::const_iterator it;
-		for (it = new_links.begin(); it != new_links.end(); ++it ) {
-			AddLink( *it );
-		}
-	}
-
-	blk_ref FindLink( string const & block_type ) const {
-		//cout << endl << "FindLink()";
-		//Find the first link with the requested block type
-		for ( uint i = 0; i < links.size(); ++i ) {
-			blk_ref found_block = links[i]->asLink();
-			if ( found_block->GetBlockType() == block_type ) {
-				return found_block;
-			}
-		}
-
-		//No block was found, so return a null one
-		return blk_ref(-1);
-	}
-
-	void ClearLinks() { 
-		//cout << endl << "ClearLinks()";
-
-		vector<CrossRefAttr*>::iterator it;
-		for ( it = links.begin(); it != links.end(); ++it ) {
-			delete *it;
-		}
-		links.clear();
-	}
-
-	void RemoveLinks( blk_ref const & block ) {
-		cout << endl << "RemoveLinks()";
-		//Remove all links that match this block
-		vector<CrossRefAttr*>::iterator it;
-		for ( it = links.begin(); it != links.end(); ++it ) {
-			if ( (*it)->asLink() == block ) {
-				delete *it;
-				links.erase( it );
-			}
-		}
-	}
-	void RemoveCrossLinks( IBlock * block_to_remove ) {
-		//cout << endl << "RemoveCrossLinks()";
-		vector<CrossRefAttr*>::iterator it;
-		for ( it = links.begin(); it != links.end(); ++it ) {
-			(*it)->RemoveCrossLinks( block_to_remove );
-			if ( (*it)->asLink().is_null() == true ) {
-				delete *it;
-				links.erase( it );
-			}
-		}
-	}
-private:
-	vector<CrossRefAttr*> links;
-};
-
-class ModifierGroupAttr : public LinkGroupAttr {
-public:
-	ModifierGroupAttr( string const & name, IBlock * owner ) : LinkGroupAttr( name, owner, first_ver, last_ver ) {}
-	~ModifierGroupAttr() {}
-
-	AttrType GetType() const { return attr_modifiergroup; }
-
-	void ReadAttr( istream& in, unsigned int version ) {
-
-		//The only difference is that there is a boolean before this link group
-		//bool has_links =
-		ReadBool( in, version );
-
-		//if ( has_links || version >= VER_10_2_0_0 ) {
-			LinkGroupAttr::ReadAttr( in, version );
-		//}
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		//The only difference is that there is a boolean before this link group
-		WriteBool( (links.size() > 0), out, version );
-
-		//if ( links.size() > 0 || version >= VER_10_2_0_0) {
-			LinkGroupAttr::WriteAttr( out, version );
-		//}
-
-	}
-};
-*/
-class BoundingBoxAttr : public AAttr {
-public:
-	BoundingBoxAttr( string const & name, IBlock * owner, BoundingBox * data ) : AAttr( name, owner ), _data(data) {}
-	~BoundingBoxAttr() {}
-	AttrType GetType() const { return attr_bbox; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << *_data;
-
-		return out.str();
-	}
-	BoundingBox asBoundingBox() const { return *_data; }
-	void Set(BoundingBox const & n ) { *_data = n; }
-
-private:
-	BoundingBox * _data;
-};
-
-/*
-class ShaderAttr : public LinkAttr {
-public:
-	ShaderAttr( string const & name, IBlock * owner ) : LinkAttr(name, owner, first_ver, last_ver), isUsed(false) {}
-	~ShaderAttr() {}
-	AttrType GetType() const { return attr_shader; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		isUsed = ReadBool( in, version );
-		if ( isUsed ) {	
-			//Read in shader name
-			_shader_name = ReadString( in );
-
-			//Read in unknown link
-			LinkAttr::ReadAttr( in, version );
-		}
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		WriteBool( isUsed, out, version );
-		if ( isUsed ) {	
-			//Write out shader name
-			WriteString( _shader_name, out );
-
-			//Write out unknown link
-			LinkAttr::WriteAttr( out, version );
-		}
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << "Shader:  ";
-
-		if ( isUsed ) {
-			out << endl
-				<< "   Shader Name:  " << _shader_name << endl
-				<< "   Unknown Link:  " << LinkAttr::asLink() << endl;
-		} else {
-			out << "None" << endl;
-		}
-
-		return out.str();
-	}
-
-protected:
-	bool isUsed;
-	string _shader_name;
-};
-*/
-
-/*class TexSourceAttr : public LinkAttr {
-public:
-	TexSourceAttr( string const & name, IBlock * owner ) : LinkAttr(name, owner ) {
-		data.unknownByte = 0;
-		data.useExternal = false;
-	}
-	~TexSourceAttr() {}
-	AttrType GetType() const { return attr_texsource; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		data.useExternal = ( ReadByte(in) != 0);
-
-		//All data is always read after version 10.1.0.0
-		if ( version >= VER_10_1_0_0 ) {
-			data.fileName = ReadString( in );
-			LinkAttr::ReadAttr( in, version );
-
-		} else if ( data.useExternal ) {
-			data.fileName = ReadString( in );
-		} else {
-			//Unknown byte exists up to version 10.0.1.0
-			if ( version <= VER_10_0_1_0 ) {
-				data.unknownByte = ReadByte ( in );
-			}
-
-			//Read link for Pixel Data
-			LinkAttr::ReadAttr( in, version );
-		}
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		WriteByte( byte(data.useExternal), out );
-
-		//All data is always written after version 10.1.0.0
-		if ( version >= VER_10_1_0_0 ) {
-			WriteString( data.fileName, out );
-			LinkAttr::WriteAttr( out, version );
-
-		} else if ( data.useExternal ) {
-			WriteString( data.fileName, out );
-		} else {
-			//Unknown byte exists up to version 10.0.1.0
-			if ( version <= VER_10_0_1_0 ) {
-				WriteByte ( data.unknownByte, out );
-			}
-
-			//Write link for Pixel Data
-			LinkAttr::WriteAttr( out, version );
-		}
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << endl
-			<< "   Location:  ";
-
-		if ( data.useExternal ) {
-			out << "External";
-		} else {
-			out << "Internal";
-		}
-
-        out << endl
-			<< "   File Name:  " << data.fileName << endl
-			<< "   Unknown Byte:  " << int(data.unknownByte) << endl
-			<< "   Pixel Data:  " << asLink();
-
-		return out.str();
-	}
-	TexSource asTexSource() const { return data; }
-	void Set( TexSource const &n ) {
-		data.useExternal = n.useExternal;
-		data.unknownByte = n.unknownByte;
-		data.fileName = n.fileName;
-	}
-private:
-	TexSource data;
-};*/
-
-/*class ControllerTargetAttr : public AAttr {
-public:
-	ControllerTargetAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~ControllerTargetAttr() {}
-	AttrType GetType() const { return attr_controllertarget; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		ReadUInt(in);
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		//WriteUInt( FindTarget()->GetBlockNum(), out );
-		WriteUInt( FindTarget().get_index(), out ); // we need get_index(), GetBlockNum() chokes on null block references
-	}
-	blk_ref FindTarget() const {
-		//Find first ancestor that is controllable
-		blk_ref block(_owner);
-		blk_ref par;
-		while ( true ) {
-			//Get parent
-			par = block->GetParent();
-
-			//If parent is null, we're done - there are no node ancestors so return a null reference
-			if (par.is_null() == true)
-				return blk_ref(-1);
-
-			// If parent is NiSequenceStreamHelper, return null reference (this is necessary to create consistent XKf files)
-			if ( par->GetBlockType() == "NiSequenceStreamHelper" )
-				return blk_ref(-1);
-
-			//If parent is controllable, return it
-			if ( par->IsControllable() == true )
-				return par;
-
-			//We didn't find a controllable object this time, set block to par and try again
-			block = par;
-		}
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << FindTarget();
-
-		return out.str();
-	}
-	blk_ref asLink() const { return FindTarget(); }
-	void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically.  You cannot change it directly."); }
-
-};*/
-
-class EmitterObjectAttr : public AAttr {
-public:
-	EmitterObjectAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~EmitterObjectAttr() {}
-	AttrType GetType() const { return attr_emitterobject; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		ReadUInt(in);
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		//WriteUInt( FindTarget()->GetBlockNum(), out );
-		WriteUInt( FindTarget().get_index(), out ); // we need get_index(), GetBlockNum() chokes on null block references
-	}
-	blk_ref FindTarget() const {
-		//Find first ancestor that is a node
-		blk_ref block(_owner);
-		blk_ref par;
-		while ( true ) {
-			//Get parent
-			par = block->GetParent();
-
-			//If parent is null, we're done - there are no node ancestors so return a null reference
-			if (par.is_null() == true)
-				return blk_ref(-1);
-
-			// If parent is NiSequenceStreamHelper, return null reference (this is necessary to create consistent XKf files)
-			if ( par->GetBlockType() == "NiSequenceStreamHelper" )
-				return blk_ref(-1);
-
-			//If parent is a node, return it
-			if ( QueryNode( par ) != NULL )
-				return par;
-
-			//We didn't find a node this time, set block to par and try again
-			block = par;
-		}
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << FindTarget();
-
-		return out.str();
-	}
-	blk_ref asLink() const { return FindTarget(); }
-	void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically.  You cannot change it directly."); }
-
-};
-
-class SelfLinkAttr : public AAttr {
-public:
-	SelfLinkAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~SelfLinkAttr() {}
-	AttrType GetType() const { return attr_emitterobject; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		ReadUInt(in);
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		//WriteUInt( FindTarget()->GetBlockNum(), out );
-		WriteUInt( _owner->GetBlockNum(), out );
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << blk_ref(_owner) << endl;
-
-		return out.str();
-	}
-	blk_ref asLink() const { return blk_ref(_owner); }
-	void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically.  You cannot change it directly."); }
-
-};
-
-class SkeletonRootAttr : public AAttr {
-public:
-	SkeletonRootAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~SkeletonRootAttr() {}
-	AttrType GetType() const { return attr_skeletonroot; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		original_root = ReadUInt( in );  //Read data but do nothing with it
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		WriteUInt( FindRoot().get_index(), out );
-	}
-	blk_ref FindRoot() const {
-		//Find Skeleton Root - first node in ancestry of any bone that has 'not a skin influence' flag set
-		
-		//Get skin data to find a bone
-		blk_ref skin_dat_blk = _owner->GetAttr("Data");
-
-		//If there is no skin data, return a null block
-		if ( skin_dat_blk.is_null() == true || skin_dat_blk.is_fixed() == false ) {
-			return blk_ref(-1);
-		}
-
-		//Get bones from skin data
-		ISkinData * skin_dat_int = QuerySkinData(skin_dat_blk);
-		vector<blk_ref> bones = skin_dat_int->GetBones();
-
-		//If size of bones is zero, return a null block
-		if ( bones.size() == 0 ) {
-			return blk_ref(-1);
-		}
-
-		//Arbitrarily start at the first bone in the list, and get it's ancestors
-		// We want to get the closest common ancestor between the _owner and the bones
-		// So start with a list of ancestors of the first bone (this is just a random choice)
-		blk_ref block = bones[0];
-		blk_ref par = block->GetParent();
-		list<blk_ref> bone_pars;
-		while ( par.is_null() == false ) {
-			bone_pars.push_front(par);
-			par = par->GetParent();
-		};
-		
-		// There is something odd going on with some of the DAoC files:
-		// all bones would refer to the scene root (see Atlantis/figures/fig03/lbody08_hig_m.nif).
-		// In this case the skeleton root also refers to the scene root; let's cover this case here.
-		if (bone_pars.empty()) return blk_ref(bones[0]);
-		
-		// Now do the same with the owner.
-		block = _owner;
-		par = block->GetParent();
-		list<blk_ref> owner_pars;
-		while ( par.is_null() == false ) {
-			owner_pars.push_front(par);
-			par = par->GetParent();
-		};
-		// Now find closest common ancestor.
-		if ( owner_pars.empty() )
-			throw runtime_error("Owner has no parents (invalid NIF file?). Cannot set skeleton root.");
-		blk_ref skelroot;
-		list<blk_ref>::const_iterator bone_par_it = bone_pars.begin();
-		list<blk_ref>::const_iterator owner_par_it = owner_pars.begin();
-		while ( *bone_par_it == *owner_par_it ) {
-			skelroot = *bone_par_it;
-			bone_par_it++;
-			owner_par_it++;
-		};
-		if ( skelroot.is_null() )
-			throw runtime_error("Skinning instance has no common parent with the bones it refers to (invalid NIF file?). Cannot set skeleton root.");
-		return skelroot;
-		/*NEW CODE END*/
-		/*OLD CODE BEGIN
-		blk_ref block = bones[0];
-		blk_ref par;
-		int flags;
-		while ( true ) {
-			//Get parent
-			par = block->GetParent();
-
-			//If parent is null, we're done - every node is an influence or there are no nodes
-			//Probably shouldn't happen
-			if (par.is_null() == true) {
-				return block;
-
-			}
-
-			//If parent is a node and its 'not a skin influence' flag is set, it is the root for this skeleton
-			if ( par->QueryInterface(ID_NODE) != NULL ) {
-				flags = par->GetAttr("Flags")->asInt();
-
-				if ( (flags & 8) != 0 ) {
-					// extra check: skeleton root cannot be a bone (this fixes the BabelFish.nif problem)
-					int par_is_bone = false;
-					for ( vector<blk_ref>::const_iterator it = bones.begin(); it != bones.end(); ++it) {
-						if ( *it == par ) {
-							par_is_bone = true;
-							break;
-						};
-					}
-					if ( ! par_is_bone ) return par;
-				}
-			}
-
-			//We didn't find the root this time, set block to par and try again
-			block = par;
-		}
-		//OLD CODE END
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		blk_ref root;
-		root = FindRoot();
-		out << root;
-
-		return out.str();
-	}
-	blk_ref asLink() const { return FindRoot(); }
-	void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically.  You cannot change it directly."); }
-
-private:
-	int original_root;
-};
-
-class ParentAttr : public AAttr {
-public:
-	ParentAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~ParentAttr() {}
-	AttrType GetType() const { return attr_parent; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		ReadUInt(in);
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		WriteUInt( _owner->GetParent()->GetBlockNum(), out );
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << _owner->GetParent();
-
-		return out.str();
-	}
-	blk_ref asLink() const { return _owner->GetParent(); }
-	void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically.  You cannot change it directly."); }
-};
-
-class ParticleGroupAttr : public AAttr {
-public:
-	ParticleGroupAttr( string const & name, IBlock * owner ) : AAttr(name, owner, first_ver, last_ver) {}
-	~ParticleGroupAttr() {}
-	AttrType GetType() const { return attr_particlegroup; }
-
-	void ReadAttr( istream& in, unsigned int version ) {
-		num_particles = ReadUShort( in );
-		num_valid = ReadUShort( in );
-
-		particles.resize(num_particles);
-		for ( int i = 0; i < num_particles; ++i ) {
-			for (int c = 0; c < 3; ++c) {
-				for (int r = 0; r < 3; ++r) {
-					particles[i].unk_matrix[r][c] = ReadFloat( in );
-				}
-			}
-			particles[i].unk_short = ReadUShort( in );
-			particles[i].vert_id = ReadUShort( in );
-		}
-	}
-
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		WriteUShort( num_particles, out );
-		WriteUShort( num_valid, out );
-
-		for ( int i = 0; i < num_particles; ++i ) {
-			for (int c = 0; c < 3; ++c) {
-				for (int r = 0; r < 3; ++r) {
-					WriteFloat( particles[i].unk_matrix[r][c], out );
-				}
-			}
-
-			WriteUShort( particles[i].unk_short, out );
-			WriteUShort( particles[i].vert_id, out );
-		}
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << "Num Particles:  " << num_particles << endl
-			<< "Num Valid:  " << num_valid << endl
-			<< "Particles:" << endl;
-
-		for ( int i = 0; i < num_particles; ++i ) {
-			out << "   Particle " << i << ":" << endl;
-			const Matrix33 & m = particles[i].unk_matrix;
-			out << "      |" << setw(6) << m[0][0] << "," << setw(6) << m[0][1] << "," << setw(6) << m[0][2] << " |" << endl
-				<< "      |" << setw(6) << m[1][0] << "," << setw(6) << m[1][1] << "," << setw(6) << m[1][2] << " |" << endl
-				<< "      |" << setw(6) << m[2][0] << "," << setw(6) << m[2][1] << "," << setw(6) << m[2][2] << " |" << endl;
-
-			out << "      Unknown Short:  " << particles[i].unk_short << endl
-				<< "      Vertex ID:  " << particles[i].vert_id << endl;
-		}
-		return out.str();
-	}
-	
-private:
-	struct Particle {
-		Matrix33 unk_matrix;
-		short unk_short;
-		short vert_id;
-	};
-	short num_particles;
-	short num_valid;
-	vector<Particle> particles;
-};
-
-
-
-class LODInfoAttr : public LinkAttr {
-public:
-	LODInfoAttr( string const & name, IBlock * owner ) : LinkAttr(name, owner, first_ver, last_ver) {}
-	~LODInfoAttr() {}
-	AttrType GetType() const { return attr_lodinfo; }
-
-	void ReadAttr( istream& file, unsigned int version ) {
-		NifStream( _type, file );
-		if ( _type == 0 ) {
-			NifStream( _center, file );
-			int numRanges = ReadUInt( file );
-			ranges.resize( numRanges );
-			NifStream( ranges, file );
-		} else {
-			NifStream( _unk_short, file );
-			LinkAttr::ReadAttr( file, version );
-		}
-	}
-
-	void WriteAttr( ostream& file, unsigned int version ) const {
-		NifStream( _type, file );
-		if ( _type == 0 ) {
-			NifStream( _center, file );
-			WriteUInt( uint(ranges.size()), file );
-			NifStream( ranges, file );
-		} else {
-			NifStream( _unk_short, file );
-			LinkAttr::WriteAttr( file, version );
-		}
-	}
-
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << "   LOD Info Type:  " << _type << endl;
-
-		if ( _type == 0 ) {
-			out << "   LOD Center:  (" << _center.x << ", " << _center.y << ", " << _center.z << ")" << endl;
-
-			out << uint(ranges.size()) << endl;
-
-			for ( uint i = 0; i < ranges.size(); ++i ) {
-				out << "   " << i + 1 << ")   Near:  " << ranges[i].near << "   Far:  " << ranges[i].far << endl;
-			}
-		} else {
-			out << "Unknown Short:  " << _unk_short << endl
-				<< "Range Group Link:  " << LinkAttr::asString() << endl;
-		}
-
-		return out.str();
-	}
-	
-private:
-	uint _type;
-	Vector3 _center;
-	vector<LODRange> ranges;
-	ushort _unk_short;
-};
-
-
-class Unk292BytesAttr : public AAttr {
-public:
-	Unk292BytesAttr( string const & name, IBlock * owner ) : AAttr( name, owner ) {
-		for ( int i = 0; i < 256; ++i ) {
-			data[i] = 0;
-		}
-		//memset( data, 0, 256 );
-	}
-	~Unk292BytesAttr() {}
-	AttrType GetType() const { return attr_unk292bytes; }
-	void ReadAttr( istream& in, unsigned int version ) {
-		in.read( (char*)data, 256 );
-	}
-	void WriteAttr( ostream& out, unsigned int version ) const {
-		out.write( (char*)data, 265 );
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << "Unknown Data (256 bytes):" << endl;
-	
-		//Display Data in Hex form
-		out << hex << setfill('0');
-		for (int j = 0; j < 256; j++) {
-			out << uppercase << setw(2) << uint(data[j]);
-			if (j % 16 == 15 || j == 256 - 1)
-				out << endl;
-			else if (j % 16 == 7)
-				out << "   ";
-			else if (j % 8 == 3)
-				out << "  ";
-			else
-				out << " ";
-		}
-		out << dec << setfill(' ');
-
-		//Display data as a string
-		out << "As String:  ";
-		for (int j = 0; j < 256; j++ ) {
-			if ( data[j] == 0 ) {
-				out << endl;
-			} else if ( data[j] == 0xCD ) {
-				break;
-			} else {
-				out << data[j];
-			}
-		}
-
-		return out.str();
-	}
-protected:
-	byte data[256];
-};*/
-
-
-#endif
diff --git a/ComplexShape.cpp b/src/ComplexShape.cpp
similarity index 100%
rename from ComplexShape.cpp
rename to src/ComplexShape.cpp
diff --git a/NIF_IO.cpp b/src/NIF_IO.cpp
similarity index 100%
rename from NIF_IO.cpp
rename to src/NIF_IO.cpp
diff --git a/Type.cpp b/src/Type.cpp
similarity index 100%
rename from Type.cpp
rename to src/Type.cpp
diff --git a/kfm.cpp b/src/kfm.cpp
similarity index 100%
rename from kfm.cpp
rename to src/kfm.cpp
diff --git a/nif_math.cpp b/src/nif_math.cpp
similarity index 100%
rename from nif_math.cpp
rename to src/nif_math.cpp
diff --git a/niflib.cpp b/src/niflib.cpp
similarity index 100%
rename from niflib.cpp
rename to src/niflib.cpp
diff --git a/pch.cpp b/src/pch.cpp
similarity index 100%
rename from pch.cpp
rename to src/pch.cpp