diff --git a/NIF_IO.cpp b/NIF_IO.cpp
index f74e1438d97e4cab981f05b51a9fb70f9d3bbc5f..a02bf437b0cb864efc4338d7dc2e3e48763ac863 100644
--- a/NIF_IO.cpp
+++ b/NIF_IO.cpp
@@ -236,6 +236,18 @@ void NifStream( HeaderString & val, istream& in, uint version ) {
 	char tmp[64];
 	in.getline( tmp, 64 );
 	val.header = tmp;
+
+        // make sure this is a NIF file
+        if ( ( val.header.substr(0, 22) != "NetImmerse File Format" )
+        && ( val.header.substr(0, 20) != "Gamebryo File Format" ) )
+                throw runtime_error("Not a NIF file.");
+
+        // detect old versions
+        if ( ( val.header == "NetImmerse File Format, Version 3.1" )
+        || ( val.header == "NetImmerse File Format, Version 3.03" )
+        || ( val.header == "NetImmerse File Format, Version 3.0" )
+        || ( val.header == "NetImmerse File Format, Version 2.3" ) )
+                throw runtime_error("Unsupported: " + val.header);
 };
 
 void NifStream( HeaderString const & val, ostream& out, uint version ) {
@@ -251,7 +263,7 @@ void NifStream( HeaderString const & val, ostream& out, uint version ) {
 
 	header_string << int_ver[0] << "." << int_ver[1] << "." << int_ver[2] << "." << int_ver[3];
 
-	out << header_string.str();
+	out << header_string.str() << "\n";
 };
 
 ostream & operator<<( ostream & out, HeaderString const & val ) {
diff --git a/NIF_IO.h b/NIF_IO.h
index df54f33d314f2f730f1948c1a5e8216094c15af0..263c63d2dd96023c912c2b8c76a389eb04fc860a 100644
--- a/NIF_IO.h
+++ b/NIF_IO.h
@@ -367,6 +367,11 @@ void NifStream( LightMode & val, istream& in, uint version = 0 );
 void NifStream( LightMode const & val, ostream& out, uint version = 0  );
 ostream & operator<<( ostream & out, LightMode const & val );
 
+//HeaderString
+void NifStream( HeaderString & val, istream& in, uint version = 0 );
+void NifStream( HeaderString const & val, ostream& out, uint version = 0  );
+ostream & operator<<( ostream & out, HeaderString const & val );
+
 //--Templates--//
 
 //Key<T>
diff --git a/gen/Footer.cpp b/gen/Footer.cpp
index 1324f6285ff74b38c370ff9da53510f72adf4d3e..7b013df35dfb45a7625db73daea13935a07310c3 100644
--- a/gen/Footer.cpp
+++ b/gen/Footer.cpp
@@ -9,3 +9,33 @@ Footer::Footer() : numRoots((uint)0) {};
 
 //Destructor
 Footer::~Footer() {};
+
+void Footer::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
+	uint block_num;
+	NifStream( numRoots, in, version );
+	roots.resize(numRoots);
+	for (uint i1 = 0; i1 < roots.size(); i1++) {
+		NifStream( block_num, in, version );
+		link_stack.push_back( block_num );
+	};
+}
+
+void Footer::Write( ostream& out, map<NiObjectRef,uint> link_map, unsigned int version, unsigned int user_version ) const {
+	NifStream( numRoots, out, version );
+	for (uint i1 = 0; i1 < roots.size(); i1++) {
+		NifStream( link_map[StaticCast<NiObject>(roots[i1])], out, version );
+	};
+}
+
+string Footer::asString( bool verbose ) const {
+	stringstream out;
+	out << "  Num Roots:  " << numRoots << endl;
+	for (uint i1 = 0; i1 < roots.size(); i1++) {
+		if ( !verbose && ( i1 > MAXARRAYDUMP ) ) {
+			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+			break;
+		};
+		out << "    Roots[" << i1 << "]:  " << roots[i1] << endl;
+	};
+	return out.str();
+}
diff --git a/gen/Footer.h b/gen/Footer.h
index 0a20591d52df47a2ffb3f8cab9bae8741a9bb162..bfc14dacc5fccb368e43287b1d96d70d023b618f 100644
--- a/gen/Footer.h
+++ b/gen/Footer.h
@@ -9,6 +9,7 @@ All rights reserved.  Please see niflib.h for licence. */
 // Forward define of referenced blocks
 #include "Ref.h"
 class NiAVObject;
+#include "obj/NiObject.h"
 
 /*!
  * The NIF file footer.
@@ -29,6 +30,9 @@ struct Footer {
 	 * node).
 	 */
 	vector<Ref<NiAVObject > > roots;
+	void Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version );
+	void Write( ostream& out, map<NiObjectRef,uint> link_map, unsigned int version, unsigned int user_version ) const;
+	string asString( bool verbose = false ) const;
 };
 
 #endif
