From 20c73722384dc63b8cdd5c01d9c2d1c159e1b510 Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Fri, 27 Jan 2006 01:21:28 +0000
Subject: [PATCH] All blocks now able to read and write version 20.0.0.4 files
 except NiControllerSequence.

---
 NIF_Blocks.cpp     | 215 ++++++++++++++++++++++++----------
 NIF_Blocks.h       |  31 +++--
 NIF_IO.cpp         |  22 ++++
 NIF_IO.h           |   3 +
 docsys_extract.cpp |  44 +++----
 nif_attrs.h        | 286 ++++-----------------------------------------
 niflib.cpp         |   4 +-
 niflib.h           |   3 +-
 8 files changed, 239 insertions(+), 369 deletions(-)

diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
index 52069175..0c4d519a 100644
--- a/NIF_Blocks.cpp
+++ b/NIF_Blocks.cpp
@@ -109,12 +109,6 @@ void ABlock::AddAttr( AttrType type, string const & name, unsigned int first_ver
 		attr = new VertModeAttr( name, this, first_ver, last_ver );
 	} else if ( type == attr_lightmode ) {
 		attr = new LightModeAttr( name, this, first_ver, last_ver );
-	} else if ( type == attr_texture ) {
-		attr = new TexDescAttr( name, this, first_ver, last_ver );
-	} else if ( type == attr_bumpmap ) {
-		attr = new BumpMapAttr( name, this, first_ver, last_ver );
-	} else if ( type == attr_applymode ) {
-		attr = new ApplyModeAttr( name, this, first_ver, last_ver );
 	} else if ( type == attr_texsource ) {
 		attr = new TexSourceAttr( name, this, first_ver, last_ver );
 	} else if ( type == attr_pixellayout ) {
@@ -143,6 +137,8 @@ void ABlock::AddAttr( AttrType type, string const & name, unsigned int first_ver
 		attr = new BoolAttr( name, this, first_ver, last_ver );
 	} else if ( type == attr_targetgroup ) {
 		attr = new TargetGroupAttr( name, this, first_ver, last_ver );
+	} else if ( type == attr_shader ) {
+		attr = new ShaderAttr( name, this, first_ver, last_ver );
 	} else {
 		cout << type << endl;
 		throw runtime_error("Unknown attribute type requested.");
@@ -651,7 +647,9 @@ void NiTexturingProperty::Read( ifstream& file, unsigned int version ){
 
 		for ( uint i = 0; i < extra_textures.size(); ++i ) {
 			NifStream( extra_textures[i].first, file, version );
-			NifStream( extra_textures[i].second, file );
+			if ( extra_textures[i].first.isUsed ) {
+				NifStream( extra_textures[i].second, file );
+			}
 		}
 	}
 }
@@ -683,7 +681,9 @@ void NiTexturingProperty::Write( ofstream& file, unsigned int version ) const {
 
 		for ( uint i = 0; i < extra_textures.size(); ++i ) {
 			NifStream( extra_textures[i].first, file, version );
-			NifStream( extra_textures[i].second, file );
+			if ( extra_textures[i].first.isUsed ) {
+				NifStream( extra_textures[i].second, file );
+			}
 		}
 	}
 }
