diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
index 077086e87f371548ab8eed8a70834ccb5e5aad02..74029b508b9667b7caa02f987d7fc1d93e930d1d 100644
--- a/NIF_Blocks.cpp
+++ b/NIF_Blocks.cpp
@@ -67,6 +67,7 @@ ABlock::~ABlock() {
 		delete _attr_vect[i].ptr();
 	}
 
+	//cout << endl << "Removing cross reference to " << this << " from " << uint(_cross_refs.size()) << " blocks";
 	// Inform all cross-linked blocks that have added their references that this block is dying
 	list<IBlock*>::iterator it;
 	for (it = _cross_refs.begin(); it != _cross_refs.end(); ++it) {
@@ -180,6 +181,15 @@ void ABlock::AddAttr( AttrType type, string const & name, unsigned int first_ver
 		case attr_quaternion:
 			attr = new QuaternionAttr( name, this, first_ver, last_ver );	
 			break;
+		case attr_emitterobject:
+			attr = new EmitterObjectAttr( name, this, first_ver, last_ver );	
+			break;
+		case attr_selflink:
+			attr = new SelfLinkAttr( name, this, first_ver, last_ver );	
+			break;
+		case attr_crossref:
+			attr = new CrossRefAttr( name, this, first_ver, last_ver );	
+			break;
 		default:
 			cout << type << endl;
 			throw runtime_error("Unknown attribute type requested.");
@@ -357,6 +367,15 @@ void ABlock::RemoveChild( IBlock * old_child ) {
 	//}
 }
 
+void ABlock::RemoveCrossLink( IBlock * block_to_remove ) {
+	//Ask all attributes to remove any cross links they might have to the specified block
+	//cout << endl << "ABlock::RemoveCrossLink()";
+	vector<attr_ref>::iterator it;
+	for ( it = _attr_vect.begin(); it != _attr_vect.end(); ++it ) {
+		((AAttr*)it->ptr())->RemoveCrossLinks( block_to_remove );
+	}
+}
+
 
 void ABlock::AddParent( IBlock * new_parent) { 
 	//Don't add null parents
@@ -1772,14 +1791,17 @@ void NiMeshPSysData::Read( istream& file, unsigned int version ) {
 	unkFloats.resize( vertices.size() * 14 );
 	NifStream( unkFloats, file );
 
-	NifStream( unk2Ints[0], file );
-	NifStream( unk2Ints[1], file );
+	NifStream( unkInt, file );
 
-	NifStream( unkByte, 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 );
 
-	NifStream( unk3Ints[0], file );
-	NifStream( unk3Ints[1], file );
-	NifStream( unk3Ints[2], file );
+		GetAttr("Unknown Link Group")->Read( file, version );
+		GetAttr("Unknown Link 2")->Read( file, version );
+	}
 }
 
 void NiMeshPSysData::Write( ostream& file, unsigned int version ) const {
@@ -1787,14 +1809,17 @@ void NiMeshPSysData::Write( ostream& file, unsigned int version ) const {
 
 	NifStream( unkFloats, file );
 
-	NifStream( unk2Ints[0], file );
-	NifStream( unk2Ints[1], file );
+	NifStream( unkInt, file );
 
-	NifStream( unkByte, file );
+	GetAttr("Modifier")->Write( file, version );
 
-	NifStream( unk3Ints[0], file );
-	NifStream( unk3Ints[1], file );
-	NifStream( unk3Ints[2], file );
+	// 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 {
@@ -1814,12 +1839,11 @@ string NiMeshPSysData::asString() const {
 		out << "   <<<Data Not Shown>>>";
 	}
 
-	out << "Unknown Int 1:  " << unk2Ints[0] << endl
-		<< "Unknown Int 2:  " << unk2Ints[1] << endl
+	out << "Unknown Int:  " << unkInt << endl
+		<< "Modifier:  " << GetAttr("Modifier")->asString() << endl
 		<< "Unknown Byte:  " << unkByte << endl
-		<< "Unknown Int 3:  " << unk3Ints[0] << endl
-		<< "Unknown Int 4:  " << unk3Ints[1] << endl
-		<< "Unknown Int 5:  " << unk3Ints[2] << endl;
+		<< "Unknown Link Group:  " << GetAttr("Unknown Link Group")->asString() << endl
+		<< "Unknown Link 2:  " << GetAttr("Unknown Link 2")->asString() << endl;
 
 	return out.str();
 }
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
index e1268c3a2183387a4568839f50270ccdae60a372..6368624a9d7215c2845462d9b2aada31a93d2ef6 100644
--- a/NIF_Blocks.h
+++ b/NIF_Blocks.h
@@ -138,7 +138,7 @@ public:
 	void AddChild( IBlock * new_child );
 	void RemoveChild( IBlock * old_child );
 
-	virtual void RemoveCrossLink( IBlock * block_to_remove ) {};
+	virtual void RemoveCrossLink( IBlock * block_to_remove );
 
 	void IncCrossRef( IBlock * block );
 	void DecCrossRef( IBlock * block );
@@ -877,7 +877,11 @@ protected:
 
 class NiMeshPSysData : public APSysData {
 public:
-	NiMeshPSysData() {}
+	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;
@@ -885,7 +889,7 @@ public:
 	string GetBlockType() const { return "NiMeshPSysData"; };
 protected:
 	vector<float> unkFloats;
-	uint unk2Ints[2];
+	uint unkInt;
 	byte unkByte;
 	uint unk3Ints[3];
 };
diff --git a/docsys_extract.cpp b/docsys_extract.cpp
index 72e4dc5a90882a76ffbb6aa044229a53d86ec1ba..686466457b8af3726f473040ea945f065680e96c 100644
--- a/docsys_extract.cpp
+++ b/docsys_extract.cpp
@@ -182,7 +182,7 @@ APSysModifier::APSysModifier() {
 }
 
 APSysVolumeEmitter::APSysVolumeEmitter() {
-	AddAttr( attr_link, "Emitter Object", 335544324, 0xFFFFFFFF );
+	AddAttr( attr_emitterobject, "Emitter Object", 335544324, 0xFFFFFFFF );
 	Init();
 }
 
@@ -577,7 +577,7 @@ NiPSysColliderManager::NiPSysColliderManager() {
 	AddAttr( attr_bool, "Spawn on Collide", 0, 0xFFFFFFFF );
 	AddAttr( attr_bool, "Die on Collide", 0, 0xFFFFFFFF );
 	AddAttr( attr_link, "Spawn Modifier", 0, 0xFFFFFFFF );
-	AddAttr( attr_link, "Manager", 0, 0xFFFFFFFF );
+	AddAttr( attr_selflink, "Manager", 0, 0xFFFFFFFF );
 	AddAttr( attr_link, "Unknown Link?", 0, 0xFFFFFFFF );
 	AddAttr( attr_link, "Collider Object", 0, 0xFFFFFFFF );
 	Init();
@@ -614,7 +614,7 @@ NiPSysEmitterInitialRadiusCtlr::NiPSysEmitterInitialRadiusCtlr() {
 }
 
 NiPSysGravityModifier::NiPSysGravityModifier() {
-	AddAttr( attr_link, "Gravity Object", 0, 0xFFFFFFFF );
+	AddAttr( attr_crossref, "Gravity Object", 0, 0xFFFFFFFF );
 	AddAttr( attr_vector3, "Gravity Axis", 0, 0xFFFFFFFF );
 	AddAttr( attr_float, "Decay", 0, 0xFFFFFFFF );
 	AddAttr( attr_float, "Strength", 0, 0xFFFFFFFF );
diff --git a/nif_attrs.h b/nif_attrs.h
index a8a9af40a202b9671f791a3a62cc8df977f0ddff..52f68a6a12323cce4abf6693ebe4e96c23aa62b5 100644
--- a/nif_attrs.h
+++ b/nif_attrs.h
@@ -91,6 +91,8 @@ public:
 			this->WriteAttr( out, version );
 		}
 	}
+	//Function to remove CrossLinks without decrimenting them
+	virtual void RemoveCrossLinks( IBlock * block_to_remove ) {};
 protected:
 	//Internal Read/Write Functions
 	virtual void ReadAttr( istream& in, unsigned int version ) = 0;
@@ -450,6 +452,127 @@ private:
 	lnk_ref link;
 };
 
+class CrossRefAttr : public AAttr {
+public:
+	CrossRefAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), link(NULL), tmp_blk_num(-1) {}
+	~CrossRefAttr() {
+		//cout << endl << "~CrossRefAttr()";
+		//This attribute is dying, so decriment the link
+		UpdateLink(NULL);
+	}
+	AttrType GetType() const { 
+		//cout << endl << "CrossRefAttr::GetType()";
+		return attr_link;
+	}
+	void ReadAttr( istream& in, unsigned int version ) {
+		//cout << endl << "CrossRefAttr::ReadAttr()";
+		//Update any existing links to null
+		UpdateLink(NULL);
+
+		tmp_blk_num = ReadUInt( in );
+	}
+	void WriteAttr( ostream& out, unsigned int version ) const {
+		//cout << endl << "CrossRefAttr::WriteAttr()";
+		if ( link == NULL ) {
+			WriteUInt( -1, out );
+		} else {
+			WriteUInt( link->GetBlockNum(), out );
+		}
+	}
+	string asString() const {
+		//cout << endl << "CrossRefAttr::asString()";
+		stringstream out;
+		out.setf(ios::fixed, ios::floatfield);
+		out << setprecision(1);
+
+		out << GetBlkRef();
+
+		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( GetBlkRef() );
+
+		return out; 
+	}
+	void ClearLinks() { 
+		//cout << endl << "CrossRefAttr::ClearLinks()";
+		UpdateLink( NULL );
+	}
+	void AddLinks( list<blk_ref> const & new_links ) {
+		//cout << endl << "CrossRefAttr::AddLinks()";
+		//Just take the first one
+		UpdateLink( (*(new_links.begin())).get_block() );
+	}
+
+	void UpdateLink( IBlock * new_ptr ) {
+		//cout << endl << "CrossRefAttr::UpdateLink()";
+		//cout << endl << "Setting tmp_blk_num to -1";
+		//The link cannot be temporary after being updated
+		tmp_blk_num = -1;
+
+		/*cout << endl << "Check if new pointer is same as old"
+			<< endl << "Old Value:  " << link
+			<< endl << "New Value:  " << new_ptr;*/
+		//Check if the new pointer is the same as the old pointer
+		if ( new_ptr == link )
+			return;
+
+		//cout << endl << "If different, decrement ref to old site if it exists.";
+		//The new pointer is different, so decrement the reference at the old site if it exists
+		if ( link != NULL ) {
+			//cout << endl << "Decrimenting Cross Reference at:  " << link;
+			((ABlock*)link)->DecCrossRef( _owner );
+		}
+
+		//Now set the pointer to the new location
+		link = new_ptr;
+
+		//If link is not null, we need to increment the cross reference count
+		if ( link != NULL ) {
+			//cout << endl << "Incrementing Cross Reference at:  " << link;
+			((ABlock*)link)->IncCrossRef( _owner );
+		}
+	}
+
+	blk_ref GetBlkRef() const {
+		//cout << endl << "CrossRefAttr::GetBlkRef()";
+		//If the link is not fixed, there will be a temporary block number
+		if ( tmp_blk_num != -1 ) {
+			return blk_ref(tmp_blk_num);
+		} else {
+			if ( link == NULL ) {
+				return blk_ref(-1);
+			} else {
+				return blk_ref(link);
+			}
+		}
+	}
+
+	blk_ref asLink() const { return GetBlkRef(); }
+	void Set( blk_ref const & n ) { 
+		//cout << endl << "CrossRefAttr::Set()";
+		UpdateLink( n.get_block() );
+	}
+	void RemoveCrossLinks( IBlock * block_to_remove ) {
+		//cout << endl << "CrossRefAttr::RemoveCrossLinks()";
+		if ( link == block_to_remove ) {
+			link = NULL;
+			//Do not decrement target location because it is already dead
+		}
+	}
+private:
+	int tmp_blk_num;
+	IBlock * link;
+};
+
+
 class FlagsAttr : public AAttr {
 public:
 	FlagsAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {}
@@ -713,14 +836,23 @@ protected:
 	LinkSetList links;
 };
 
-class TargetGroupAttr : public LinkGroupAttr {
+class TargetGroupAttr : public AAttr {
 public:
-	TargetGroupAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : LinkGroupAttr( name, owner, first_ver, last_ver ) {}
-	~TargetGroupAttr() {}
+	TargetGroupAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) {
+		//cout << endl << "TargetGroupAttr()";
+	}
+	~TargetGroupAttr() {
+		//cout << endl << "~TargetGroupAttr()";
+		ClearLinks();
+	}
 
-	AttrType GetType() const { return attr_targetgroup; }
+	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 ) {
@@ -728,12 +860,13 @@ public:
 		}
 
 		for (int i = 0; i < len; ++i ) {
-			int index = ReadUInt( in );
-			if (index != -1 )
-				AddLink( blk_ref( index ) );
+			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;
@@ -743,10 +876,110 @@ public:
 		}
 
 		//Write the block indices
-		for (LinkSetConstIt it = links.begin(); it != links.end(); ++it ) {
-			WriteUInt( it->get_index(), out );
+		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 {
@@ -1244,6 +1477,56 @@ public:
 			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, unsigned int first_ver, unsigned int last_ver ) : 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;
 		}
@@ -1262,6 +1545,32 @@ public:
 
 };
 