diff --git a/gen/Header.cpp b/gen/Header.cpp
index cdc6e0b3c0d15f9bf16dcaaec1fb7a9c179caca2..ba59c6733a2d83cfe55e7fd5fb85d203418e64cd 100644
--- a/gen/Header.cpp
+++ b/gen/Header.cpp
@@ -11,3 +11,154 @@ Header::Header() : version((uint)0x04000002), endianType((byte)1), userVersion((
 
 //Destructor
 Header::~Header() {};
+
+void Header::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
+	NifStream( headerString, in, version );
+	NifStream( version, in, version );
+	if ( version >= 0x14000004 ) {
+		NifStream( endianType, in, version );
+	};
+	if ( version >= 0x0A010000 ) {
+		NifStream( userVersion, in, version );
+	};
+	NifStream( numBlocks, in, version );
+	if ( ( version >= 0x0A000102 ) && ( version <= 0x0A000102 ) ) {
+		NifStream( unknownInt1, in, version );
+	};
+	if ( version >= 0x0A010000 ) {
+		if ( (userVersion != 0) ) {
+			NifStream( unknownInt3, in, version );
+		};
+	};
+	if ( version >= 0x0A000102 ) {
+		if ( (userVersion != 0) ) {
+			NifStream( creator_.length, in, version );
+			creator_.value.resize(creator_.length);
+			for (uint i3 = 0; i3 < creator_.value.size(); i3++) {
+				NifStream( creator_.value[i3], in, version );
+			};
+			NifStream( exportType_.length, in, version );
+			exportType_.value.resize(exportType_.length);
+			for (uint i3 = 0; i3 < exportType_.value.size(); i3++) {
+				NifStream( exportType_.value[i3], in, version );
+			};
+			NifStream( exportScript_.length, in, version );
+			exportScript_.value.resize(exportScript_.length);
+			for (uint i3 = 0; i3 < exportScript_.value.size(); i3++) {
+				NifStream( exportScript_.value[i3], in, version );
+			};
+		};
+	};
+	if ( version >= 0x0A000100 ) {
+		NifStream( numBlockTypes, in, version );
+		blockTypes.resize(numBlockTypes);
+		for (uint i2 = 0; i2 < blockTypes.size(); i2++) {
+			NifStream( blockTypes[i2], in, version );
+		};
+		blockTypeIndex.resize(numBlocks);
+		for (uint i2 = 0; i2 < blockTypeIndex.size(); i2++) {
+			NifStream( blockTypeIndex[i2], in, version );
+		};
+		NifStream( unknownInt2, in, version );
+	};
+}
+
+void Header::Write( ostream& out, map<NiObjectRef,uint> link_map, unsigned int version, unsigned int user_version ) const {
+	NifStream( headerString, out, version );
+	NifStream( version, out, version );
+	if ( version >= 0x14000004 ) {
+		NifStream( endianType, out, version );
+	};
+	if ( version >= 0x0A010000 ) {
+		NifStream( userVersion, out, version );
+	};
+	NifStream( numBlocks, out, version );
+	if ( ( version >= 0x0A000102 ) && ( version <= 0x0A000102 ) ) {
+		NifStream( unknownInt1, out, version );
+	};
+	if ( version >= 0x0A010000 ) {
+		if ( (userVersion != 0) ) {
+			NifStream( unknownInt3, out, version );
+		};
+	};
+	if ( version >= 0x0A000102 ) {
+		if ( (userVersion != 0) ) {
+			NifStream( creator_.length, out, version );
+			for (uint i3 = 0; i3 < creator_.value.size(); i3++) {
+				NifStream( creator_.value[i3], out, version );
+			};
+			NifStream( exportType_.length, out, version );
+			for (uint i3 = 0; i3 < exportType_.value.size(); i3++) {
+				NifStream( exportType_.value[i3], out, version );
+			};
+			NifStream( exportScript_.length, out, version );
+			for (uint i3 = 0; i3 < exportScript_.value.size(); i3++) {
+				NifStream( exportScript_.value[i3], out, version );
+			};
+		};
+	};
+	if ( version >= 0x0A000100 ) {
+		NifStream( numBlockTypes, out, version );
+		for (uint i2 = 0; i2 < blockTypes.size(); i2++) {
+			NifStream( blockTypes[i2], out, version );
+		};
+		for (uint i2 = 0; i2 < blockTypeIndex.size(); i2++) {
+			NifStream( blockTypeIndex[i2], out, version );
+		};
+		NifStream( unknownInt2, out, version );
+	};
+}
+
+string Header::asString( bool verbose ) const {
+	stringstream out;
+	out << "  Header String:  " << headerString << endl;
+	out << "  Version:  " << version << endl;
+	out << "  Endian Type:  " << endianType << endl;
+	out << "  User Version:  " << userVersion << endl;
+	out << "  Num Blocks:  " << numBlocks << endl;
+	out << "  Unknown Int 1:  " << unknownInt1 << endl;
+	if ( (userVersion != 0) ) {
+		out << "    Unknown Int 3:  " << unknownInt3 << endl;
+		out << "    Length:  " << creator_.length << endl;
+		for (uint i2 = 0; i2 < creator_.value.size(); i2++) {
+			if ( !verbose && ( i2 > MAXARRAYDUMP ) ) {
+				out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+				break;
+			};
+			out << "      Value[" << i2 << "]:  " << creator_.value[i2] << endl;
+		};
+		out << "    Length:  " << exportType_.length << endl;
+		for (uint i2 = 0; i2 < exportType_.value.size(); i2++) {
+			if ( !verbose && ( i2 > MAXARRAYDUMP ) ) {
+				out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+				break;
+			};
+			out << "      Value[" << i2 << "]:  " << exportType_.value[i2] << endl;
+		};
+		out << "    Length:  " << exportScript_.length << endl;
+		for (uint i2 = 0; i2 < exportScript_.value.size(); i2++) {
+			if ( !verbose && ( i2 > MAXARRAYDUMP ) ) {
+				out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+				break;
+			};
+			out << "      Value[" << i2 << "]:  " << exportScript_.value[i2] << endl;
+		};
+	};
+	out << "  Num Block Types:  " << numBlockTypes << endl;
+	for (uint i1 = 0; i1 < blockTypes.size(); i1++) {
+		if ( !verbose && ( i1 > MAXARRAYDUMP ) ) {
+			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+			break;
+		};
+		out << "    Block Types[" << i1 << "]:  " << blockTypes[i1] << endl;
+	};
+	for (uint i1 = 0; i1 < blockTypeIndex.size(); i1++) {
+		if ( !verbose && ( i1 > MAXARRAYDUMP ) ) {
+			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl;
+			break;
+		};
+		out << "    Block Type Index[" << i1 << "]:  " << blockTypeIndex[i1] << endl;
+	};
+	out << "  Unknown Int 2:  " << unknownInt2 << endl;
+	return out.str();
+}
diff --git a/gen/Header.h b/gen/Header.h
index 2e6d2442f77797177226776237f8226e6ec41ead..35235f5e9869cfe43b12ef9f20e5cedcd9380a0b 100644
--- a/gen/Header.h
+++ b/gen/Header.h
@@ -7,6 +7,7 @@ All rights reserved.  Please see niflib.h for licence. */
 #include "NIF_IO.h"
 // Include structures
 #include "gen/ShortString.h"
+#include "obj/NiObject.h"
 
 /*!
  * The NIF file header.
@@ -81,6 +82,9 @@ struct Header {
 	 * Unknown.
 	 */
 	uint unknownInt2;
+	void Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version );
+	void Write( ostream& out, map<NiObjectRef,uint> link_map, unsigned int version, unsigned int user_version ) const;
+	string asString( bool verbose = false ) const;
 };
 
 #endif