@@ -1613,8 +1613,30 @@ string NiMeshPSysData::asString() const {
 void NiPSysData::Read( ifstream& file, unsigned int version ) {
 	APSysData::Read( file, version );
 
-	unkFloats.resize( vertices.size() * 10 );
-	NifStream( unkFloats, file );
+	//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 );
 }
@@ -1622,7 +1644,22 @@ void NiPSysData::Read( ifstream& file, unsigned int version ) {
 void NiPSysData::Write( ofstream& file, unsigned int version ) const {
 	APSysData::Write( file, version );
 
-	NifStream( unkFloats, file );
+	//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 );
 }
@@ -1644,6 +1681,26 @@ string NiPSysData::asString() const {
 		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();
@@ -3816,20 +3873,26 @@ void NiPixelData::Read( ifstream& file, unsigned int version ) {
 
 	pxFormat = PixelFormat( ReadUInt(file) );
 
-	NifStream( redMask, file );
-	NifStream( greenMask, file );
-	NifStream( blueMask, file );
-	NifStream( alphaMask, 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 );
+		NifStream( bpp, file );
 
-	for ( int i = 0; i < 8; ++i ) {
-		NifStream( unk8Bytes[i], file );
-	}
+		for ( int i = 0; i < 8; ++i ) {
+			NifStream( unk8Bytes[i], file );
+		}
 
-	//There is an unknown int here from version 10.1.0.0 on
-	if ( version >= VER_10_1_0_0 ) {
-		NifStream( unkInt, 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 );
@@ -3850,6 +3913,12 @@ void NiPixelData::Read( ifstream& file, unsigned int version ) {
 
 	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);
 }
 
@@ -3858,20 +3927,23 @@ void NiPixelData::Write( ofstream& file, unsigned int version ) const {
 
 	WriteUInt( uint(pxFormat), file );
 
-	NifStream( redMask, file );
-	NifStream( greenMask, file );
-	NifStream( blueMask, file );
-	NifStream( alphaMask, 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 );
+		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 on
-	if ( version >= VER_10_1_0_0 ) {
-		NifStream( unkInt, 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 );
@@ -3895,6 +3967,12 @@ void NiPixelData::Write( ofstream& file, unsigned int version ) const {
 	}
 
 	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);
 }
 
@@ -3934,7 +4012,15 @@ string NiPixelData::asString() const {
 	}
 
 	out << "Unknown Int:  " << unkInt << endl
-		<< "Palette:  "  << GetAttr("Palette")->asLink() << 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
@@ -3972,7 +4058,8 @@ string NiPixelData::asString() const {
 #endif
 	}
 
-	out << "Mipmap Image Data:  "  << dataSize << " Bytes (Not Shown)" << endl;
+	out << "Unknown Int 2:  " << unkInt2 << endl
+		<< "Mipmap Image Data:  "  << dataSize << " Bytes (Not Shown)" << endl;
 	
 	return out.str();
 }
@@ -4562,34 +4649,34 @@ string NiVisData::asString() const {
  * NiLookAtInterpolator methods
  **********************************************************/
 
-void NiLookAtInterpolator::Read( ifstream& 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( ofstream& file, unsigned int version ) const {
-
-}
-
-string NiLookAtInterpolator::asString() const {
-	stringstream out;
-	out.setf(ios::fixed, ios::floatfield);
-	out << setprecision(1);
-
-	return out.str();
-}
+//void NiLookAtInterpolator::Read( ifstream& 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( ofstream& file, unsigned int version ) const {
+//
+//}
+//
+//string NiLookAtInterpolator::asString() const {
+//	stringstream out;
+//	out.setf(ios::fixed, ios::floatfield);
+//	out << setprecision(1);
+//
+//	return out.str();
+//}
 
 
 /***********************************************************
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
index d30ea18a..648ceebf 100644
--- a/NIF_Blocks.h
+++ b/NIF_Blocks.h
@@ -648,6 +648,8 @@ private:
 	vector<MipMap> mipmaps;
 	uint dataSize;
 	byte * data;
+	byte unk54Bytes[54];
+	uint unkInt2;
 };
 
 /**
@@ -830,6 +832,11 @@ public:
 protected:
 	vector<float> unkFloats;
 	uint unkInt;
+	bool unkBool1;
+	vector<byte> unkBytes1;
+	byte unkByte;
+	bool unkBool2;
+	vector<byte> unkBytes2;
 };
 
 /**
@@ -2224,8 +2231,6 @@ private:
 class NiIntegerExtraData : public AExtraData {
 public:
 	NiIntegerExtraData() {
-		AddAttr( attr_link, "Unknown Link", VER_20_0_0_4 );
-		AddAttr( attr_string, "Unknown String", VER_20_0_0_4 );
 		AddAttr( attr_int, "Integer Data" );
 	}
 	~NiIntegerExtraData() {}
@@ -2234,14 +2239,10 @@ public:
 
 		void Read( ifstream& in, unsigned int version ) {
 		AExtraData::Read( in, version );
-		GetAttr("Unknown String")->Read( in, version );
-		GetAttr("Unknown Link")->Read( in, version );
 		GetAttr("Integer Data")->Read( in, version );
 	}
 	void Write( ofstream& out, unsigned int version ) const {
 		AExtraData::Write( out, version );
-		GetAttr("Unknown String")->Write( out, version );
-		GetAttr("Unknown Link")->Write( out, version );
 		GetAttr("Integer Data")->Write( out, version );
 	}
 
@@ -2251,8 +2252,6 @@ public:
 		out << setprecision(1);
 
 		out << AExtraData::asString()
-			<< "Unknown String:  " << GetAttr("Unknown String")->asString() << endl
-			<< "Unknown Link:  " << GetAttr("Unknown Link")->asString() << endl
 			<< "Integer Data:  " << GetAttr("Integer Data")->asString() << endl;
 
 		return out.str();
@@ -2616,16 +2615,16 @@ public:
  */
 class NiLookAtInterpolator : public AInterpolator {
 public:
-	NiLookAtInterpolator() {
-		AddAttr( attr_short, "Unknown Short", 0, 0xFFFFFFFF );
+	NiLookAtInterpolator();
+	//	AddAttr( attr_short, "Unknown Short", 0, 0xFFFFFFFF );
 
-		AddAttr( attr_link, "Unknown Link", 0, 0xFFFFFFFF );
-		Init();
-	}
+	//	AddAttr( attr_link, "Unknown Link", 0, 0xFFFFFFFF );
+	//	Init();
+	//}
 
-	void Read( ifstream& in, unsigned int version );
-	void Write( ofstream& out, unsigned int version ) const;
-	string asString() const;
+	//void Read( ifstream& in, unsigned int version );
+	//void Write( ofstream& out, unsigned int version ) const;
+	//string asString() const;
 
 	void Init() {}
 	~NiLookAtInterpolator() {}
diff --git a/NIF_IO.cpp b/NIF_IO.cpp
index f16af8e8..5be46669 100644
--- a/NIF_IO.cpp
+++ b/NIF_IO.cpp
@@ -500,6 +500,28 @@ void NifStream( TexDesc const & val, ofstream& out, uint version ) {
 	}
 };
 
+string HexString( const byte * src, uint len ) {
+	stringstream out;
+	
+	//Display Data in Hex form
+	out << hex << setfill('0');
+
+	for ( uint i = 0; i < len; ++i ) {
+		out << uppercase << setw(2) << uint(src[i]);
+		if (i % 16 == 15 || i == len - 1)
+			out << endl;
+		else if (i % 16 == 7)
+			out << "   ";
+		else if (i % 8 == 3)
+			out << "  ";
+		else
+			out << " ";
+	}
+
+	return out.str();
+
+}
+
 //string indent( int level ) {
 //	string tmp;
 //	tmp.resize( level * 3 );
diff --git a/NIF_IO.h b/NIF_IO.h
index c83df098..7218bd82 100644
--- a/NIF_IO.h
+++ b/NIF_IO.h
@@ -40,6 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. */
 #include <fstream>
 #include <iomanip>
 #include <string>
+#include <sstream>
 #include "niflib.h"
 using namespace std;
 
@@ -321,6 +322,8 @@ void NifStream( vector<T> const & val, ofstream& file ) {
 //void NifString( Color4 const & val, stringstream& out, string heading, int ind_lvl = 0 );
 //void NifString( Triangle const & val, stringstream& out, string heading, int ind_lvl = 0 );
 
+string HexString( const byte * src, uint len );
+
 class NIF;
 
 class nifIndex {
diff --git a/docsys_extract.cpp b/docsys_extract.cpp
index 39f080c9..24e0842d 100644
--- a/docsys_extract.cpp
+++ b/docsys_extract.cpp
@@ -162,9 +162,7 @@ APSysModifier::APSysModifier() {
 AShape::AShape() {
 	AddAttr( attr_link, "Data", 0, 0xFFFFFFFF );
 	AddAttr( attr_link, "Skin Instance", 0, 0xFFFFFFFF );
-	AddAttr( attr_bool, "Has Shader", 167772416, 0xFFFFFFFF );
-	AddAttr( attr_string, "Shader Name", 167772416, 0xFFFFFFFF );
-	AddAttr( attr_link, "Shader Link", 167772416, 0xFFFFFFFF );
+	AddAttr( attr_shader, "Shader", 167772416, 0xFFFFFFFF );
 	Init();
 }
 
@@ -379,6 +377,24 @@ NiLookAtController::NiLookAtController() {
 	Init();
 }
 
+NiLookAtInterpolator::NiLookAtInterpolator() {
+	AddAttr( attr_short, "Unknown Short", 0, 0xFFFFFFFF );
+	AddAttr( attr_link, "Unknown Link", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[0]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[1]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[2]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[3]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[4]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[5]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[6]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[7]", 0, 0xFFFFFFFF );
+	AddAttr( attr_float, "Unknown Floats[8]", 0, 0xFFFFFFFF );
+	AddAttr( attr_link, "Unknown Link 1", 0, 0xFFFFFFFF );
+	AddAttr( attr_link, "Unknown Link 2", 0, 0xFFFFFFFF );
+	AddAttr( attr_link, "Unknown Link 3", 0, 0xFFFFFFFF );
+	Init();
+}
+
 NiMaterialColorController::NiMaterialColorController() {
 	AddAttr( attr_short, "Unknown", 167837696, 167837696 );
 	AddAttr( attr_link, "Data", 0, 0xFFFFFFFF );
@@ -752,12 +768,6 @@ NiStencilProperty::NiStencilProperty() {
 	Init();
 }
 
-//NiStringPalette::NiStringPalette() {
-//	AddAttr( attr_string, "Palette", 0, 0xFFFFFFFF );
-//	AddAttr( attr_int, "Length", 0, 0xFFFFFFFF );
-//	Init();
-//}
-
 NiTextureEffect::NiTextureEffect() {
 	AddAttr( attr_condint, "Conditional Int", 0, 67108866 );
 	AddAttr( attr_byte, "Unknown Byte 2", 335544324, 0xFFFFFFFF );
@@ -798,22 +808,6 @@ NiTextureTransformController::NiTextureTransformController() {
 	Init();
 }
 
-//NiTexturingProperty::NiTexturingProperty() {
-//	AddAttr( attr_flags, "Flags", 0, 167772416 );
-//	AddAttr( attr_applymode, "Apply Mode", 0, 0xFFFFFFFF );
-//	AddAttr( attr_int, "Texture Count", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Base Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Dark Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Detail Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Gloss Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Glow Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_bumpmap, "Bump Map Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Decal 0 Texture", 0, 0xFFFFFFFF );
-//	AddAttr( attr_texture, "Unknown Texture", 335544324, 0xFFFFFFFF );
-//	AddAttr( attr_extratexturegroup, "Extra Textures", 167772416, 0xFFFFFFFF );
-//	Init();
-//}
-
 NiTransformController::NiTransformController() {
 	AddAttr( attr_link, "Interpolator", 0, 0xFFFFFFFF );
 	Init();
diff --git a/nif_attrs.h b/nif_attrs.h
index 8094e622..5323f010 100644
--- a/nif_attrs.h
+++ b/nif_attrs.h
@@ -888,97 +888,29 @@ public:
     //                 0 - lighting emmisive
     //  1 - lighting emmisive amb diff
 
-class TexDescAttr : public LinkAttr {
+class ShaderAttr : public LinkAttr {
 public:
-	TexDescAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : LinkAttr(name, owner, first_ver, last_ver) {
-		data.clampMode = CLAMP_S_CLAMP_T;
-		data.filterMode = FILTER_NEAREST;
-		data.hasUnknownData = false;
-		data.isUsed = false;
-		data.PS2_K = 0;
-		data.PS2_L = 0;
-		data.textureSet = 0;
-		data.unknown5Floats[0] = 0.0f;
-		data.unknown5Floats[1] = 0.0f;
-		data.unknown5Floats[2] = 0.0f;
-		data.unknown5Floats[3] = 0.0f;
-		data.unknown5Floats[4] = 0.0f;
-		data.unknownFloat1 = 0.0f;
-		data.unknownFloat2 = 0.0f;
-		data.unknownInt = 0;
-		data.unknownShort = 0;
-		//memset( &data, 0, sizeof(data) );
-	}
-	~TexDescAttr() {}
-	AttrType GetType() const { return attr_texture; }
+	ShaderAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : LinkAttr(name, owner, first_ver, last_ver), isUsed(false) {}
+	~ShaderAttr() {}
+	AttrType GetType() const { return attr_shader; }
 	void ReadAttr( ifstream& in, unsigned int version ) {
-		data.isUsed = ReadBool( in, version );
-		if ( data.isUsed ) {	
-			//Read in link for TexSource
-			LinkAttr::ReadAttr( in, version );
-
-			data.clampMode = TexClampMode( ReadUInt( in ) );
-			data.filterMode = TexFilterMode( ReadUInt( in ) );
-			data.textureSet = ReadUInt( in );
+		isUsed = ReadBool( in, version );
+		if ( isUsed ) {	
+			//Read in shader name
+			_shader_name = ReadString( in );
 
-			//PS2 values exist up to version 10.2.0.0
-			if ( version <= VER_10_2_0_0 ) {
-				data.PS2_L = ReadUShort( in );
-				data.PS2_K = ReadUShort( in );
-			}
-
-			//unknownShort exists up to version 4.1.0.12
-			if ( version <= VER_4_1_0_12) {
-				data.unknownShort = ReadUShort( in );
-			}
-
-			//From version 10.1.0.0 and up, this unknown data block may exist
-			if ( version >= VER_10_1_0_0 ) {
-				data.hasUnknownData = ReadBool( in, version );
-				if ( data.hasUnknownData == true ) {
-					for (int i = 0; i < 5; ++i ) {
-						data.unknown5Floats[i] = ReadFloat( in );;
-					}
-					data.unknownInt = ReadUInt( in );
-					data.unknownFloat1 = ReadFloat( in );
-					data.unknownFloat2 = ReadFloat( in );
-				}
-			}
+			//Read in unknown link
+			LinkAttr::ReadAttr( in, version );
 		}
 	}
 	void WriteAttr( ofstream& out, unsigned int version ) const {
-		WriteBool( data.isUsed, out, version );
-		if ( data.isUsed ) {
-			//Write link
-			LinkAttr::WriteAttr( out, version );
-
-			WriteUInt( data.clampMode, out );
-			WriteUInt( data.filterMode, out );
-			WriteUInt( data.textureSet, out );
+		WriteBool( isUsed, out, version );
+		if ( isUsed ) {	
+			//Write out shader name
+			WriteString( _shader_name, out );
 
-			//PS2 values exist up to version 10.2.0.0
-			if ( version <= VER_10_2_0_0 ) {
-				WriteUShort( data.PS2_L, out );
-				WriteUShort( data.PS2_K, out );
-			}
-
-			//unknownShort exists up to version 4.1.0.12
-			if ( version <= VER_4_1_0_12 ) {
-				WriteUShort( data.unknownShort, out );
-			}
-
-			//From version 10.1.0.0 and up, this unknown data block may exist
-			if ( version >= VER_10_1_0_0 ) {
-				WriteBool( data.hasUnknownData, out, version );
-				if ( data.hasUnknownData == true ) {
-					for (int i = 0; i < 5; ++i ) {
-						WriteFloat( data.unknown5Floats[i], out );;
-					}
-					WriteUInt( data.unknownInt, out );
-					WriteFloat( data.unknownFloat1, out );
-					WriteFloat( data.unknownFloat2, out );
-				}
-			}
+			//Write out unknown link
+			LinkAttr::WriteAttr( out, version );
 		}
 	}
 	string asString() const {
@@ -986,192 +918,24 @@ public:
 		out.setf(ios::fixed, ios::floatfield);
 		out << setprecision(1);
 
-		if ( data.isUsed ) {
-			out << endl
-				<< "   Source:  " << asLink() << endl
-				<< "   Clamp Mode:  ";
-			switch ( data.clampMode ) {
-				case CLAMP_S_CLAMP_T:
-					out << "Clamp S Clamp T";
-					break;
-				case CLAMP_S_WRAP_T:
-					out << "Clamp S Wrap T";
-					break;
-				case WRAP_S_CLAMP_T:
-					out << "Wrap S Clamp T";
-					break;
-				case WRAP_S_WRAP_T:
-					out << "Wrap S Wrap T";
-					break;
-				default:
-					out << "!Invalid Value! - " << data.clampMode;
-				break;
-			}
-			out << endl
-				<< "   Filter Mode:  ";
-			switch ( data.filterMode ) {
-				case FILTER_NEAREST:
-					out << "Nearest";
-					break;
-				case FILTER_BILERP:
-					out << "Biliner";
-					break;
-				case FILTER_TRILERP:
-					out << "Trilinear";
-					break;
-				case FILTER_NEAREST_MIPNEAREST:
-					out << "Nearest, Mip Nearest";
-					break;
-				case FILTER_NEAREST_MIPLERP:
-					out << "Nearest, Mip Linear";
-					break;
-				case FILTER_BILERP_MIPNEAREST:
-					out << "Bilinear, Mip Nearest";
-					break;
-				default:
-					out << "!Invalid Value! - " << data.clampMode;
-				break;
-			}
-			
+		out << "Shader:  ";
+
+		if ( isUsed ) {
 			out << endl
-				<< "   Texture Set:  " << data.textureSet << endl
-				<< "   PS2 L Setting:  " << data.PS2_L << endl
-				<< "   PS2 K Setting:  " << data.PS2_K << endl
-				<< "   Unknown Short:  " << data.unknownShort
-				<< "   Unknown Data:   ";
-
-
-			//From version 10.1.0.0 and up, this unknown data block may exist
-			if ( data.hasUnknownData == true ) {
-				out << endl
-					<< "      Unknown 5 Floats:" << endl;
-				for (int i = 0; i < 5; ++i ) {
-					out << "         " << i + 1 << ":  " << data.unknown5Floats[i] << endl;
-				}
-				out << "      Unknown Int:  " << data.unknownInt << endl
-					<< "      Unknown Float 1:  " << data.unknownFloat1 << endl
-					<< "      Unknown Float 2:  " << data.unknownFloat2 << endl;
-			} else {
-				out << "None" << endl;
-			}
+				<< "   Shader Name:  " << _shader_name << endl
+				<< "   Unknown Link:  " << LinkAttr::asLink() << endl;
 		} else {
-			out << "None";
-		}
-
-		return out.str();
-	}
-	TexDesc asTexDesc() const { return data; }
-	void Set( TexDesc const & n ) { data = n; }
-protected:
-	TexDesc data;
-};
-
-class BumpMapAttr : public TexDescAttr {
-public:
-	BumpMapAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : TexDescAttr(name, owner, first_ver, last_ver) {}
-	~BumpMapAttr() {}
-	AttrType GetType() const { return attr_bumpmap; }
-	void ReadAttr( ifstream& in, unsigned int version ) {
-		TexDescAttr::ReadAttr( in, version );
-		if ( data.isUsed ) {
-			//data.bmLumaScale = ReadFloat( in );
-			//data.bmLumaOffset = ReadFloat( in );
-			//data.bmMatrix[0][0] = ReadFloat( in );
-			//data.bmMatrix[1][0] = ReadFloat( in );
-			//data.bmMatrix[0][1] = ReadFloat( in );
-			//data.bmMatrix[1][1] = ReadFloat( in );
-		}
-	}
-	void WriteAttr( ofstream& out, unsigned int version ) const {
-		TexDescAttr::WriteAttr( out, version );
-
-		if ( data.isUsed ) {
-			//WriteFloat( data.bmLumaScale, out );
-			//WriteFloat( data.bmLumaOffset, out );
-			//WriteFloat( data.bmMatrix[0][0], out );
-			//WriteFloat( data.bmMatrix[1][0], out );
-			//WriteFloat( data.bmMatrix[0][1], out );
-			//WriteFloat( data.bmMatrix[1][1], out );
-		}
-	}
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		out << TexDescAttr::asString();
-
-		if ( data.isUsed ) {
-			//out << endl
-			//	<< "   BumpMap Info:" << endl
-			//	<< "      Luma Offset:  " << data.bmLumaOffset << endl
-			//	<< "      Luma Scale:  " << data.bmLumaScale << endl
-			//	<< "      Matrix:" << endl
-			//	<< "         |" << setw(6) << data.bmMatrix[0][0] << "," << setw(6) << data.bmMatrix[0][1] << " |" << endl
-			//	<< "         |" << setw(6) << data.bmMatrix[1][0] << "," << setw(6) << data.bmMatrix[1][1] << " |" << endl;
+			out << "None" << endl;
 		}
 
 		return out.str();
 	}
-};
 
-    //int isPresent
-    //if(isPresent != 0)
-    //    int source          - index of NiSourceTexture record
-    //    int clamp           - clamp mode:
-    //                          0 - clamp clamp
-    //                          1 - clamp wrap
-    //                          2 - wrap clamp
-    //                          3 - wrap wrap
-    //    int set(?)
-    //    int unknown
-
-    //    short ps2_L         = 0
-    //    short ps2_K         = -2 or -75
-    //    short unknown2      = 0 or 0x0101 (=257)
-
-class ApplyModeAttr : public IntAttr {
-public:
-	ApplyModeAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : IntAttr( name, owner, first_ver, last_ver ) {}
-	~ApplyModeAttr() {}
-	AttrType GetType() const { return attr_applymode; }
-	string asString() const {
-		stringstream out;
-		out.setf(ios::fixed, ios::floatfield);
-		out << setprecision(1);
-
-		switch ( data ) {
-			case 0:
-				out << "Replace";
-				break;
-			case 1:
-				out << "Decal";
-				break;
-			case 2:
-				out << "Modulate";
-				break;
-			case 3:
-				out << "Hilight";
-				break;
-			case 4:
-				out << "Hilight2";
-				break;
-			default:
-				out << "!Invalid Value! - " << data;
-				break;
-		}
-
-		return out.str();
-	}	
+protected:
+	bool isUsed;
+	string _shader_name;
 };
 
-    //int apply         - apply mode:
-    //                    0 - replace
-    //                    1 - decal
-    //                    2 - modulate
-    //                    3 - hilight
-    //                    4 - hilight2
-
 class TexSourceAttr : public LinkAttr {
 public:
 	TexSourceAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : LinkAttr(name, owner, first_ver, last_ver) {
diff --git a/niflib.cpp b/niflib.cpp
index a5367bc0..0fa29426 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -234,7 +234,7 @@ vector<blk_ref> ReadNifList( string const & file_name ) {
 			}
 		}
 
-		cout << endl << i << ":  " << blockName;
+		//cout << endl << i << ":  " << blockName;
 
 		//Create Block of the type that was found
 		blocks[i] = CreateBlock(blockName);
@@ -257,7 +257,7 @@ vector<blk_ref> ReadNifList( string const & file_name ) {
 		bk_intl->SetBlockNum(i);
 		bk_intl->Read( in, version );
 
-		cout << endl << blocks[i]->asString() << endl;
+		//cout << endl << blocks[i]->asString() << endl;
 	}
 
 	//cout << endl;
diff --git a/niflib.h b/niflib.h
index 0c58ff49..48333363 100644
--- a/niflib.h
+++ b/niflib.h
@@ -131,7 +131,8 @@ enum AttrType {
 	attr_color3, /*!< Color3 Attribute.  Holds a Float3 structure that corresponds to an RGB color. */ 
 	attr_parent, /*!< Parent Attribute.  Automatic. */
 	attr_targetgroup, /*!< A linkgroup that stores its count with a short instead of a long integer. */
-	attr_unk292bytes //Temporary
+	attr_unk292bytes, //Temporary
+	attr_shader /*!< A string and a link that describe the shader used on a particular mesh.  */
 };
 
 //NIF Versions
-- 
GitLab