+class SelfLinkAttr : public AAttr {
+public:
+	SelfLinkAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : 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, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {}
diff --git a/niflib.h b/niflib.h
index 95e202a86d20964d7b576c8c79c25c6cdb020ec1..4ca29c70cd309ebc5ebbdcdc60e1ca3b993f89a9 100644
--- a/niflib.h
+++ b/niflib.h
@@ -112,7 +112,8 @@ enum AttrType {
 	attr_float3, /*!< Float3 Attribute.  Holds a Float3 structure. */ 
 	attr_float4, /*!< Float4 Attribute.  Holds a Float4 structure. */ 
 	attr_string, /*!< String Attribute.  Holds a text string. */ 
-	attr_link, /*!< Link Attribute.  Links to one other Nif block lower in the Nif tree. */ 
+	attr_link, /*!< Link Attribute.  Links to one other Nif block lower in the Nif tree. */
+	attr_crossref, /*!< Cross Reference Attribute.  Links to one other Nif block higher in the Nif tree.  Will be automatically cleared if the block it links to is destroyed. */ 
 	attr_flags, /*!< Flags Attribute.  Holds a set of 16 bit flags whos function depends on the block that uses them. */ 
 	attr_matrix33, /*!< Matrix33 Attribute.  Holds a Matrix33 structure. */ 
 	attr_linkgroup, /*!< Link Group Attribute.  Links to several other Nif blocks lower in the Nif tree. */ 
@@ -129,6 +130,8 @@ enum AttrType {
 	attr_mipmapformat, /*!< Mipmap Format Attribute.  Holds an integer that corresponds to the mipmap format. */ 
 	attr_modifiergroup, /*!< A link group conditioned on a boolean value. */
 	attr_alphaformat, /*!< Alpha Format Attribute.  Holds an integer that corresponds to the alpha format. */ 
+	attr_selflink,  /*!< Self Link Attribute.  Automatic. */ 
+	attr_emitterobject,  /*!< Emitter Object Attribute.  Automatic. */ 
 	attr_controllertarget, /*!< Controller Target Attribute.  Automatic. */ 
 	attr_skeletonroot, /*!< Skeleton Root Attribute.  Automatic. */ 
 	attr_particlegroup, /*!< Particle Group Attribute. */