diff --git a/gen/obj_defines.h b/gen/obj_defines.h
index 8fb40a0c9998277869834d5e88a566a8449d0dd3..4e327a0e41816cc7cffbf1e3b1715f9fef28d6c5 100644
--- a/gen/obj_defines.h
+++ b/gen/obj_defines.h
@@ -14056,174 +14056,4 @@ list<Ref<NiObject> > refs; \
 refs = NiNode::GetRefs(); \
 return refs; \
 
-#define HEADER_READ \
-NifStream( headerString, in, version ); \
-NifStream( version, in, version ); \
-if ( version >= 0x14000004 ) { \
-	NifStream( endianType, in, version ); \
-}; \
-if ( version >= 0x0A010000 ) { \
-	NifStream( userVersion, in, version ); \
-}; \
-NifStream( numBlocks, in, version ); \
-if ( ( version >= 0x0A000102 ) && ( version <= 0x0A000102 ) ) { \
-	NifStream( unknownInt1, in, version ); \
-}; \
-if ( version >= 0x0A010000 ) { \
-	if ( (userVersion != 0) ) { \
-		NifStream( unknownInt3, in, version ); \
-	}; \
-}; \
-if ( version >= 0x0A000102 ) { \
-	if ( (userVersion != 0) ) { \
-		NifStream( creator_.length, in, version ); \
-		creator_.value.resize(creator_.length); \
-		for (uint i2 = 0; i2 < creator_.value.size(); i2++) { \
-			NifStream( creator_.value[i2], in, version ); \
-		}; \
-		NifStream( exportType_.length, in, version ); \
-		exportType_.value.resize(exportType_.length); \
-		for (uint i2 = 0; i2 < exportType_.value.size(); i2++) { \
-			NifStream( exportType_.value[i2], in, version ); \
-		}; \
-		NifStream( exportScript_.length, in, version ); \
-		exportScript_.value.resize(exportScript_.length); \
-		for (uint i2 = 0; i2 < exportScript_.value.size(); i2++) { \
-			NifStream( exportScript_.value[i2], in, version ); \
-		}; \
-	}; \
-}; \
-if ( version >= 0x0A000100 ) { \
-	NifStream( numBlockTypes, in, version ); \
-	blockTypes.resize(numBlockTypes); \
-	for (uint i1 = 0; i1 < blockTypes.size(); i1++) { \
-		NifStream( blockTypes[i1], in, version ); \
-	}; \
-	blockTypeIndex.resize(numBlocks); \
-	for (uint i1 = 0; i1 < blockTypeIndex.size(); i1++) { \
-		NifStream( blockTypeIndex[i1], in, version ); \
-	}; \
-	NifStream( unknownInt2, in, version ); \
-}; \
-
-#define HEADER_WRITE \
-NifStream( headerString, out, version ); \
-NifStream( version, out, version ); \
-if ( version >= 0x14000004 ) { \
-	NifStream( endianType, out, version ); \
-}; \
-if ( version >= 0x0A010000 ) { \
-	NifStream( userVersion, out, version ); \
-}; \
-NifStream( numBlocks, out, version ); \
-if ( ( version >= 0x0A000102 ) && ( version <= 0x0A000102 ) ) { \
-	NifStream( unknownInt1, out, version ); \
-}; \
-if ( version >= 0x0A010000 ) { \
-	if ( (userVersion != 0) ) { \
-		NifStream( unknownInt3, out, version ); \
-	}; \
-}; \
-if ( version >= 0x0A000102 ) { \
-	if ( (userVersion != 0) ) { \
-		NifStream( creator_.length, out, version ); \
-		for (uint i2 = 0; i2 < creator_.value.size(); i2++) { \
-			NifStream( creator_.value[i2], out, version ); \
-		}; \
-		NifStream( exportType_.length, out, version ); \
-		for (uint i2 = 0; i2 < exportType_.value.size(); i2++) { \
-			NifStream( exportType_.value[i2], out, version ); \
-		}; \
-		NifStream( exportScript_.length, out, version ); \
-		for (uint i2 = 0; i2 < exportScript_.value.size(); i2++) { \
-			NifStream( exportScript_.value[i2], out, version ); \
-		}; \
-	}; \
-}; \
-if ( version >= 0x0A000100 ) { \
-	NifStream( numBlockTypes, out, version ); \
-	for (uint i1 = 0; i1 < blockTypes.size(); i1++) { \
-		NifStream( blockTypes[i1], out, version ); \
-	}; \
-	for (uint i1 = 0; i1 < blockTypeIndex.size(); i1++) { \
-		NifStream( blockTypeIndex[i1], out, version ); \
-	}; \
-	NifStream( unknownInt2, out, version ); \
-}; \
-
-#define HEADER_STRING \
-out << "Header String:  " << headerString << endl; \
-out << "Version:  " << version << endl; \
-out << "Endian Type:  " << endianType << endl; \
-out << "User Version:  " << userVersion << endl; \
-out << "Num Blocks:  " << numBlocks << endl; \
-out << "Unknown Int 1:  " << unknownInt1 << endl; \
-if ( (userVersion != 0) ) { \
-	out << "  Unknown Int 3:  " << unknownInt3 << endl; \
-	out << "  Length:  " << creator_.length << endl; \
-	for (uint i1 = 0; i1 < creator_.value.size(); i1++) { \
-		if ( !verbose && ( i1 > MAXARRAYDUMP ) ) { \
-			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; \
-			break; \
-		}; \
-		out << "    Value[" << i1 << "]:  " << creator_.value[i1] << endl; \
-	}; \
-	out << "  Length:  " << exportType_.length << endl; \
-	for (uint i1 = 0; i1 < exportType_.value.size(); i1++) { \
-		if ( !verbose && ( i1 > MAXARRAYDUMP ) ) { \
-			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; \
-			break; \
-		}; \
-		out << "    Value[" << i1 << "]:  " << exportType_.value[i1] << endl; \
-	}; \
-	out << "  Length:  " << exportScript_.length << endl; \
-	for (uint i1 = 0; i1 < exportScript_.value.size(); i1++) { \
-		if ( !verbose && ( i1 > MAXARRAYDUMP ) ) { \
-			out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; \
-			break; \
-		}; \
-		out << "    Value[" << i1 << "]:  " << exportScript_.value[i1] << endl; \
-	}; \
-}; \
-out << "Num Block Types:  " << numBlockTypes << endl; \
-for (uint i0 = 0; i0 < blockTypes.size(); i0++) { \
-	if ( !verbose && ( i0 > MAXARRAYDUMP ) ) { \
-		out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; \
-		break; \
-	}; \
-	out << "  Block Types[" << i0 << "]:  " << blockTypes[i0] << endl; \
-}; \
-for (uint i0 = 0; i0 < blockTypeIndex.size(); i0++) { \
-	if ( !verbose && ( i0 > MAXARRAYDUMP ) ) { \
-		out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; \
-		break; \
-	}; \
-	out << "  Block Type Index[" << i0 << "]:  " << blockTypeIndex[i0] << endl; \
-}; \
-out << "Unknown Int 2:  " << unknownInt2 << endl; \
-
-#define FOOTER_READ \
-NifStream( numRoots, in, version ); \
-roots.resize(numRoots); \
-for (uint i0 = 0; i0 < roots.size(); i0++) { \
-	NifStream( block_num, in, version ); \
-	link_stack.push_back( block_num ); \
-}; \
-
-#define FOOTER_WRITE \
-NifStream( numRoots, out, version ); \
-for (uint i0 = 0; i0 < roots.size(); i0++) { \
-	NifStream( link_map[StaticCast<NiObject>(roots[i0])], out, version ); \
-}; \
-
-#define FOOTER_STRING \
-out << "Num Roots:  " << numRoots << endl; \
-for (uint i0 = 0; i0 < roots.size(); i0++) { \
-	if ( !verbose && ( i0 > MAXARRAYDUMP ) ) { \
-		out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; \
-		break; \
-	}; \
-	out << "  Roots[" << i0 << "]:  " << roots[i0] << endl; \
-}; \
-
 #endif