diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index cdc748b693785f6b571da56cb8d80a71034c5936..7bcfc267ee9d79054f7b5e70c56811c77ad54a9d 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -27,6 +27,31 @@
 
     Change log
     ----------
+      0.2.6
+      -----
+    o All
+      - Consolidate code to single plugin: NifPlugins.dlu
+      - Please remove the following when upgrading to this release.
+        o MaxNifImport.dli, NifExport.dle, NifProps.dlu, NifFurniture.dlu
+      - Started Wiki Documentation Project:
+        o http://www.niftools.org/wiki/index.php/3ds_Max
+      
+    o Exporter
+      - Fix bug in Skin Partitioning that would cause max to hang
+      - Add links to website and wiki to dialog
+      - Tag NIF files with Plugin version in first comment field.
+      - Fix export of selected nodes.
+      - Support Prn strings for Oblivion and Morrowind when selected
+        mesh is bound to bone bug does not have skin modifier.
+      - Change export to place all data in NonAccum nodes when available
+        Plan to do proper support for NonAccum nodes in the future.
+      - Add progress bar updates while exporting
+      
+    o Importer
+      - Add links to website and wiki to dialog
+      - Add support for Prn string imports for Oblivion and Morrowind
+      
+      
       0.2.5
       -----
     o Exporter
@@ -179,10 +204,8 @@
     Installation
     ------------
     
-    Copy NifExport.dle, NifProps.dlu, MaxNifImport.dli to your 3dsmax\plugins 
-      directory.
-  
-      Copy MaxNifTools.ini to your 3dsmax\plugcfg directory.
+    Copy NifPlugins.dlu, to your 3dsmax\plugins directory.
+    Copy MaxNifTools.ini to your 3dsmax\plugcfg directory.
         
     Usage
     -----
diff --git a/MaxNifTools.ini b/MaxNifTools.ini
index 806e0bfad4b9f3b9afa479e96f7dbe14b1594599..4dcf39a7e11228d6ec13fddd6a19ddd46d043070 100644
--- a/MaxNifTools.ini
+++ b/MaxNifTools.ini
@@ -9,6 +9,10 @@ ShortDescription=Netimmerse/Gamebryo
 KnownApplications=Oblivion;Morrowind;Civilization 4;Dark Age of Camelot;User
 ; Reparse the Applications (and therefore Texture directory cache) on every import/export
 Reparse=0
+; Website - Primary website
+Website=http://www.niftools.org
+; Wiki - Documentation website
+Wiki=http://www.niftools.org/wiki/index.php/3ds_Max
 
 [MaxNifExport]
 ; Max SDK Plugin Version (0 - AutoSelect; 0x17700d00 - Max6, 0x1f401100 - Max8)  Default: 0 
@@ -42,10 +46,29 @@ ExportExtraNodes=0
 ExportSkin=1
 ; Export UserPropBuffer. Default: 0
 UserPropBuffer=0
-; Flatten Node Hierarchy. Default: 1
-FlattenHierarchy=1
+; Flatten Node Hierarchy. Default: 0
+FlattenHierarchy=0
 ; Remove Unreferenced Bones. Default: 1
 RemoveUnreferencedBones=1
+; Sort Nodes To End of Child Lists. Default: 0
+SortNodesToEnd=1
+SkeletonOnly=0
+Cameras=0
+GenerateBoneCollision=0
+ExportTransforms=1
+ExportType=0
+MultiplePartitions=0
+BonesPerVertex=4
+BonesPerPartition=18
+UseTimeTags=0
+AllowAccum=1
+Creator=
+; Collapse Transforms to final position. Default: 0
+CollapseTransforms=0
+; Add Tangent and Binormal Extra Data block. Default: 0
+TangentAndBinormalExtraData=0
+FixNormals=0
+UseAlternateStripper=0
 
 [MaxNifImport]
 ; Max SDK Plugin Version (0 - AutoSelect; 0x17700d00 - Max6, 0x1f401100 - Max8)  Default: 0 
@@ -70,8 +93,8 @@ RemoveDegenerateFaces=1
 RemoveIllegalFaces=1
 ; EnableSkinSupport attempt to skin the mesh if bones are available. Default:1
 EnableSkinSupport=1
-; Enable Havok Collision Support. Default:1
-EnableCollision=1
+; Enable Havok Collision Support. Default:0
+EnableCollision=0
 ; Vertex Color Support mode. (0-Disable; 1-Bake into mesh; 2-Use VertexPaint Modifier) Default:1
 VertexColorMode=1
 ; Merge NonAccum transforms into base node. Default: 1
@@ -80,6 +103,10 @@ MergeNonAccum=1
 Lights=0
 ; Import Cameras. Default: 0
 Cameras=0
+; Use Civilization 4 Shader for Materials if available. Default:1
+UseCiv4Shader=1
+ImportUPB=1
+IgnoreRootNode=0
 
 [BipedImport]
 ; Top level bone import setting. Default:1
@@ -123,6 +150,8 @@ ReplaceTCBRotationWithBezier=1
 ApplyOverallTransformToSkinAndBones=1
 ; Clear Animation on Import. Default: 1
 ClearAnimation=1
+AddNoteTracks=1
+AddTimeTags=1
 
 [Collision]
 ; Scale Factor when blowing up bhk Shapes
@@ -139,25 +168,30 @@ bhkScaleFactor=7.0
 ;
 ; UseSkeleton - Whether to use skeleton. Default: 0
 ; Skeleton - Default Skeleton to use when importing oblivion meshes
-; GoToSkeletonBindPosition - Morrowind trishape data is not in the correct bind position for import. Default: 1
+; TextureUseFullPath - Whether to use fully qualified names when exporting. Default: 0
+; SupportPrnStrings - Whether this application supports Prn Extra Strings as a substitute mechanism for bones parenting
+; Rotate90Degrees - Semicolon separated list of nodes that need to be rotated 90 degrees in Y Axis when importing through Prn lists
 
 [Oblivion]
 NiVersion=20.0.0.5
 NiUserVersion=11
 ; Installation Folder
 InstallPath=[HKLM\SOFTWARE\Bethesda Softworks\Oblivion]=@"Installed Path"
-ExtractFolder=E:\Nifs\Oblivion
-
 RootPath=${InstallPath}\Data
+
+;ExtractFolder=E:\Nifs\Oblivion ; This is the folder where you extracted the BSA contents for the Oblvion models
+ExtractFolder=$(RootPath)
 MeshRootPath=${ExtractFolder}\Oblivion - Meshes
 TextureRootPath=${ExtractFolder}\Oblivion - Textures - Compressed
+
 UseSkeleton=1
 Skeleton=${MeshRootPath}\meshes\characters\_male\skeleton.nif
 RootPaths=${RootPath};${MeshRootPath};${TextureRootPath}
 TextureRootPaths=${RootPath};${TextureRootPath}
 TextureExtensions=.dds;
 TextureSearchPaths=${RootPath}\Textures;${TextureRootPath}\Textures\Characters;${TextureRootPath}\Textures\Armor
-GoToSkeletonBindPosition=1
+SupportPrnStrings=1
+Rotate90Degrees=Bip?? Head
 
 [Morrowind]
 NiVersion=4.0.0.2
@@ -170,6 +204,8 @@ TextureRootPaths=${RootPath}\Textures;${ExtractFolder}\Textures
 TextureExtensions=.tga;
 TextureSearchPaths=${RootPath}\Textures
 GoToSkeletonBindPosition=1
+SupportPrnStrings=1
+Rotate90Degrees=Bip?? Head
 
 [Civilization 4]
 NiVersion=20.0.0.4
@@ -181,8 +217,8 @@ RootPaths=${ExtractFolder};${InstallPath}\Assets;${InstallPath}\Mods;%USERPROFIL
 TextureRootPaths=$(ExtractFolder)\art\shared\
 TextureExtensions=.dds;.bmp
 TextureSearchPaths=
-GoToSkeletonBindPosition=1
 DummyNodeMatches=MD;Bip;Bip??;* NonAccum;Effect*;Sound*;Dummy*
+TextureUseFullPath=1
 
 [Dark Age of Camelot]
 NiVersion=10.1.0.0
@@ -194,8 +230,6 @@ RootPaths=${Isles_InstallPath};${ExtractFolder}
 TextureRootPaths=$(ExtractFolder)
 TextureExtensions=.dds;.bmp;.tga
 TextureSearchPaths=
-GoToSkeletonBindPosition=1
-ApplyOverallTransformToSkinAndBones=0
 
 [User]
 NiVersion=20.0.0.5
diff --git a/NifCommon/AppSettings.cpp b/NifCommon/AppSettings.cpp
index d25330a5567c6fa437450c59048f67e64d414574..d4697021169436ed1ebfeb1c287f2678d7068a40 100644
--- a/NifCommon/AppSettings.cpp
+++ b/NifCommon/AppSettings.cpp
@@ -65,6 +65,8 @@ void AppSettings::ReadSettings(string iniFile)
    textureUseFullPath = GetSetting<bool>("TextureUseFullPath", textureUseFullPath);
 
    dummyNodeMatches = TokenizeString(GetSetting<string>("DummyNodeMatches").c_str(), ";");
+   rotate90Degrees = TokenizeString(GetSetting<string>("Rotate90Degrees").c_str(), ";");
+   supportPrnStrings = GetSetting<bool>("SupportPrnStrings", supportPrnStrings);
 }
 
 void AppSettings::WriteSettings(Interface *gi)
diff --git a/NifCommon/AppSettings.h b/NifCommon/AppSettings.h
index 87d93ad389f5b13fbd1f1a1383ab83dc217388be..7a0dd224af6ff83777eaa2cf28732bf7092738e5 100644
--- a/NifCommon/AppSettings.h
+++ b/NifCommon/AppSettings.h
@@ -26,6 +26,7 @@ public:
       , goToSkeletonBindPosition(true)
       , disableCreateNubsForBones(false)
       , textureUseFullPath(false)
+      , supportPrnStrings(false)
    {}
 
    std::string Name;
@@ -46,6 +47,8 @@ public:
    int applyOverallTransformToSkinAndBones;
    std::string NiVersion;
    int NiUserVersion;
+   stringlist rotate90Degrees;
+   bool supportPrnStrings;
 
    static void Initialize(Interface *gi);
    void ReadSettings(std::string iniFile);
diff --git a/NifCommon/Hyperlinks.cpp b/NifCommon/Hyperlinks.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0fead5cb320d296a5e30b896a13685898bede550
--- /dev/null
+++ b/NifCommon/Hyperlinks.cpp
@@ -0,0 +1,179 @@
+// Hyperlinks.cpp
+//
+// Copyright 2002 Neal Stublen
+// All rights reserved.
+//
+// http://www.awesoftware.com
+//
+
+#include <windows.h>
+
+#include "Hyperlinks.h"
+
+
+#define PROP_ORIGINAL_FONT		TEXT("_Hyperlink_Original_Font_")
+#define PROP_ORIGINAL_PROC		TEXT("_Hyperlink_Original_Proc_")
+#define PROP_STATIC_HYPERLINK	TEXT("_Hyperlink_From_Static_")
+#define PROP_UNDERLINE_FONT		TEXT("_Hyperlink_Underline_Font_")
+
+
+LRESULT CALLBACK _HyperlinkParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+	WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
+
+	switch (message)
+	{
+	case WM_CTLCOLORSTATIC:
+		{
+			HDC hdc = (HDC) wParam;
+			HWND hwndCtl = (HWND) lParam;
+
+			BOOL fHyperlink = (NULL != GetProp(hwndCtl, PROP_STATIC_HYPERLINK));
+			if (fHyperlink)
+			{
+				LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
+				SetTextColor(hdc, RGB(0, 0, 192));
+				return lr;
+			}
+
+			break;
+		}
+	case WM_DESTROY:
+		{
+			SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc);
+			RemoveProp(hwnd, PROP_ORIGINAL_PROC);
+			break;
+		}
+	}
+	return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
+}
+
+LRESULT CALLBACK _HyperlinkProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+	WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
+
+	switch (message)
+	{
+	case WM_DESTROY:
+		{
+			SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc);
+			RemoveProp(hwnd, PROP_ORIGINAL_PROC);
+
+			HFONT hOrigFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
+			SendMessage(hwnd, WM_SETFONT, (WPARAM) hOrigFont, 0);
+			RemoveProp(hwnd, PROP_ORIGINAL_FONT);
+
+			HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
+			DeleteObject(hFont);
+			RemoveProp(hwnd, PROP_UNDERLINE_FONT);
+
+			RemoveProp(hwnd, PROP_STATIC_HYPERLINK);
+
+			break;
+		}
+	case WM_MOUSEMOVE:
+		{
+			if (GetCapture() != hwnd)
+			{
+				HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
+				SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
+				InvalidateRect(hwnd, NULL, FALSE);
+				SetCapture(hwnd);
+			}
+			else
+			{
+				RECT rect;
+				GetWindowRect(hwnd, &rect);
+
+				POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+				ClientToScreen(hwnd, &pt);
+
+				if (!PtInRect(&rect, pt))
+				{
+					HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
+					SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
+					InvalidateRect(hwnd, NULL, FALSE);
+					ReleaseCapture();
+               SetCursor(LoadCursor(NULL, IDC_ARROW));
+				}
+			}
+			break;
+		}
+	case WM_SETCURSOR:
+		{
+			// Since IDC_HAND is not available on all operating systems,
+			// we will load the arrow cursor if IDC_HAND is not present.
+			HCURSOR hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND));
+			if (NULL == hCursor)
+			{
+				hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
+			}
+			SetCursor(hCursor);
+			return TRUE;
+		}
+   case WM_KILLFOCUS:
+      {
+         if (GetCapture() == hwnd)
+         {
+            HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
+            SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
+            InvalidateRect(hwnd, NULL, FALSE);
+            ReleaseCapture();
+            SetCursor(LoadCursor(NULL, IDC_ARROW));
+         }
+      }
+      break;
+	}
+
+	return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
+}
+
+BOOL ConvertStaticToHyperlink(HWND hwndCtl)
+{
+	// Subclass the parent so we can color the controls as we desire.
+
+	HWND hwndParent = GetParent(hwndCtl);
+	if (NULL != hwndParent)
+	{
+		WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndParent, GWL_WNDPROC);
+		if (pfnOrigProc != _HyperlinkParentProc)
+		{
+			SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
+			SetWindowLong(hwndParent, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkParentProc);
+		}
+	}
+
+	// Make sure the control will send notifications.
+
+	DWORD dwStyle = GetWindowLong(hwndCtl, GWL_STYLE);
+	SetWindowLong(hwndCtl, GWL_STYLE, dwStyle | SS_NOTIFY);
+
+	// Subclass the existing control.
+
+	WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndCtl, GWL_WNDPROC);
+	SetProp(hwndCtl, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
+	SetWindowLong(hwndCtl, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkProc);
+
+	// Create an updated font by adding an underline.
+
+	HFONT hOrigFont = (HFONT) SendMessage(hwndCtl, WM_GETFONT, 0, 0);
+	SetProp(hwndCtl, PROP_ORIGINAL_FONT, (HANDLE) hOrigFont);
+
+	LOGFONT lf;
+	GetObject(hOrigFont, sizeof(lf), &lf);
+	lf.lfUnderline = TRUE;
+
+	HFONT hFont = CreateFontIndirect(&lf);
+	SetProp(hwndCtl, PROP_UNDERLINE_FONT, (HANDLE) hFont);
+
+	// Set a flag on the control so we know what color it should be.
+
+	SetProp(hwndCtl, PROP_STATIC_HYPERLINK, (HANDLE) 1);
+
+	return TRUE;
+}
+
+BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId)
+{
+	return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId));
+}
diff --git a/NifCommon/Hyperlinks.h b/NifCommon/Hyperlinks.h
new file mode 100644
index 0000000000000000000000000000000000000000..2fc4d0b088f302ad46fa56324e4ba21f7d0c1910
--- /dev/null
+++ b/NifCommon/Hyperlinks.h
@@ -0,0 +1,10 @@
+// Hyperlinks.h
+//
+// Copyright 2002 Neal Stublen
+// All rights reserved.
+//
+// http://www.awesoftware.com
+//
+
+BOOL ConvertStaticToHyperlink(HWND hwndCtl);
+BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId);
diff --git a/NifCommon/niutils.cpp b/NifCommon/niutils.cpp
index b46ca0d562248312e56887780d56b560cf81b5ab..89c629024b465a72bbae345bd096e4f7ced4ab90 100644
--- a/NifCommon/niutils.cpp
+++ b/NifCommon/niutils.cpp
@@ -937,3 +937,74 @@ void CalcCenteredSphere(const vector<Vector3>& vertices, Vector3& center, float&
       radius = max(radius, mag);
    }
 }
+
+
+
+static void TransformVector3(Matrix44& tm, vector<Vector3>& pts)
+{
+   Matrix44::IDENTITY;
+   for (vector<Vector3>::iterator itr = pts.begin(); itr != pts.end(); ++itr)
+   {    
+      Matrix44 m4(*itr, Matrix33::IDENTITY, 1.0f);
+      Matrix44 ntm = m4 * tm;
+      Vector3 v  = ntm.GetTranslation();
+      (*itr) = v;
+   }
+}
+
+void CollapseGeomTransform(NiNode node) {
+   
+}
+
+void CollapseGeomTransform(NiTriBasedGeomRef shape)
+{
+   NiTriBasedGeomDataRef data = shape->GetData();
+   vector<Vector3> verts = data->GetVertices();
+   vector<Vector3> norms = data->GetNormals();
+   int nuvsets = data->GetUVSetCount();
+   vector< vector<TexCoord> > uvSets;
+   uvSets.resize(nuvsets);
+   for (int i=0; i<nuvsets; ++i)
+      uvSets[i] = data->GetUVSet(i);
+
+   Matrix44 ltm = shape->GetLocalTransform();
+   Matrix44 invtm = ltm.Inverse();
+   Matrix44 tm = ltm * invtm;
+   shape->SetLocalTransform(tm);
+
+   TransformVector3(ltm, verts);
+
+   data->SetVertices(verts);
+   data->SetNormals(norms);
+   data->SetUVSetCount(nuvsets);
+   for (int i=0; i<nuvsets; ++i)
+      data->SetUVSet(i, uvSets[i]);
+}
+
+void CollapseGeomTransforms(vector<NiTriBasedGeomRef>& shapes)
+{
+   for (vector<NiTriBasedGeomRef>::iterator itr = shapes.begin(); itr != shapes.end(); ++itr) {
+      CollapseGeomTransform(*itr);
+   }
+}
+
+void FixNormals(vector<Triangle>& tris, vector<Vector3>& verts, vector<Vector3>& norms)
+{
+   if (tris.size() != norms.size())
+      return;
+
+   int n = tris.size();
+   for (int i=0; i < n; ++i)
+   {
+      Triangle& tri = tris[i];
+      Vector3 v1 = verts[tri.v1];
+      Vector3 v2 = verts[tri.v2];
+      Vector3 v3 = verts[tri.v3];
+      Vector3 n1 = (v2-v1).CrossProduct(v3-v1).Normalized();
+      Vector3 n2 = norms[i];
+      float dp = n1.DotProduct(n2);
+      if ( dp < 0.0f ) {
+         std::swap(tri.v3, tri.v2);
+      }
+   }
+}
\ No newline at end of file
diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h
index 294205978136de04e80ad65b70568f4c8006bc94..3403a7d2595f5e1c1951d36d320d73fc849445d9 100644
--- a/NifCommon/niutils.h
+++ b/NifCommon/niutils.h
@@ -395,4 +395,8 @@ inline Niflib::Ref<T> CreateNiObject() {
    return Niflib::StaticCast<T>(Niflib::CreateObject(T::TypeConst().GetTypeName()));
 }
 
+void CollapseGeomTransform(Niflib::NiTriBasedGeomRef shape);
+void CollapseGeomTransforms(std::vector<Niflib::NiTriBasedGeomRef>& shapes);
+void FixNormals(std::vector<Niflib::Triangle>& tris, std::vector<Niflib::Vector3>& verts, std::vector<Niflib::Vector3>& norms);
+
 #endif // _NIUTILS_H_
\ No newline at end of file
diff --git a/NifExport/Animation.cpp b/NifExport/Animation.cpp
index 9496485d03cc33c8fbcf94f72626d760012e9c85..865f94c79821c52f16c44ec2a8c392752e600d3c 100644
--- a/NifExport/Animation.cpp
+++ b/NifExport/Animation.cpp
@@ -52,7 +52,7 @@ struct AnimationExport
 
    bool doExport(NiControllerSequenceRef seq);
    bool doExport(NiControllerManagerRef ctrl, INode *node);
-   bool exportController(INode *node);
+   bool exportController(INode *node, bool hasAccum);
    Control *GetTMController(INode* node);
    NiTimeControllerRef exportController(INode *node, Interval range, bool setTM );
    bool GetTextKeys(INode *node, vector<StringKey>& textKeys);
@@ -134,7 +134,7 @@ NiNodeRef Exporter::createAccumNode(NiNodeRef parent, INode *node)
 {
    NiNodeRef accumNode;
    bool isTracked = isNodeTracked(node);
-   if (!Exporter::mAllowAccum || !isTracked)
+   if (!Exporter::mAllowAccum || (!isTracked && !isSkeletonRoot(node)))
    {
       accumNode = parent;
    }
@@ -369,28 +369,7 @@ bool AnimationExport::doExport(NiControllerSequenceRef seq)
 
    // Now let the fun begin.
 
-   bool ok = exportController(node);
-
-   // Handle NonAccum
-   if (ok && Exporter::mAllowAccum)
-   {
-      NiNodeRef ninode = new NiNode();
-      ninode->SetName(FormatString("%s NonAccum", node->GetName()));
-
-      if (Exporter::mNifVersionInt >= VER_10_2_0_0)
-      {
-         NiTransformControllerRef control = new NiTransformController();
-         NiTransformInterpolatorRef interp = new NiTransformInterpolator();
-         ninode->AddController(StaticCast<NiTimeController>(control));
-         control->SetInterpolator(StaticCast<NiInterpolator>(interp));
-
-         interp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) );
-         interp->SetScale( 1.0f );
-         interp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) );
-         seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), Exporter::mDefaultPriority);
-      }
-   }
-   return ok;
+   return exportController(node, true);
 }
 
 bool AnimationExport::doExport(NiControllerManagerRef mgr, INode *node)
@@ -521,30 +500,7 @@ bool AnimationExport::doExport(NiControllerManagerRef mgr, INode *node)
       this->range = this->ranges[this->seq];
 
       // Now let the fun begin.
-      bool ok = exportController(node);
-
-      // Handle NonAccum
-      if (ok && Exporter::mAllowAccum)
-      {
-         NiNodeRef ninode = ne.getNode( FormatString("%s NonAccum", node->GetName()) );
-         objRefs.insert( StaticCast<NiAVObject>(ninode) );
-
-         if (Exporter::mNifVersionInt >= VER_10_2_0_0)
-         {
-            NiTransformControllerRef control = new NiTransformController();
-            NiTransformInterpolatorRef interp = new NiTransformInterpolator();
-            ninode->AddController(StaticCast<NiTimeController>(control));
-            control->SetInterpolator(StaticCast<NiInterpolator>(interp));
-
-            interp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) );
-            interp->SetScale( 1.0f );
-            interp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) );
-            seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), Exporter::mDefaultPriority);
-
-            // now remove temporary controller
-            ninode->RemoveController(StaticCast<NiTimeController>(control));
-         }
-      }
+      bool ok = exportController(node, true);
    }
 
    // Set objects with animation
@@ -598,6 +554,8 @@ NiTimeControllerRef AnimationExport::exportController(INode *node, Interval rang
    bool skip = false;
    NiTimeControllerRef timeControl;
 
+   ne.ProgressUpdate(Exporter::Animation, FormatText("'%s' Animation", node->GetName()));
+
    // Primary recursive decent routine
 
    ObjectState os = node->EvalWorldState(range.Start()); 
@@ -626,11 +584,11 @@ NiTimeControllerRef AnimationExport::exportController(INode *node, Interval rang
          bool keepData = false;
 
          // Set default transform to NaN except for root node
-         Vector3 trans(FloatNegINF, FloatNegINF, FloatNegINF);
-         Quaternion rot(FloatNegINF, FloatNegINF, FloatNegINF, FloatNegINF);
+         //Vector3 trans(FloatNegINF, FloatNegINF, FloatNegINF);
+         //Quaternion rot(FloatNegINF, FloatNegINF, FloatNegINF, FloatNegINF);
          float scale = FloatNegINF;
-         //Vector3 trans = TOVECTOR3(tm.GetTrans());
-         //Quaternion rot = TOQUAT( Quat(tm), true );
+         Vector3 trans = TOVECTOR3(tm.GetTrans());
+         Quaternion rot = TOQUAT( Quat(tm), true );
 
          NiNodeRef ninode = ne.getNode( node->GetName() );
          if (setTM) {
@@ -867,7 +825,7 @@ NiTimeControllerRef AnimationExport::exportController(INode *node, Interval rang
    return timeControl;
 }
 
-bool AnimationExport::exportController(INode *node)
+bool AnimationExport::exportController(INode *node, bool hasAccum)
 {
    bool ok = true;
 
@@ -880,11 +838,42 @@ bool AnimationExport::exportController(INode *node)
    if (control != NULL)
    {
       NiSingleInterpolatorControllerRef interpControl = DynamicCast<NiSingleInterpolatorController>(control);
-      if (interpControl) {
+      if (interpControl) 
+      {
          // Get Priority from node
          float priority;
          npGetProp(node, NP_ANM_PRI, priority, Exporter::mDefaultPriority);
          seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(control), priority);
+
+         // Handle NonAccum 
+         if (Exporter::mAllowAccum && hasAccum)
+         {
+            NiTransformInterpolatorRef interp = DynamicCast<NiTransformInterpolator>(interpControl->GetInterpolator());
+            NiNodeRef accnode = ne.getNode( FormatString("%s NonAccum", node->GetName()) );
+            objRefs.insert( StaticCast<NiAVObject>(accnode) );
+
+            if (Exporter::mNifVersionInt >= VER_10_2_0_0)
+            {
+               NiTransformControllerRef acccontrol = new NiTransformController();
+               NiTransformInterpolatorRef accinterp = new NiTransformInterpolator();
+               accnode->AddController(StaticCast<NiTimeController>(acccontrol));
+               acccontrol->SetInterpolator(StaticCast<NiInterpolator>(accinterp));
+
+               accinterp->SetTranslation( Vector3(0.0f, 0.0f, 0.0f) );
+               accinterp->SetScale( 1.0f );
+               accinterp->SetRotation( Quaternion(1.0f, 0.0f, 0.0f, 0.0f) );
+
+               // Transfer entire data to accum node
+               if (interp != NULL) {
+                  accinterp->SetData( interp->GetData() );
+                  interp->SetData( NiTransformDataRef() );
+               }
+               seq->AddInterpolator(StaticCast<NiSingleInterpolatorController>(acccontrol), Exporter::mDefaultPriority);
+
+               accnode->RemoveController(acccontrol);
+            }
+         }
+
       }
       else 
       {
@@ -899,7 +888,7 @@ bool AnimationExport::exportController(INode *node)
    for (int i=0, n=node->NumberOfChildren(); ok && i<n; ++i)
    {
       INode *child = node->GetChildNode(i);
-      ok |= exportController(child);
+      ok |= exportController(child, false);
    }
    return ok;
 }
\ No newline at end of file
diff --git a/NifExport/Coll.cpp b/NifExport/Coll.cpp
index 39a4c81feddab0d3de873233bf178d270dfe61a4..5503d203fc12cd047e0657cb7202159058c01234 100755
--- a/NifExport/Coll.cpp
+++ b/NifExport/Coll.cpp
@@ -249,6 +249,8 @@ bool Exporter::makeCollisionHierarchy(NiNodeRef &parent, INode *node, TimeValue
 
 Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node)
 {
+   ProgressUpdate(Collision, FormatText("'%s' Collision", node->GetName()));
+
 	// marked as collision?
 	bool coll = npIsCollision(node);
 
diff --git a/NifExport/Config.cpp b/NifExport/Config.cpp
index e4655ff47fd769cc0c3f9a6954665e112a410015..39ab0efcbaf817a43e6c64f0e34dc51e241611b2 100755
--- a/NifExport/Config.cpp
+++ b/NifExport/Config.cpp
@@ -76,9 +76,14 @@ void Exporter::writeConfig(Interface *i)
       SetIniValue(NifExportSection, "MultiplePartitions", mMultiplePartitions, iniName);
       SetIniValue<int>(NifExportSection, "BonesPerVertex", mBonesPerVertex, iniName);     
       SetIniValue<int>(KfExportSection, "BonesPerPartition", mBonesPerPartition, iniName);
-      SetIniValue(NifExportSection, "UseTimeTags", mUseTimeTags, iniName);
+      //SetIniValue(NifExportSection, "UseTimeTags", mUseTimeTags, iniName);
 
       SetIniValue(NifExportSection, "AllowAccum", mAllowAccum, iniName);
+      SetIniValue(NifExportSection, "CollapseTransforms", mCollapseTransforms, iniName);
+      SetIniValue(NifExportSection, "FixNormals", mFixNormals, iniName);
+      SetIniValue(NifExportSection, "TangentAndBinormalExtraData", mTangentAndBinormalExtraData, iniName);
+      SetIniValue(NifExportSection, "UseAlternateStripper", mUseAlternateStripper, iniName);
+      
 
       SetIniValue<string>(NifExportSection, "Creator", mCreatorName, iniName);
       
@@ -143,11 +148,15 @@ void Exporter::readConfig(Interface *i)
 
       mMultiplePartitions = GetIniValue(NifExportSection, "MultiplePartitions", false, iniName);
       mBonesPerVertex = GetIniValue<int>(NifExportSection, "BonesPerVertex", 4, iniName);     
-      mBonesPerPartition = GetIniValue<int>(KfExportSection, "BonesPerPartition", 20, iniName);
+      mBonesPerPartition = GetIniValue<int>(NifExportSection, "BonesPerPartition", 20, iniName);
 
-      mUseTimeTags = GetIniValue(NifExportSection, "UseTimeTags", false, iniName);
+      //mUseTimeTags = GetIniValue(NifExportSection, "UseTimeTags", false, iniName);
       mAllowAccum = GetIniValue(NifExportSection, "AllowAccum", true, iniName);
+      mCollapseTransforms = GetIniValue(NifExportSection, "CollapseTransforms", false, iniName);
+      mFixNormals = GetIniValue(NifExportSection, "FixNormals", false, iniName);
+      mTangentAndBinormalExtraData = GetIniValue(NifExportSection, "TangentAndBinormalExtraData", false, iniName);
 
+      mUseAlternateStripper = GetIniValue(NifExportSection, "UseAlternateStripper", false, iniName);
       mCreatorName = GetIniValue<string>(NifExportSection, "Creator", "", iniName);
   }
 }
@@ -180,6 +189,69 @@ void Exporter::writeKfConfig(Interface *i)
 }
 
 
+AppSettings * Exporter::exportAppSettings()
+{
+   TCHAR iniName[MAX_PATH];
+   LPCTSTR pluginDir = GetCOREInterface()->GetDir(APP_PLUGCFG_DIR);
+   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+
+   // Update the current app version
+   AppSettings * appSettings = FindAppSetting(Exporter::mGameName);
+   if (appSettings == NULL && !TheAppSettings.empty())
+      appSettings = &TheAppSettings.front();
+
+   if (Exporter::mAutoDetect){
+      SetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName);
+   } else {
+      SetIniValue<string>(NifExportSection, "CurrentApp", appSettings->Name, iniName);
+   }
+
+   appSettings->NiVersion = Exporter::mNifVersion;
+   appSettings->NiUserVersion = Exporter::mNifUserVersion;
+   appSettings->rotate90Degrees = Exporter::mRotate90Degrees;
+   appSettings->supportPrnStrings = Exporter::mSupportPrnStrings;
+
+   return appSettings;
+}
+
+AppSettings *Exporter::importAppSettings(string fname)
+{
+   AppSettings *appSettings = NULL;
+   TCHAR iniName[MAX_PATH];
+   LPCTSTR pluginDir = GetCOREInterface()->GetDir(APP_PLUGCFG_DIR);
+   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+
+   // Locate which application to use. If Auto, find first app where this file appears in the root path list
+   string curapp = GetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName);
+   if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
+      Exporter::mAutoDetect = true;
+      // Scan Root paths
+      for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
+         if ((*itr).IsFileInRootPaths(fname)) {
+            appSettings = &(*itr);
+            break;
+         }
+      }
+   } else {
+      Exporter::mAutoDetect = false;
+      appSettings = FindAppSetting(curapp);
+   }
+   if (appSettings == NULL && !TheAppSettings.empty())
+      appSettings = &TheAppSettings.front();
+
+   if (!appSettings)
+      return NULL;
+
+   Exporter::mGameName = appSettings->Name;
+   Exporter::mNifVersion = appSettings->NiVersion;
+   Exporter::mNifUserVersion = appSettings->NiUserVersion;
+   if (!appSettings->rotate90Degrees.empty())
+      Exporter::mRotate90Degrees = appSettings->rotate90Degrees;
+   Exporter::mSupportPrnStrings = appSettings->supportPrnStrings;
+
+   return appSettings;
+}
+
 void regSet(HKEY hKey, const char *value, float f)
 {
 	DWORD dw = *((DWORD*)&f);
diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp
index 2067aba6a31a04bc79e246be547623b785990eb4..d65cecd7f6c693649ea4576b197127f4f3e7baaa 100755
--- a/NifExport/Exporter.cpp
+++ b/NifExport/Exporter.cpp
@@ -40,71 +40,152 @@ bool Exporter::mUseTimeTags = false;
 bool Exporter::mAutoDetect = true;
 bool Exporter::mAllowAccum = true;
 string Exporter::mCreatorName;
+bool Exporter::mCollapseTransforms = false;
+bool Exporter::mFixNormals = false;
+bool Exporter::mTangentAndBinormalExtraData = false;
+bool Exporter::mSupportPrnStrings = false;
+stringlist Exporter::mRotate90Degrees;
+bool Exporter::mSuppressPrompts = false;
+bool Exporter::mUseAlternateStripper = false;
+
+static bool IsNodeOrParentSelected(INode *node) {
+   if (node == NULL)
+      return false;
+   if (node->Selected())
+      return true;
+   return IsNodeOrParentSelected(node->GetParentNode());
+}
 
 Exporter::Exporter(Interface *i, AppSettings *appSettings)
    : mI(i), mAppSettings(appSettings)
 {
+   memset(progressCounters, 0, sizeof(progressCounters));
+   memset(progressMax, 0, sizeof(progressMax));
 }
 
-
 Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
 {
-   //root->SetName("Scene Root");
+   root->SetName("Scene Root");
 
-   int nifVersion = GetVersion(Exporter::mNifVersion);
+   int nifVersion = ParseVersionString(Exporter::mNifVersion);
    mIsBethesda = (nifVersion == VER_20_0_0_5 || nifVersion == VER_20_0_0_4) && (Exporter::mNifUserVersion == 11);
 
    if (mUseTimeTags && nifVersion >= VER_20_0_0_4) {
       throw runtime_error("Time tag sequences are not supported for version 20.0.0.4 or higher.");
    }
 
-   CalcBoundingBox(node, mBoundingBox);
-
-   if (mSkeletonOnly && mIsBethesda)
+   if (!Exporter::mSelectedOnly)
    {
-      BSBoundRef bsb = CreateNiObject<BSBound>();
-      bsb->SetName("BBX");    
-      bsb->SetCenter( TOVECTOR3(mBoundingBox.Center()) );
-      bsb->SetDimensions( TOVECTOR3(mBoundingBox.Width() / 2.0f) );
-      root->AddExtraData(DynamicCast<NiExtraData>(bsb));
-
-      BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
-      bsx->SetName("BSX");
-      bsx->SetFlags( 0x00000007 );
-      root->AddExtraData(DynamicCast<NiExtraData>(bsx));
+      CalcBoundingBox(node, mBoundingBox);
+
+      if (mSkeletonOnly && mIsBethesda)
+      {
+         BSBoundRef bsb = CreateNiObject<BSBound>();
+         bsb->SetName("BBX");    
+         bsb->SetCenter( TOVECTOR3(mBoundingBox.Center()) );
+         bsb->SetDimensions( TOVECTOR3(mBoundingBox.Width() / 2.0f) );
+         root->AddExtraData(DynamicCast<NiExtraData>(bsb));
+
+         BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
+         bsx->SetName("BSX");
+         bsx->SetFlags( 0x00000007 );
+         root->AddExtraData(DynamicCast<NiExtraData>(bsx));
+      }
+      else if (mExportCollision && mIsBethesda)
+      {
+         BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
+         bsx->SetName("BSX");
+         bsx->SetFlags( 0x00000002 );
+         root->AddExtraData(DynamicCast<NiExtraData>(bsx));
+      }
+
+      exportUPB(root, node);
    }
-   else if (mExportCollision && mIsBethesda)
-   {
-	   BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
-	   bsx->SetName("BSX");
-	   bsx->SetFlags( 0x00000002 );
-      root->AddExtraData(DynamicCast<NiExtraData>(bsx));
+
+   mNiRoot = root;
+   if (mSelectedOnly) {
+      int count = 0;
+      int n = mI->GetSelNodeCount();
+      vector<INode*> selectedRoots;
+      for (int i=0; i<n; ++i) {
+         INode * selNode = mI->GetSelNode(i);
+         if (!IsNodeOrParentSelected(selNode->GetParentNode())) {
+            selectedRoots.push_back(selNode);
+            count += countNodes(selNode);
+         }
+      }
+      if (selectedRoots.size() == 0) {
+         throw runtime_error("No Nodes have been selected for Export.");
+      }
+
+      progressMax[Geometry] = progressMax[Skin] = count;
+      if (mExportCollision)
+         progressMax[Collision] = progressMax[Geometry];
+      if (mExportType == NIF_WO_ANIM)
+         progressMax[Animation] = progressMax[Geometry];
+     
+      for (int i=0; i<selectedRoots.size(); i++){
+         Result result = exportNodes(root, selectedRoots[i]);
+         if (result != Ok && result != Skip)
+            return result;
+         if (mExportCollision) {
+            result = exportCollision(root, selectedRoots[i]);
+            if (result != Ok)
+               return result;
+         }
+      }
+      // Always Zero out root transforms
+      vector<NiAVObjectRef> children = root->GetChildren();
+      for (int i=0; i<children.size(); ++i){
+         children[i]->SetLocalTransform(Matrix44::IDENTITY);
+      }
+
+      // Fix Used Nodes that where never properly initialized.  Happens normally during select export
+      for (NodeMap::iterator itr = mNodeMap.begin(); itr != mNodeMap.end(); ++itr) {
+         NiNodeRef bone = (*itr).second;
+         if (bone->GetParent() == NULL) {
+            if (INode* boneNode = mI->GetINodeByName((*itr).first.c_str())) {
+               makeNode(root, boneNode, false);
+            }
+         }
+      }
+
+      // Special case when exporting a single branch, use first child as scene root
+      if (selectedRoots.size() == 1 ) {
+         vector<NiNodeRef> childnodes = DynamicCast<NiNode>(root->GetChildren());
+         if (childnodes.size() == 1) {
+            NiNodeRef child = childnodes[0];
+            root->RemoveChild(child);
+            root = child;
+            mNiRoot = root;
+            exportPrn(root, selectedRoots[0]);
+         }
+      }
+
+   } else {
+      // Estimate progress bar
+      int count = countNodes(node);
+      progressMax[Geometry] = progressMax[Skin] = count;
+      if (mExportCollision)
+         progressMax[Collision] = progressMax[Geometry];
+      if (mExportType == NIF_WO_ANIM)
+         progressMax[Animation] = progressMax[Geometry];
+
+      // Normal export
+      Result result = exportNodes(root, node);
+      if (result != Ok)
+         return result;
+      if (mExportCollision) {
+         result = exportCollision(root, node);
+         if (result != Ok)
+            return result;
+      }
    }
 
-   bool ok = exportUPB(root, node);
-   //if (!ok && Exporter::mExportCollision)
-   //{
-   //   NiStringExtraDataRef strings = DynamicCast<NiStringExtraData>(CreateBlock("NiStringExtraData"));	
-   //   strings->SetName("UPB");
-   //   strings->SetData("Ellasticity = 0.300000\r\nFriction = 0.300000\r\nUnyielding = 0\r\nProxy_Geometry = <None>\r\nUse_Display_Proxy = 0\r\nDisplay_Children = 1\r\nDisable_Collisions = 0\r\nInactive = 0\r\nDisplay_Proxy = <None>\r\nMass = 0.000000\r\nSimulation_Geometry = 2\r\nCollision_Groups = 589825\r\n");
-   //   root->AddExtraData(DynamicCast<NiExtraData>(strings));
-   //}
-
-	mNiRoot = root;
-	
-	Result result = exportNodes(root, node);
-   if (result != Ok)
-      return result;
-		
-	if (mExportCollision)
-	{
-		result = exportCollision(root, node);
-		if (result != Ok)
-			return result;
-	}
    // handle post export callbacks (like skin)
-   for (CallbackList::iterator cb = mPostExportCallbacks.begin(); cb != mPostExportCallbacks.end(); cb = mPostExportCallbacks.erase(cb))
-   {
+   progressMax[Skin] = mPostExportCallbacks.size();
+   for (CallbackList::iterator cb = mPostExportCallbacks.begin(); cb != mPostExportCallbacks.end(); cb = mPostExportCallbacks.erase(cb)) {
+      ProgressUpdate(Skin, NULL);
       (*cb)->execute();
       delete (*cb);
    }
@@ -113,15 +194,18 @@ Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
       removeUnreferencedBones(mNiRoot);
    if (mSortNodesToEnd)
       sortNodes(mNiRoot);
-
+   root = mNiRoot;
 	return Ok;
 }
 
 // Primary recursive decent routine
 Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
 {
+   TSTR nodeName = node->GetName();
    bool coll = npIsCollision(node);
-   if (coll ||	(node->IsHidden() && !mExportHidden && !coll) || (mSelectedOnly && !node->Selected()))
+
+   ProgressUpdate(Geometry, FormatText("'%s' Geometry", nodeName.data()));
+   if (coll ||	(node->IsHidden() && !mExportHidden && !coll))
       return Skip;
 
    bool local = !mFlattenHierarchy;
@@ -134,7 +218,6 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
    // Always skip bones and bipeds
    SClass_ID scid = node->SuperClassID();
    Class_ID ncid = node->ClassID();
-   TSTR nodeName = node->GetName();
    TSTR nodeClass; node->GetClassName(nodeClass);
 
    // For some unusual reason, bones named Helper are converted to Meshes and 
@@ -168,9 +251,13 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
             // Else dont create a node
             newParent = nodeParent;
          }
-         Result result = exportMesh(newParent, node, t);
-         if (result != Ok)
-            return result;
+         // No need to export meshes when NIF is not exported.
+         if (mExportType != SINGLE_KF_WO_NIF && mExportType != MULTI_KF_WO_NIF)
+         {
+            Result result = exportMesh(newParent, node, t);
+            if (result != Ok)
+               return result;
+         }
       }
    }
    else if (mExportCameras && os.obj && os.obj->SuperClassID()==CAMERA_CLASS_ID)
@@ -196,3 +283,24 @@ Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
    }
    return Ok;
 }
+
+void Exporter::ProgressUpdate(ProgressSection section, const TCHAR *s)
+{
+   if (mSuppressPrompts)
+      return;
+
+   if (mI->GetCancel()){
+      throw CancelExporterException();
+   }
+   int total = 1;
+   int percent = 1;
+   for (int i=0; i< int(ProgressSectionCount); i++){
+      total += progressMax[i];
+      if (i < section) {
+         percent += progressMax[i];
+      } else if (i == section ) {
+         percent += (++progressCounters[i]);
+      }
+   }
+   mI->ProgressUpdate( (percent * 100) / total , s == NULL ? TRUE : FALSE,  const_cast<TCHAR*>(s));
+}
diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h
index 4f0a481a34ec803ebf9c743dffd60edbade1f271..146a3b6a271bc62ead3a36e93ede46e0ae1ce11a 100755
--- a/NifExport/Exporter.h
+++ b/NifExport/Exporter.h
@@ -46,6 +46,8 @@ public:
       virtual Result execute() = 0;
    };
 
+   class CancelExporterException{};
+
 
 	/* exporter version */
 	static int				mVersion;
@@ -85,6 +87,13 @@ public:
    static bool          mAutoDetect;
    static bool          mAllowAccum;
    static string        mCreatorName;
+   static bool          mCollapseTransforms;
+   static bool          mFixNormals;
+   static bool          mTangentAndBinormalExtraData;
+   static stringlist    mRotate90Degrees;
+   static bool          mSupportPrnStrings;
+   static bool          mSuppressPrompts;
+   static bool          mUseAlternateStripper;
 
 	Exporter(Interface *i, AppSettings *appSettings);
 
@@ -109,6 +118,9 @@ public:
    // reads config from registry
    static void				readKfConfig(Interface *i);
 
+   static AppSettings * exportAppSettings();
+   static AppSettings * importAppSettings(string fname);
+
 public:
 	typedef vector<unsigned short> TriStrip;
 	typedef	list<TriStrip> TriStrips;
@@ -225,8 +237,23 @@ public:
    NiNodeRef exportBone(NiNodeRef parent, INode *node);
    Result exportLight(NiNodeRef root, INode *node, GenLight* light);
    void getChildNodes(INode *node, vector<NiNodeRef>&list);
-
+   bool exportPrn(NiNodeRef &root, INode *node);
    NiNodeRef createAccumNode(NiNodeRef parent, INode *node);
+   int countNodes(INode *node);
+   bool isSkeletonRoot(INode *node);
+
+   /* Progress Bar stuff */
+   enum ProgressSection
+   {
+      Geometry,
+      Animation,
+      Collision,
+      Skin,
+      ProgressSectionCount
+   };
+   int progressCounters[ProgressSectionCount];
+   int progressMax[ProgressSectionCount];
+   void ProgressUpdate( ProgressSection section, const TCHAR *s );
 };
 
 #endif 
diff --git a/NifExport/KfExport.cpp b/NifExport/KfExport.cpp
index 8036afb0dd00f206a29f74a3257816236f6ebc72..0d85c97b45a488494901a02fd618b3f0a19ae56f 100644
--- a/NifExport/KfExport.cpp
+++ b/NifExport/KfExport.cpp
@@ -8,6 +8,7 @@ using namespace Niflib;
 #define KFEXPORT_CLASS_ID	Class_ID(0xa57ff0a4, 0xa0374ffc)
 
 static LPCTSTR KfExportSection = TEXT("KfExport");
+extern TSTR shortDescription;
 
 class KfExport : public SceneExport 
 {
@@ -16,8 +17,9 @@ public:
    static HWND		hParams;
    int				mDlgResult;
    TSTR iniFileName;
-   TSTR shortDescription;
    TSTR fileVersion;
+   TSTR webSite;
+   TSTR wikiSite;
 
    int				ExtCount();					// Number of extensions supported
    const TCHAR		*Ext(int n);					// Extension #n (i.e. "3DS")
@@ -114,8 +116,8 @@ static BOOL CALLBACK KfExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam
             GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
             if (tmp[0] != 0)
             {
-               int nifVersion = GetVersion(tmp);
-               if (!IsVersionSupported(nifVersion))
+               int nifVersion = ParseVersionString(tmp);
+               if (!IsSupportedVersion(nifVersion))
                {
                   MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP);
                   return FALSE;
@@ -148,19 +150,19 @@ static BOOL CALLBACK KfExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam
             EndDialog(hWnd, imp->mDlgResult=IDCANCEL);
             close = true;
             break;
+
+         case IDC_LBL_LINK:
+            ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL);
+            break;
+
+         case IDC_LBL_WIKI:
+            ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL);
+            break;
          }
 
          if (close)
             SendMessage(hWnd, WM_CLOSE, 0, 0);
       }
-      else if (HIWORD(wParam) == STN_CLICKED)
-      {
-         if (LOWORD(wParam) == IDC_LBL_LINK)
-         {
-            ShellExecute(hWnd, "open", "http://www.niftools.org",
-               NULL, NULL, SW_SHOWDEFAULT);
-         }
-      }
       else if (HIWORD(wParam) == CBN_SELCHANGE)
       {
          if (LOWORD(wParam) == IDC_CB_GAME)
@@ -200,6 +202,8 @@ KfExport::KfExport()
    iniFileName = iniName;
    shortDescription = GetIniValue<TSTR>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName);
    fileVersion = GetFileVersion(NULL);
+   webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org", iniFileName);
+   wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max", iniFileName);
 }
 
 KfExport::~KfExport() 
@@ -328,8 +332,8 @@ int	KfExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL s
 
       if (!Exporter::mNifVersion.empty())
       {
-         nifVersion = GetVersion(Exporter::mNifVersion);
-         if (!IsVersionSupported(nifVersion))
+         nifVersion = ParseVersionString(Exporter::mNifVersion);
+         if (!IsSupportedVersion(nifVersion))
             throw exception(FormatString("Version '%s' is not a supported version.").c_str());
       }
 
diff --git a/NifExport/Mesh.cpp b/NifExport/Mesh.cpp
index 1601f417f6240e5c97575efb81c19a83cd38826e..6752fb9e2917d887bc1f58d9d2b694bc79f7c8d4 100755
--- a/NifExport/Mesh.cpp
+++ b/NifExport/Mesh.cpp
@@ -112,6 +112,11 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 
          shape->SetName(name);
          shape->SetLocalTransform(tm);
+
+         if (Exporter::mCollapseTransforms) {
+            CollapseGeomTransform(shape);
+         }
+         
          makeSkin(shape, node, grp->second, t);
 		}
 
@@ -129,9 +134,13 @@ NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp
 	NiTriBasedGeomRef shape;
 	NiTriBasedGeomDataRef data;
 
+   //if (Exporter::mFixNormals) {
+   //   FixNormals(grp.faces, grp.verts, grp.vnorms);
+   //}
+
 	if (exportStrips) {
       shape = new NiTriStrips();
-      data = new NiTriStripsData(grp.faces);
+      data = new NiTriStripsData(grp.faces, !mUseAlternateStripper);
 	} else {
       shape = new NiTriShape();
       data = new NiTriShapeData(grp.faces);
@@ -150,6 +159,9 @@ NiTriBasedGeomRef Exporter::makeMesh(NiNodeRef &parent, Mtl *mtl, FaceGroup &grp
 
 	shape->SetData(data);
 
+   if (Exporter::mTangentAndBinormalExtraData)
+      shape->UpdateTangentSpace();
+
 	NiAVObjectRef av(DynamicCast<NiAVObject>(shape));
 	makeMaterial(av, mtl);
 
@@ -380,17 +392,6 @@ Exporter::Result SkinInstance::execute()
       shape->GenHardwareSkinInfo(Exporter::mBonesPerPartition, Exporter::mBonesPerVertex);
    else
       shape->GenHardwareSkinInfo(0, 0);
-
-   //NiSkinInstanceRef skinInst = shape->GetSkinInstance();
-   //NiSkinDataRef skinData = skinInst->GetSkinData();
-   //Matrix44 tm = skinData->GetOverallTransform();
-   //Matrix3 otm = TOMATRIX3(tm);
-   //Matrix3 stm = TOMATRIX3(shape->GetLocalTransform());
-   //Matrix3 btm = Inverse(bone_init_tm) * stm;
-
-   //Matrix3 asdf = btm * otm;
-   //skinData->SetOverallTransform(TOMATRIX4(btm));
-      
    return Exporter::Ok;
 }
 
@@ -567,22 +568,21 @@ NiNodeRef Exporter::exportBone(NiNodeRef parent, INode *node)
             newParent->SetCollisionObject( NiCollisionObjectRef() );
          }        
          newParent = accumNode;
-      } else if (wildmatch("Bip??", node->GetName())) {
+      } else if (isSkeletonRoot(node)) {
          newParent = createAccumNode(newParent, node);
       }
    }
    else // normal handling
    {
       // Check for Accum Root using 
-      if (mExportType == NIF_WO_KF)
-      {
+      if (mExportType == NIF_WO_KF){
          // Add controllers
          if (Exporter::mAllowAccum) {
             newParent = createAccumNode(newParent, node);
          }
-      }
-      else if (mExportType != NIF_WO_ANIM && isNodeTracked(node)) 
-      {
+      } else if (mExportType != NIF_WO_ANIM && isNodeTracked(node)) {
+         newParent = createAccumNode(newParent, node);
+      } else if (isSkeletonRoot(node)) {
          newParent = createAccumNode(newParent, node);
       }
    }
diff --git a/NifExport/NifExport.cpp b/NifExport/NifExport.cpp
index cc14df14e5dfefa2fcc57f36178d7e15fed8b002..13a6e5d2a96661a8dc67c5e398d8a58185e57f4c 100755
--- a/NifExport/NifExport.cpp
+++ b/NifExport/NifExport.cpp
@@ -5,12 +5,13 @@
 #include "obj/NiControllerManager.h"
 #include "obj/NiTimeController.h"
 #include "obj/NiControllerSequence.h"
-
+#include "Hyperlinks.h"
 using namespace Niflib;
 
 #define NifExport_CLASS_ID	Class_ID(0xa57ff0a4, 0xa0374ffb)
 
 LPCTSTR NifExportSection = TEXT("MaxNifExport");
+TSTR shortDescription;
 
 class NifExport : public SceneExport 
 {
@@ -19,8 +20,9 @@ public:
 	static HWND		hParams;
 	int				mDlgResult;
    TSTR iniFileName;
-   TSTR shortDescription;
    TSTR fileVersion;
+   TSTR webSite;
+   TSTR wikiSite;
 		
 	int				ExtCount();					// Number of extensions supported
 	const TCHAR		*Ext(int n);					// Extension #n (i.e. "3DS")
@@ -34,7 +36,8 @@ public:
 	void			ShowAbout(HWND hWnd);		// Show DLL's "About..." box
 
 	BOOL			SupportsOptions(int ext, DWORD options);
-	int				DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
+   int				DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0);
+   int				DoExportInternal(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options);
 
 	NifExport();
 	~NifExport();		
@@ -63,199 +66,201 @@ ClassDesc2* GetNifExportDesc() { return &NifExportDesc; }
 
 extern list<NiObjectRef> GetAllObjectsByType( NiObjectRef const & root, const Type & type );
 
-
-
 BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
 	static NifExport *imp = NULL;
+   char buffer[256];
+   static bool setCursor = false;
 
 	switch (message) 
-	{
-		case WM_INITDIALOG:
-			{
-            imp = (NifExport *)lParam;
-
-            // Append file version to dialog
-            if (!imp->fileVersion.isNull()) {
-               char buffer[256];
-               GetWindowText(hWnd, buffer, _countof(buffer));
-               _tcscat(buffer, TEXT(" "));
-               _tcscat(buffer, imp->fileVersion);
-               SetWindowText(hWnd, buffer);
+   {
+   case WM_INITDIALOG:
+      {
+         imp = (NifExport *)lParam;
+
+         // Append file version to dialog
+         if (!imp->fileVersion.isNull()) {
+            GetWindowText(hWnd, buffer, _countof(buffer));
+            _tcscat(buffer, TEXT(" "));
+            _tcscat(buffer, imp->fileVersion);
+            SetWindowText(hWnd, buffer);
+         }
+
+         CenterWindow(hWnd,GetParent(hWnd));
+         CheckDlgButton(hWnd, IDC_CHK_STRIPS, Exporter::mTriStrips);
+         CheckDlgButton(hWnd, IDC_CHK_HIDDEN, Exporter::mExportHidden);
+         CheckDlgButton(hWnd, IDC_CHK_FURN, Exporter::mExportFurn);
+         CheckDlgButton(hWnd, IDC_CHK_LIGHTS, Exporter::mExportLights);
+         CheckDlgButton(hWnd, IDC_CHK_VCOLORS, Exporter::mVertexColors);
+         SetDlgItemText(hWnd, IDC_ED_TEXPREFIX, Exporter::mTexPrefix.c_str());
+         CheckDlgButton(hWnd, IDC_CHK_COLL, Exporter::mExportCollision);
+         CheckDlgButton(hWnd, IDC_CHK_REMAP, Exporter::mRemapIndices);
+
+         CheckDlgButton(hWnd, IDC_CHK_EXTRA, Exporter::mExportExtraNodes);
+         CheckDlgButton(hWnd, IDC_CHK_UPB, Exporter::mUserPropBuffer);
+         CheckDlgButton(hWnd, IDC_CHK_HIER, Exporter::mFlattenHierarchy);
+         CheckDlgButton(hWnd, IDC_CHK_REM_BONES, Exporter::mRemoveUnreferencedBones);
+         CheckDlgButton(hWnd, IDC_CHK_SORTNODES, Exporter::mSortNodesToEnd);
+         CheckDlgButton(hWnd, IDC_CHK_SKEL_ONLY, Exporter::mSkeletonOnly);
+         CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras);
+         CheckDlgButton(hWnd, IDC_CHK_BONE_COLL, Exporter::mGenerateBoneCollision);            
+
+         string selection = Exporter::mGameName;
+         string version = Exporter::mNifVersion;
+         string userVer = FormatString("%d", Exporter::mNifUserVersion);
+         for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr)
+            SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_ADDSTRING, 0, LPARAM(itr->Name.c_str()));
+         SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_SELECTSTRING, WPARAM(-1), LPARAM(selection.c_str()));
+         SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str()));
+         SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str()));
+         CheckDlgButton(hWnd, IDC_CHK_AUTO_DETECT, Exporter::mAutoDetect);
+
+         // Populate Type options
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_ANIM, LPARAM("NIF w/o Animation"));
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_KF, LPARAM("NIF with Animation"));
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WITH_NIF, LPARAM("Single KF with NIF"));
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WO_NIF, LPARAM("Single KF w/o NIF"));
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WITH_NIF, LPARAM("Multi KF with NIF"));
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WO_NIF, LPARAM("Multi KF w/o NIF"));
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WITH_MGR, LPARAM("NIF w/ Manager"));
+
+         SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_SETCURSEL, WPARAM(Exporter::mExportType), 0);
+
+         CheckDlgButton(hWnd, IDC_CHK_TRANSFORMS2, Exporter::mExportTransforms);
+         SetDlgItemText(hWnd, IDC_ED_PRIORITY2, FormatText("%.1f", Exporter::mDefaultPriority));
+         //CheckDlgButton(hWnd, IDC_CHK_USE_TIME_TAGS, Exporter::mUseTimeTags);           
+
+         // Skin
+         CheckDlgButton(hWnd, IDC_CHK_SKIN, Exporter::mExportSkin);
+         CheckDlgButton(hWnd, IDC_CHK_SKINPART, Exporter::mMultiplePartitions);
+         SetDlgItemText(hWnd, IDC_ED_BONES_PART, FormatText("%d", Exporter::mBonesPerPartition));
+         SetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, FormatText("%d", Exporter::mBonesPerVertex));
+
+         CheckDlgButton(hWnd, IDC_CHK_ALLOW_ACCUM, Exporter::mAllowAccum);
+
+         TSTR tmp;
+         tmp.printf("%.4f", Exporter::mWeldThresh);
+         SetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp);
+
+         ConvertStaticToHyperlink(hWnd, IDC_LBL_LINK);
+         ConvertStaticToHyperlink(hWnd, IDC_LBL_WIKI);
+
+         imp->mDlgResult = IDCANCEL;
+      }
+      return TRUE;
+
+   case WM_CLOSE:
+      EndDialog(hWnd, imp->mDlgResult);
+      return TRUE;
+
+   case WM_COMMAND:
+      if (HIWORD(wParam) == BN_CLICKED)
+      {
+         char tmp[MAX_PATH], *end;
+         bool close = false;
+         switch (LOWORD(wParam))
+         {
+         case IDOK:
+            // Validity Check
+            GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
+            if (tmp[0] != 0)
+            {
+               int nifVersion = ParseVersionString(tmp);
+               if (!IsSupportedVersion(nifVersion))
+               {
+                  MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP);
+                  return FALSE;
+               }
             }
 
-				CenterWindow(hWnd,GetParent(hWnd));
-				CheckDlgButton(hWnd, IDC_CHK_STRIPS, Exporter::mTriStrips);
-				CheckDlgButton(hWnd, IDC_CHK_HIDDEN, Exporter::mExportHidden);
-				CheckDlgButton(hWnd, IDC_CHK_FURN, Exporter::mExportFurn);
-				CheckDlgButton(hWnd, IDC_CHK_LIGHTS, Exporter::mExportLights);
-				CheckDlgButton(hWnd, IDC_CHK_VCOLORS, Exporter::mVertexColors);
-				SetDlgItemText(hWnd, IDC_ED_TEXPREFIX, Exporter::mTexPrefix.c_str());
-				CheckDlgButton(hWnd, IDC_CHK_COLL, Exporter::mExportCollision);
-            CheckDlgButton(hWnd, IDC_CHK_REMAP, Exporter::mRemapIndices);
-
-            CheckDlgButton(hWnd, IDC_CHK_EXTRA, Exporter::mExportExtraNodes);
-            CheckDlgButton(hWnd, IDC_CHK_UPB, Exporter::mUserPropBuffer);
-            CheckDlgButton(hWnd, IDC_CHK_HIER, Exporter::mFlattenHierarchy);
-            CheckDlgButton(hWnd, IDC_CHK_REM_BONES, Exporter::mRemoveUnreferencedBones);
-            CheckDlgButton(hWnd, IDC_CHK_SORTNODES, Exporter::mSortNodesToEnd);
-            CheckDlgButton(hWnd, IDC_CHK_SKEL_ONLY, Exporter::mSkeletonOnly);
-            CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras);
-            CheckDlgButton(hWnd, IDC_CHK_BONE_COLL, Exporter::mGenerateBoneCollision);            
-
-            string selection = Exporter::mGameName;
-            string version = Exporter::mNifVersion;
-            string userVer = FormatString("%d", Exporter::mNifUserVersion);
-            for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr)
-               SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_ADDSTRING, 0, LPARAM(itr->Name.c_str()));
-            SendDlgItemMessage(hWnd, IDC_CB_GAME, CB_SELECTSTRING, WPARAM(-1), LPARAM(selection.c_str()));
-            SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str()));
-            SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str()));
-            CheckDlgButton(hWnd, IDC_CHK_AUTO_DETECT, Exporter::mAutoDetect);
-
-            // Populate Type options
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_ANIM, LPARAM("NIF w/o Anim"));
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WO_KF, LPARAM("NIF with Anim"));
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WITH_NIF, LPARAM("Single KF with NIF"));
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::SINGLE_KF_WO_NIF, LPARAM("Single KF w/o NIF"));
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WITH_NIF, LPARAM("Multi KF with NIF"));
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::MULTI_KF_WO_NIF, LPARAM("Multi KF w/o NIF"));
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_ADDSTRING, Exporter::NIF_WITH_MGR, LPARAM("NIF w/ Manager"));
-            
-            SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_SETCURSEL, WPARAM(Exporter::mExportType), 0);
-
-            CheckDlgButton(hWnd, IDC_CHK_TRANSFORMS2, Exporter::mExportTransforms);
-            SetDlgItemText(hWnd, IDC_ED_PRIORITY2, FormatText("%.1f", Exporter::mDefaultPriority));
-            CheckDlgButton(hWnd, IDC_CHK_USE_TIME_TAGS, Exporter::mUseTimeTags);           
+            Exporter::mTriStrips = IsDlgButtonChecked(hWnd, IDC_CHK_STRIPS);
+            Exporter::mExportHidden = IsDlgButtonChecked(hWnd, IDC_CHK_HIDDEN);
+            Exporter::mExportFurn = IsDlgButtonChecked(hWnd, IDC_CHK_FURN);
+            Exporter::mExportLights = IsDlgButtonChecked(hWnd, IDC_CHK_LIGHTS);
+            Exporter::mVertexColors = IsDlgButtonChecked(hWnd, IDC_CHK_VCOLORS);
+            Exporter::mExportCollision = IsDlgButtonChecked(hWnd, IDC_CHK_COLL);
+            Exporter::mRemapIndices = IsDlgButtonChecked(hWnd, IDC_CHK_REMAP);
+
+            Exporter::mExportExtraNodes = IsDlgButtonChecked(hWnd, IDC_CHK_EXTRA);
+            Exporter::mUserPropBuffer = IsDlgButtonChecked(hWnd, IDC_CHK_UPB);
+            Exporter::mFlattenHierarchy = IsDlgButtonChecked(hWnd, IDC_CHK_HIER);
+            Exporter::mRemoveUnreferencedBones = IsDlgButtonChecked(hWnd, IDC_CHK_REM_BONES);
+            Exporter::mSortNodesToEnd = IsDlgButtonChecked(hWnd, IDC_CHK_SORTNODES);
+            Exporter::mSkeletonOnly = IsDlgButtonChecked(hWnd, IDC_CHK_SKEL_ONLY);
+            Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA);
+            Exporter::mGenerateBoneCollision = IsDlgButtonChecked(hWnd, IDC_CHK_BONE_COLL);
+
+            Exporter::mExportTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_TRANSFORMS2);
+            //Exporter::mUseTimeTags = IsDlgButtonChecked(hWnd, IDC_CHK_USE_TIME_TAGS);           
+
+            Exporter::mExportType = Exporter::ExportType(SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_GETCURSEL, 0, 0));
+            GetDlgItemText(hWnd, IDC_ED_PRIORITY2, tmp, MAX_PATH);
+            Exporter::mDefaultPriority = atof(tmp);
+
+            GetDlgItemText(hWnd, IDC_ED_TEXPREFIX, tmp, MAX_PATH);
+            Exporter::mTexPrefix = tmp;
+
+            GetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp, MAX_PATH);
+            Exporter::mWeldThresh = atof(tmp);
+
+            Exporter::mAllowAccum = IsDlgButtonChecked(hWnd, IDC_CHK_ALLOW_ACCUM);
 
             // Skin
-            CheckDlgButton(hWnd, IDC_CHK_SKIN, Exporter::mExportSkin);
-            CheckDlgButton(hWnd, IDC_CHK_SKINPART, Exporter::mMultiplePartitions);
-            SetDlgItemText(hWnd, IDC_ED_BONES_PART, FormatText("%d", Exporter::mBonesPerPartition));
-            SetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, FormatText("%d", Exporter::mBonesPerVertex));
-
-            CheckDlgButton(hWnd, IDC_CHK_ALLOW_ACCUM, Exporter::mAllowAccum);
-
-				TSTR tmp;
-				tmp.printf("%.4f", Exporter::mWeldThresh);
-				SetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp);
-
-				imp->mDlgResult = IDCANCEL;
-			}
-			return TRUE;
-
-		case WM_CLOSE:
-			EndDialog(hWnd, imp->mDlgResult);
-			return TRUE;
-
-		case WM_COMMAND:
-			if (HIWORD(wParam) == BN_CLICKED)
-			{
-				char tmp[MAX_PATH], *end;
-				bool close = false;
-				switch (LOWORD(wParam))
-				{
-					case IDOK:
-                  // Validity Check
-                  GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
-                  if (tmp[0] != 0)
-                  {
-                     int nifVersion = GetVersion(tmp);
-                     if (!IsVersionSupported(nifVersion))
-                     {
-                        MessageBox(hWnd, FormatString("Version '%s' is not a supported version.", tmp).c_str(), "NifExport", MB_OK|MB_ICONSTOP);
-                        return FALSE;
-                     }
-                  }
-
-						Exporter::mTriStrips = IsDlgButtonChecked(hWnd, IDC_CHK_STRIPS);
-						Exporter::mExportHidden = IsDlgButtonChecked(hWnd, IDC_CHK_HIDDEN);
-						Exporter::mExportFurn = IsDlgButtonChecked(hWnd, IDC_CHK_FURN);
-						Exporter::mExportLights = IsDlgButtonChecked(hWnd, IDC_CHK_LIGHTS);
-						Exporter::mVertexColors = IsDlgButtonChecked(hWnd, IDC_CHK_VCOLORS);
-						Exporter::mExportCollision = IsDlgButtonChecked(hWnd, IDC_CHK_COLL);
-						Exporter::mRemapIndices = IsDlgButtonChecked(hWnd, IDC_CHK_REMAP);
-
-                  Exporter::mExportExtraNodes = IsDlgButtonChecked(hWnd, IDC_CHK_EXTRA);
-                  Exporter::mUserPropBuffer = IsDlgButtonChecked(hWnd, IDC_CHK_UPB);
-                  Exporter::mFlattenHierarchy = IsDlgButtonChecked(hWnd, IDC_CHK_HIER);
-                  Exporter::mRemoveUnreferencedBones = IsDlgButtonChecked(hWnd, IDC_CHK_REM_BONES);
-                  Exporter::mSortNodesToEnd = IsDlgButtonChecked(hWnd, IDC_CHK_SORTNODES);
-                  Exporter::mSkeletonOnly = IsDlgButtonChecked(hWnd, IDC_CHK_SKEL_ONLY);
-                  Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA);
-                  Exporter::mGenerateBoneCollision = IsDlgButtonChecked(hWnd, IDC_CHK_BONE_COLL);
-
-                  Exporter::mExportTransforms = IsDlgButtonChecked(hWnd, IDC_CHK_TRANSFORMS2);
-                  Exporter::mUseTimeTags = IsDlgButtonChecked(hWnd, IDC_CHK_USE_TIME_TAGS);           
-
-                  Exporter::mExportType = Exporter::ExportType(SendDlgItemMessage(hWnd, IDC_CBO_ANIM_TYPE, CB_GETCURSEL, 0, 0));
-                  GetDlgItemText(hWnd, IDC_ED_PRIORITY2, tmp, MAX_PATH);
-                  Exporter::mDefaultPriority = atof(tmp);
-
-						GetDlgItemText(hWnd, IDC_ED_TEXPREFIX, tmp, MAX_PATH);
-						Exporter::mTexPrefix = tmp;
-
-						GetDlgItemText(hWnd, IDC_ED_WELDTHRESH, tmp, MAX_PATH);
-						Exporter::mWeldThresh = atof(tmp);
-
-                  Exporter::mAllowAccum = IsDlgButtonChecked(hWnd, IDC_CHK_ALLOW_ACCUM);
-
-                  // Skin
-                  Exporter::mExportSkin = IsDlgButtonChecked(hWnd, IDC_CHK_SKIN);
-                  Exporter::mMultiplePartitions = IsDlgButtonChecked(hWnd, IDC_CHK_SKINPART);
-                  GetDlgItemText(hWnd, IDC_ED_BONES_PART, tmp, MAX_PATH);
-                  Exporter::mBonesPerPartition = atoi(tmp);
-                  GetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, tmp, MAX_PATH);
-                  Exporter::mBonesPerVertex = atoi(tmp);
-
-                  GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
-                  if (AppSettings *appSettings = FindAppSetting(tmp))
-                  {
-                     Exporter::mGameName = appSettings->Name;
-                     GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
-                     Exporter::mNifVersion = tmp;
-                     GetDlgItemText(hWnd, IDC_CB_USER_VERSION, tmp, MAX_PATH);
-                     Exporter::mNifUserVersion = strtol(tmp, &end, 0);
-                  }
-                  Exporter::mAutoDetect = IsDlgButtonChecked(hWnd, IDC_CHK_AUTO_DETECT);
-
-						EndDialog(hWnd, imp->mDlgResult=IDOK);
-						close = true;
-						break;
-
-					case IDCANCEL:
-						EndDialog(hWnd, imp->mDlgResult=IDCANCEL);
-						close = true;
-						break;
-				}
-
-				if (close)
-					SendMessage(hWnd, WM_CLOSE, 0, 0);
-			}
-         else if (HIWORD(wParam) == STN_CLICKED)
-			{
-				if (LOWORD(wParam) == IDC_LBL_LINK)
-				{
-					ShellExecute(hWnd, "open", "http://www.niftools.org",
-						         NULL, NULL, SW_SHOWDEFAULT);
-				}
-			}
-         else if (HIWORD(wParam) == CBN_SELCHANGE)
+            Exporter::mExportSkin = IsDlgButtonChecked(hWnd, IDC_CHK_SKIN);
+            Exporter::mMultiplePartitions = IsDlgButtonChecked(hWnd, IDC_CHK_SKINPART);
+            GetDlgItemText(hWnd, IDC_ED_BONES_PART, tmp, MAX_PATH);
+            Exporter::mBonesPerPartition = atoi(tmp);
+            GetDlgItemText(hWnd, IDC_ED_BONES_VERTEX, tmp, MAX_PATH);
+            Exporter::mBonesPerVertex = atoi(tmp);
+
+            GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
+            if (AppSettings *appSettings = FindAppSetting(tmp))
+            {
+               Exporter::mGameName = appSettings->Name;
+               GetDlgItemText(hWnd, IDC_CB_VERSION, tmp, MAX_PATH);
+               Exporter::mNifVersion = tmp;
+               GetDlgItemText(hWnd, IDC_CB_USER_VERSION, tmp, MAX_PATH);
+               Exporter::mNifUserVersion = strtol(tmp, &end, 0);
+            }
+            Exporter::mAutoDetect = IsDlgButtonChecked(hWnd, IDC_CHK_AUTO_DETECT);
+
+            EndDialog(hWnd, imp->mDlgResult=IDOK);
+            close = true;
+            break;
+
+         case IDCANCEL:
+            EndDialog(hWnd, imp->mDlgResult=IDCANCEL);
+            close = true;
+            break;
+
+         case IDC_LBL_LINK:
+            ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL);
+            break;
+
+         case IDC_LBL_WIKI:
+            ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL);
+            break;
+         }
+
+         if (close)
+            SendMessage(hWnd, WM_CLOSE, 0, 0);
+      }
+      else if (HIWORD(wParam) == CBN_SELCHANGE)
+      {
+         if (LOWORD(wParam) == IDC_CB_GAME)
          {
-            if (LOWORD(wParam) == IDC_CB_GAME)
+            char tmp[MAX_PATH];
+            GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
+            if (AppSettings *appSettings = FindAppSetting(tmp))
             {
-               char tmp[MAX_PATH];
-               GetDlgItemText(hWnd, IDC_CB_GAME, tmp, MAX_PATH);
-               if (AppSettings *appSettings = FindAppSetting(tmp))
-               {
-                  string version = appSettings->NiVersion;
-                  string userVer = FormatString("%d", appSettings->NiUserVersion);
-                  SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str()));
-                  SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str()));
-               }
+               string version = appSettings->NiVersion;
+               string userVer = FormatString("%d", appSettings->NiUserVersion);
+               SendDlgItemMessage(hWnd, IDC_CB_VERSION, WM_SETTEXT, 0, LPARAM(version.c_str()));
+               SendDlgItemMessage(hWnd, IDC_CB_USER_VERSION, WM_SETTEXT, 0, LPARAM(userVer.c_str()));
             }
          }
-			break;
-	}
+      }
+      break;
+   }
 	return FALSE;
 }
 
@@ -279,6 +284,9 @@ NifExport::NifExport()
    iniFileName = iniName;
    shortDescription = GetIniValue<TSTR>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName);
    fileVersion = GetFileVersion(NULL);
+
+   webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org", iniFileName);
+   wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max", iniFileName);
 }
 
 NifExport::~NifExport() 
@@ -346,194 +354,140 @@ BOOL NifExport::SupportsOptions(int ext, DWORD options)
 	return TRUE;
 }
 
+static DWORD WINAPI dummyProgress(LPVOID arg) {
+   return(0);
+}
 
 int	NifExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
 {
+   TSTR title = FormatText("Exporting '%s'...", PathFindFileName(name));
+   i->PushPrompt(title);
+   if (!suppressPrompts)
+      i->ProgressStart(title, TRUE, dummyProgress, NULL);
 	try
 	{
-      TCHAR path[MAX_PATH];
-      GetFullPathName(name, MAX_PATH, path, NULL);
-      PathRenameExtension(path, ".nif");
+      DoExportInternal(name, ei, i, suppressPrompts, options);
+   }
+   catch (Exporter::CancelExporterException&)
+   {
+      // Special user cancellation exception
+   }
+   catch (exception &e)
+   {
+      if (!suppressPrompts)
+         MessageBox(NULL, e.what(), "Export Error", MB_OK);
+   }
+   catch (...)
+   {
+      if (!suppressPrompts)
+         MessageBox(NULL, "Unknown error.", "Export Error", MB_OK);
+   }
+   i->PopPrompt();
+   if (!suppressPrompts)
+      i->ProgressEnd();
+   return true;
+}
+
+int NifExport::DoExportInternal(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options)
+{
+   TCHAR path[MAX_PATH];
+   GetFullPathName(name, MAX_PATH, path, NULL);
+   PathRenameExtension(path, ".nif");
+
+   Exporter::mSelectedOnly = (options&SCENE_EXPORT_SELECTED) != 0;
 
-      // read application settings
-      AppSettings::Initialize(i);
+   // read application settings
+   AppSettings::Initialize(i);
 
-      TCHAR iniName[MAX_PATH];
-      LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
-      PathCombine(iniName, pluginDir, "MaxNifTools.ini");
-      bool iniNameIsValid = (-1 != _taccess(iniName, 0));
-
-      // Set whether Config should use registry or not
-      Exporter::mUseRegistry = !iniNameIsValid || GetIniValue<bool>(NifExportSection, "UseRegistry", false, iniName);
-      // read config from registry
-      Exporter::readConfig(i);
-      // read config from root node
-      Exporter::readConfig(i->GetRootNode());
-
-      // locate the "default" app setting
-      AppSettings *appSettings = NULL;
-      if (iniNameIsValid)
-      {
-         string fname = path;
-         // Locate which application to use. If Auto, find first app where this file appears in the root path list
-         string curapp = GetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName);
-         if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
-            Exporter::mAutoDetect = true;
-            // Scan Root paths
-            for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
-               if ((*itr).IsFileInRootPaths(fname)) {
-                  appSettings = &(*itr);
-                  break;
-               }
-            }
-         } else {
-            Exporter::mAutoDetect = false;
-            appSettings = FindAppSetting(curapp);
-         }
-      }
-      if (appSettings == NULL && !TheAppSettings.empty())
-         appSettings = &TheAppSettings.front();
+   TCHAR iniName[MAX_PATH];
+   LPCTSTR pluginDir = i->GetDir(APP_PLUGCFG_DIR);
+   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+   bool iniNameIsValid = (-1 != _taccess(iniName, 0));
+
+   // Set whether Config should use registry or not
+   Exporter::mUseRegistry = !iniNameIsValid || GetIniValue<bool>(NifExportSection, "UseRegistry", false, iniName);
+   // read config from registry
+   Exporter::readConfig(i);
+   // read config from root node
+   Exporter::readConfig(i->GetRootNode());
+
+   // locate the "default" app setting
+   string fname = path;
+   AppSettings *appSettings = Exporter::importAppSettings(fname);
+
+   Exporter::mSuppressPrompts = suppressPrompts;
+   if(!suppressPrompts)
+   {
+      if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_NIF_PANEL), GetActiveWindow(), NifExportOptionsDlgProc, (LPARAM)this) != IDOK)
+         return true;
 
-      Exporter::mGameName = appSettings->Name;
-      Exporter::mNifVersion = appSettings->NiVersion;
-      Exporter::mNifUserVersion = appSettings->NiUserVersion;
+      // write config to registry
+      Exporter::writeConfig(i);
+      // write config to root node
+      Exporter::writeConfig(i->GetRootNode());
 
-      if(!suppressPrompts)
-      {
-         if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_NIF_PANEL), GetActiveWindow(), NifExportOptionsDlgProc, (LPARAM)this) != IDOK)
-            return true;
-
-         // write config to registry
-         Exporter::writeConfig(i);
-         // write config to root node
-         Exporter::writeConfig(i->GetRootNode());
-
-         // Update the current app version
-         appSettings = FindAppSetting(Exporter::mGameName);
-         if (appSettings == NULL && !TheAppSettings.empty())
-            appSettings = &TheAppSettings.front();
-
-         if (Exporter::mAutoDetect){
-            SetIniValue<string>(NifExportSection, "CurrentApp", "AUTO", iniName);
-         } else {
-            SetIniValue<string>(NifExportSection, "CurrentApp", appSettings->Name, iniName);
-         }
-
-         appSettings->NiVersion = Exporter::mNifVersion;
-         appSettings->NiUserVersion = Exporter::mNifUserVersion;
-         appSettings->WriteSettings(i);
-      }
-
-      int nifVersion = VER_20_0_0_5;
-      int nifUserVer = Exporter::mNifUserVersion;
+      appSettings = Exporter::exportAppSettings();
+      appSettings->WriteSettings(i);
+   }
 
-      if (!Exporter::mNifVersion.empty())
-      {
-         nifVersion = GetVersion(Exporter::mNifVersion);
-         if (!IsVersionSupported(nifVersion))
-            throw exception(FormatString("Version '%s' is not a supported version.").c_str());
-      }
-      Exporter::mNifVersionInt = nifVersion;
+   int nifVersion = VER_20_0_0_5;
+   int nifUserVer = Exporter::mNifUserVersion;
+   if (!Exporter::mNifVersion.empty())
+   {
+      nifVersion = ParseVersionString(Exporter::mNifVersion);
+      if (!IsSupportedVersion(nifVersion))
+         throw exception(FormatString("Version '%s' is not a supported version.").c_str());
+   }
+   Exporter::mNifVersionInt = nifVersion;
 
-      Exporter::ExportType exportType = Exporter::mExportType;
+   Exporter::ExportType exportType = Exporter::mExportType;
 
-      // Hack so MW exports cleaner. Basically write tree without NiControllerManager
-      if (  nifVersion <= VER_10_0_1_0 
-         && (Exporter::mExportType != Exporter::NIF_WO_ANIM && Exporter::mExportType != Exporter::NIF_WITH_MGR)
-         )
-      {
-         Exporter::mExportType = Exporter::NIF_WO_KF;
-      }
-      Niflib::NifInfo info(nifVersion, nifUserVer, nifUserVer);
-      info.creator = Exporter::mCreatorName;
-      info.exportInfo1 = "Niflib";
-      info.exportInfo2 = FormatText("Niftools Max Plugins %s", fileVersion.data());
+   // Hack so MW exports cleaner. Basically write tree without NiControllerManager
+   if (  nifVersion <= VER_10_0_1_0 
+      && (Exporter::mExportType != Exporter::NIF_WO_ANIM && Exporter::mExportType != Exporter::NIF_WITH_MGR)
+      )
+   {
+      Exporter::mExportType = Exporter::NIF_WO_KF;
+   }
+   Niflib::NifInfo info(nifVersion, nifUserVer, nifUserVer);
+   info.endian = Niflib::NifInfo::INFO_LITTLE_ENDIAN;
+   info.creator = Exporter::mCreatorName;
+   info.exportInfo1 = FormatText("Niftools 3ds Max Plugins %s", fileVersion.data());
 
-		Exporter::mSelectedOnly = (options&SCENE_EXPORT_SELECTED) != 0;
-		Exporter exp(i, appSettings);
-		
-		Ref<NiNode> root = DynamicCast<NiNode>(CreateBlock( "NiNode" ));
-		Exporter::Result result = exp.doExport(root, i->GetRootNode());
-
-		if (result!=Exporter::Ok)
-			throw exception("Unknown error.");
+	Exporter exp(i, appSettings);
+	
+	Ref<NiNode> root = new NiNode();
+	Exporter::Result result = exp.doExport(root, i->GetRootNode());
+
+	if (result!=Exporter::Ok)
+		throw exception("Unknown error.");
 
-      if (exportType == Exporter::NIF_WO_ANIM || exportType == Exporter::NIF_WITH_MGR)
-      {
-         WriteNifTree(path, NiObjectRef(root), info);
+   if (exportType == Exporter::NIF_WO_ANIM || exportType == Exporter::NIF_WITH_MGR)
+   {
+      WriteNifTree(path, NiObjectRef(root), info);
+   }
+   else 
+   {
+      Niflib::ExportOptions export_type = EXPORT_NIF;
+      switch (exportType) {
+         case Exporter::SINGLE_KF_WITH_NIF: export_type = EXPORT_NIF_KF;       break;
+         case Exporter::SINGLE_KF_WO_NIF:   export_type = EXPORT_KF;           break;
+         case Exporter::MULTI_KF_WITH_NIF:  export_type = EXPORT_NIF_KF_MULTI; break;
+         case Exporter::MULTI_KF_WO_NIF:    export_type = EXPORT_KF_MULTI;     break;
       }
-      else 
-      {
-         Niflib::ExportOptions export_type = EXPORT_NIF;
-         switch (exportType) {
-            case Exporter::SINGLE_KF_WITH_NIF: export_type = EXPORT_NIF_KF;       break;
-            case Exporter::SINGLE_KF_WO_NIF:   export_type = EXPORT_KF;           break;
-            case Exporter::MULTI_KF_WITH_NIF:  export_type = EXPORT_NIF_KF_MULTI; break;
-            case Exporter::MULTI_KF_WO_NIF:    export_type = EXPORT_KF_MULTI;     break;
-         }
-
-         Niflib::NifGame game = KF_MW;
-         if (nifVersion <= VER_4_0_0_2) {
-            game = KF_MW;
-         } else if (nifVersion <= VER_20_0_0_4) {
-            game = KF_DAOC;
-         } else {
-            game = KF_CIV4;
-         }
 
-         // 
-         //if (nifVersion <= VER_10_0_1_0) {
-
-         //   // Now search and locate newer timeframe controllers and convert to keyframecontrollers
-         //   list<NiObjectRef> mgrs = GetAllObjectsByType( root, NiControllerManager::TypeConst() );
-         //   for ( list<NiObjectRef>::iterator it = mgrs.begin(); it != mgrs.end(); ++it) {
-         //      if (NiControllerManagerRef mgr = DynamicCast<NiControllerManager>(*it)) {
-         //         NiObjectNETRef target = mgr->GetTarget();
-         //         target->RemoveController( StaticCast<NiTimeController>(mgr) );
-         //         vector<NiControllerSequenceRef> seqs = mgr->GetControllerSequences();
-         //         for (vector<NiControllerSequenceRef>::iterator itr = seqs.begin(); itr != seqs.end(); ++itr) {
-         //            NiControllerSequenceRef seq = (*itr);
-         //            MergeNifTrees(DynamicCast<NiNode>(target), seq, nifVersion, nifUserVer);
-         //         }
-         //      }
-         //   }
-         //}
-
-         WriteFileGroup(path, StaticCast<NiObject>(root), info, export_type, game);
-/*
-         for (NodeList::iterator itr = mAnimationRoots.begin(); itr != mAnimationRoots.end(); ++itr){
-            list<NiTimeControllerRef> ctlrs = (*itr)->GetControllers();
-            if ( NiControllerManagerRef mgr = SelectFirstObjectOfType<NiControllerManager>( ctlrs ) ) {
-               (*itr)->RemoveController( mgr );
-
-               vector<NiControllerSequenceRef> seqs = mgr->GetControllerSequences();
-               WriteNifTree()
-            }
-         }
-
-         if (mExportType == SINGLE_KF_WITH_NIF || mExportType == MULTI_KF_WITH_NIF)
-         {
-            GetFullPathName(name, MAX_PATH, path, NULL);
-            PathRenameExtension(path, ".nif");
-            WriteNifTree(path, NiObjectRef(root), nifVersion, nifUserVer);
-         }
-*/
+      Niflib::NifGame game = KF_MW;
+      if (nifVersion <= VER_4_0_0_2) {
+         game = KF_MW;
+      } else if (nifVersion <= VER_20_0_0_4) {
+         game = KF_DAOC;
+      } else {
+         game = KF_CIV4;
       }
-	}
 
-	catch (exception &e)
-	{
-		MessageBox(NULL, e.what(), "Export Error", MB_OK);
-		return true;
-	}
-
-	catch (...)
-	{
-		MessageBox(NULL, "Unknown error.", "Export Error", MB_OK);
-		return true;
-	}
-
-	return true;
+      WriteFileGroup(path, StaticCast<NiObject>(root), info, export_type, game);
+   }
+   return true;
 }
 
 
diff --git a/NifExport/NifExport.rc b/NifExport/NifExport.rc
index 949309e0b65749897b1daa471e9a457a2f168e90..ef925e830f8e1f8e5987acf8f799ec91504ae39d 100755
--- a/NifExport/NifExport.rc
+++ b/NifExport/NifExport.rc
@@ -104,7 +104,7 @@ BEGIN
     EDITTEXT        IDC_CB_USER_VERSION,167,89,30,12,ES_AUTOHSCROLL
     DEFPUSHBUTTON   "&Export",IDOK,5,109,34,14
     PUSHBUTTON      "&Cancel",IDCANCEL,45,109,33,14
-    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,103,109,95,14,SS_NOTIFY | SS_CENTERIMAGE
+    CONTROL         "http://www.niftools.org",IDC_LBL_LINK,"Button",BS_OWNERDRAW | WS_TABSTOP,103,109,95,14
 END
 
 IDD_NIF_PANEL DIALOGEX 0, 0, 229, 291
@@ -138,28 +138,28 @@ BEGIN
     CONTROL         "&Vertex Colors",IDC_CHK_VCOLORS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,181,67,10
     GROUPBOX        "Skin Modifier",IDC_STATIC,116,123,108,73
     CONTROL         "Export Skin Modifier",IDC_CHK_SKIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,119,134,93,10
-    LTEXT           "Bones Per Vertex:",IDC_LBL_BONES_VERTEX,120,174,63,8
-    EDITTEXT        IDC_ED_BONES_VERTEX,195,173,24,12,ES_AUTOHSCROLL
+    CONTROL         "Enable Multiple Partitions",IDC_CHK_SKINPART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,119,147,93,10
     LTEXT           "Bones Per Partition:",IDC_LBL_BONES_PART,130,160,62,8
     EDITTEXT        IDC_ED_BONES_PART,195,158,24,12,ES_AUTOHSCROLL
-    CONTROL         "Gen. Bone Collision",IDC_CHK_BONE_COLL,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,151,7,73,10
-    CONTROL         "&Remap Indices",IDC_CHK_REMAP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,152,7,72,10
+    LTEXT           "Bones Per Vertex:",IDC_LBL_BONES_VERTEX,120,174,63,8
+    EDITTEXT        IDC_ED_BONES_VERTEX,195,173,24,12,ES_AUTOHSCROLL
     GROUPBOX        "Animation",IDC_GRP_ANIMATION,4,196,108,68
     COMBOBOX        IDC_CBO_ANIM_TYPE,7,207,85,69,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
     CONTROL         "Transforms",IDC_CHK_TRANSFORMS2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,223,67,10
-    LTEXT           "Priority:",IDC_LBL_PRIORITY2,9,249,23,8
-    EDITTEXT        IDC_ED_PRIORITY2,37,248,55,12,ES_AUTOHSCROLL
+    LTEXT           "Priority:",IDC_LBL_PRIORITY2,9,236,23,8
+    EDITTEXT        IDC_ED_PRIORITY2,37,235,55,12,ES_AUTOHSCROLL
     GROUPBOX        "Miscellaneous:",IDC_STATIC,115,196,109,68
     CONTROL         "Add User Prop Buffer",IDC_CHK_UPB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,209,82,11
     CONTROL         "Sort Nodes",IDC_CHK_SORTNODES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,222,82,10
+    CONTROL         "Add Accum Nodes",IDC_CHK_ALLOW_ACCUM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,235,82,10
     LTEXT           "Auto-&Weld",IDC_LBL_WELDTHRESH,125,249,47,8,NOT WS_VISIBLE | WS_DISABLED
     EDITTEXT        IDC_ED_WELDTHRESH,179,247,43,12,ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED
     DEFPUSHBUTTON   "&Export",IDOK,5,270,34,14
     PUSHBUTTON      "&Cancel",IDCANCEL,45,270,33,14
-    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,148,270,76,14,SS_NOTIFY | SS_CENTERIMAGE
-    CONTROL         "Enable Multiple Partitions",IDC_CHK_SKINPART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,119,147,93,10
-    CONTROL         "Use Time Tags",IDC_CHK_USE_TIME_TAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,235,96,10
-    CONTROL         "Add Accum Nodes",IDC_CHK_ALLOW_ACCUM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,235,82,10
+    CONTROL         "Gen. Bone Collision",IDC_CHK_BONE_COLL,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,151,7,73,10
+    CONTROL         "&Remap Indices",IDC_CHK_REMAP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,152,7,72,10
+    LTEXT           "www.niftools.org",IDC_LBL_LINK,137,270,60,14,SS_NOTIFY | SS_CENTERIMAGE
+    LTEXT           "Wiki",IDC_LBL_WIKI,203,270,22,14,SS_NOTIFY | SS_CENTERIMAGE
 END
 
 
diff --git a/NifExport/Strips.cpp b/NifExport/Strips.cpp
index a69a666bd87683b735c34910a294edeed6b68467..019af369d72126e0754998f1a18d510a0b4a4785 100755
--- a/NifExport/Strips.cpp
+++ b/NifExport/Strips.cpp
@@ -149,7 +149,7 @@ void Exporter::strippify(FaceGroup &grp)
 
 NiTriStripsDataRef Exporter::makeTriStripsData(const TriStrips &strips)
 {
-	NiTriStripsDataRef stripData = DynamicCast<NiTriStripsData>(CreateBlock("NiTriStripsData"));
+	NiTriStripsDataRef stripData = new NiTriStripsData();
 
 	if (strips.size() > 0)
 	{
diff --git a/NifExport/Util.cpp b/NifExport/Util.cpp
index 6dd15345265f84ad5105f6ee20980af7a677330f..446731c8a631a8b51eb41aaaecffe1204a0af73f 100755
--- a/NifExport/Util.cpp
+++ b/NifExport/Util.cpp
@@ -431,3 +431,42 @@ void Exporter::getChildNodes(INode *node, vector<NiNodeRef>& list)
       getChildNodes(child, list);
    }
 }
+
+
+// Special case of a single branch being exported
+
+bool Exporter::exportPrn(NiNodeRef &obj, INode *node) {
+   // Export Prn Text strings for any parent bones if parent is root
+   if (mSupportPrnStrings) {
+      if (INode *parentNode = node->GetParentNode()){
+         string parentName = parentNode->GetName();
+         NiStringExtraDataRef strings = new NiStringExtraData();	
+         strings->SetName("Prn");
+         strings->SetData(parentName);
+         obj->AddExtraData(DynamicCast<NiExtraData>(strings));
+         return true;
+      }
+   }
+   return false;
+}
+
+
+int Exporter::countNodes(INode *node)
+{
+   int counter = 1;
+   for (int i=0; i<node->NumberOfChildren(); i++) {
+      counter += countNodes(node->GetChildNode(i));
+   }
+   return counter;
+}
+
+bool Exporter::isSkeletonRoot(INode *node)
+{
+   if (wildmatch("Bip??", node->GetName())) {
+      return true;
+   } else if ( node->GetParentNode() == mI->GetRootNode() ) {
+      return true;
+   }
+
+   return false;
+}
\ No newline at end of file
diff --git a/NifExport/resource.h b/NifExport/resource.h
index 2a1de2d1cf475590364ec678c0d5fa569637a360..dc059a1b5e514ac6d6fba9bc297b89b93a3c4ff5 100755
--- a/NifExport/resource.h
+++ b/NifExport/resource.h
@@ -62,6 +62,7 @@
 #define IDC_COLOR                       13456
 #define IDC_EDIT                        13490
 #define IDC_SPIN                        13496
+#define IDC_LBL_WIKI                    13497
 
 // Next default values for new objects
 // 
diff --git a/NifImport/BaseImporter.h b/NifImport/BaseImporter.h
index d6c3b7d287cedcac8d02690b432876299a845c18..7b8180af1782d1598019a86866395116854559e3 100644
--- a/NifImport/BaseImporter.h
+++ b/NifImport/BaseImporter.h
@@ -30,6 +30,8 @@ public:
    bool iniFileValid;
    string iniFileName;
    AppSettings *appSettings;
+   TSTR webSite;
+   TSTR wikiSite;
 
    Niflib::NiObjectRef root;
 
@@ -60,6 +62,10 @@ public:
             *(FARPROC*)&Max7CreateNewBiped = GetProcAddress(hBiped, Max7CreateNewBipedName);
          }
       }
+
+      webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org");
+      wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max");
+
       // Load ini settings
       iniFileValid = false;
       LoadIniSettings();
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
index 1d8cfc5b8a1372cd0837a187157bd81291883d25..dc81a62d7f39a11eeeb59aed12b6937aba414688 100644
--- a/NifImport/ImportSkeleton.cpp
+++ b/NifImport/ImportSkeleton.cpp
@@ -579,7 +579,26 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse)
 
       // Do all node manipulations here
       NiNodeRef parent = node->GetParent();
+      string parentname = (parent ? parent->GetName() : "");
       Matrix44 m4 = node->GetWorldTransform();
+
+      // Check for Prn strings and change parent if necessary
+      if (supportPrnStrings) {
+         list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(node->GetExtraData());
+         for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr){
+            if (strmatch((*itr)->GetName(), "Prn")) {
+               parentname = (*itr)->GetData();
+               if (INode *pn = gi->GetINodeByName(parentname.c_str())){
+                  // Apparently Heads tend to need to be rotated 90 degrees on import for 
+                  if (!rotate90Degrees.empty() && wildmatch(rotate90Degrees, parentname)) {
+                     m4 *= TOMATRIX4(RotateYMatrix(TORAD(90)));
+                  }
+                  m4 *= TOMATRIX4(pn->GetObjTMAfterWSM(0, NULL));
+               }
+            }
+         }
+      }
+
       float len = node->GetLocalTranslation().Magnitude();
 
       // Remove NonAccum nodes and merge into primary bone
@@ -653,9 +672,8 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse)
          }
          if (bone)
          {
-            if (parent)
+            if (!parentname.empty())
             {
-               string parentname = parent->GetName();
                if (mergeNonAccum && wildmatch("* NonAccum", parentname)) {
                   parentname = parentname.substr(0, parentname.length() - 9);
                }
diff --git a/NifImport/KfDialog.cpp b/NifImport/KfDialog.cpp
index f127a74998f50f7d45c05171d2b15b4626eca975..87bcd42a9b7cf28dd1d965fe38460346fb878b21 100644
--- a/NifImport/KfDialog.cpp
+++ b/NifImport/KfDialog.cpp
@@ -15,7 +15,7 @@ HISTORY:
 #include "KFMImporter.h"
 #include "resource.h"
 #include "shellapi.h"
-
+#include "Hyperlinks.h"
 using namespace Niflib;
 
 static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
@@ -43,6 +43,10 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
             CheckDlgButton(hWnd, IDC_CHK_CLEARANIM, imp->clearAnimation);
             CheckDlgButton(hWnd, IDC_CHK_KEYNOTES, imp->addNoteTracks);
             CheckDlgButton(hWnd, IDC_CHK_TIMETAGS, imp->addTimeTags);
+
+            ConvertStaticToHyperlink(hWnd, IDC_LBL_LINK);
+            ConvertStaticToHyperlink(hWnd, IDC_LBL_WIKI);
+
          }
          return TRUE;
 
@@ -68,13 +72,14 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
                case IDCANCEL:
                   EndDialog(hWnd, dlgRes=IDCANCEL);
                   return TRUE;
-               }
-            }
-            else if (HIWORD(wParam) == STN_CLICKED)
-            {
-               if (LOWORD(wParam) == IDC_LBL_LINK)
-               {
-                  ShellExecute(hWnd, "open", "http://www.niftools.org", NULL, NULL, SW_SHOWDEFAULT);
+
+               case IDC_LBL_LINK:
+                  ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL);
+                  break;
+
+               case IDC_LBL_WIKI:
+                  ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL);
+                  break;
                }
             }
          }
diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp
index acd397005f135bb0bd5a350d78bd082cfcad9c5c..797ffe51e82e18813cb475e160d5391e4b3baddf 100644
--- a/NifImport/MaxNifImport.cpp
+++ b/NifImport/MaxNifImport.cpp
@@ -18,12 +18,14 @@ using namespace Niflib;
 
 #define MaxNifImport_CLASS_ID	Class_ID(0x794ac1c1, 0x8b4c64c7)
 
-string shortDescription;
 
 class MaxNifImport : public SceneImport {
 	public:
 
 		static HWND hParams;
+      TSTR shortDescription;
+      TSTR webSite;
+      TSTR wikiSite;
 		
 		int				ExtCount();					// Number of extensions supported
 		const TCHAR *	Ext(int n);					// Extension #n (i.e. "3DS")
@@ -82,7 +84,9 @@ MaxNifImport::MaxNifImport()
       PathAppend(iniName, "MaxNifTools.ini");
    }
    iniFileName = iniName;
-   shortDescription = GetIniValue<string>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str());
+   shortDescription = GetIniValue<TSTR>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str());
+   webSite = GetIniValue<TSTR>("System", "Website", "http://www.niftools.org", iniFileName.c_str());
+   wikiSite = GetIniValue<TSTR>("System", "Wiki", "http://www.niftools.org/wiki/index.php/3ds_Max", iniFileName.c_str());
 }
 
 MaxNifImport::~MaxNifImport() 
@@ -115,7 +119,7 @@ const TCHAR *MaxNifImport::LongDesc()
 const TCHAR *MaxNifImport::ShortDesc() 
 {			
 	//TODO: Return short ASCII description (i.e. "Targa")
-	return shortDescription.c_str();
+	return shortDescription;
 }
 
 const TCHAR *MaxNifImport::AuthorName()
diff --git a/NifImport/MaxNifImport.h b/NifImport/MaxNifImport.h
index 378671d247205beb6d8df45eab3c0b09909fc2c9..303e3d7e2b9ee2d5f52efa636a21c0c3959fe577 100644
--- a/NifImport/MaxNifImport.h
+++ b/NifImport/MaxNifImport.h
@@ -74,7 +74,7 @@
 #include "NifImporter.h"
 
 extern TCHAR *GetString(int id);
-extern string shortDescription;
+extern TSTR shortDescription;
 extern HINSTANCE hInstance;
 
 #endif
diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc
index 953153fc46d211c9be623ec59dd6c077c217b91f..3f134013ec6b8b0c3d930fe4e6fa33f5654117a7 100644
--- a/NifImport/MaxNifImport.rc
+++ b/NifImport/MaxNifImport.rc
@@ -69,8 +69,9 @@ BEGIN
     PUSHBUTTON      "...",IDC_BTN_BROWSE,184,215,16,13
     DEFPUSHBUTTON   "&Import",IDOK,5,236,34,14
     PUSHBUTTON      "&Cancel",IDCANCEL,45,236,33,14
-    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,122,236,78,14,SS_NOTIFY | SS_CENTERIMAGE
     CONTROL         "Ignore Root Node",IDC_CHK_IGNORE_ROOT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,109,161,89,10
+    LTEXT           "www.niftools.org",IDC_LBL_LINK,125,239,53,8,SS_NOTIFY | SS_CENTERIMAGE
+    LTEXT           "Wiki",IDC_LBL_WIKI,185,239,16,8,SS_NOTIFY | SS_CENTERIMAGE
 END
 
 IDD_KF_PANEL DIALOGEX 0, 0, 118, 99
@@ -83,9 +84,10 @@ BEGIN
     CONTROL         "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,72,10
     CONTROL         "Add Key Notes",IDC_CHK_KEYNOTES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,31,72,10
     CONTROL         "Add Time Tags",IDC_CHK_TIMETAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,44,72,10
-    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,7,61,95,14,SS_NOTIFY | SS_CENTERIMAGE
     DEFPUSHBUTTON   "&Import",IDOK,5,78,34,14
     PUSHBUTTON      "&Cancel",IDCANCEL,45,78,33,14
+    LTEXT           "www.niftools.org",IDC_LBL_LINK,7,62,54,8,SS_NOTIFY | SS_CENTERIMAGE
+    LTEXT           "Wiki",IDC_LBL_WIKI,90,62,16,9,SS_NOTIFY | SS_CENTERIMAGE
 END
 
 
@@ -165,3 +167,14 @@ END
 /////////////////////////////////////////////////////////////////////////////
 
 
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp
index 36477856fa53ea28598e1cc09af3fbc37fe8eced..a49134caec06d393830119f1854f86b8644b5189 100644
--- a/NifImport/NIFImport.cpp
+++ b/NifImport/NIFImport.cpp
@@ -119,10 +119,16 @@ void NifImporter::LoadIniSettings()
    if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
       autoDetect = true;
       // Scan Root paths
+      bool versionmatch = false;
+      int version = GetNifVersion(this->name);
       for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
          if ((*itr).IsFileInRootPaths(this->name)) {
             appSettings = &(*itr);
             break;
+         } else if ( !versionmatch && ParseVersionString((*itr).NiVersion) == version ) {
+            // Version matching is an ok fit but we want the other if possible. And we want the first match if possible.
+            appSettings = &(*itr);
+            versionmatch = true;
          }
       }
    } else {
@@ -179,6 +185,8 @@ void NifImporter::LoadIniSettings()
    addNoteTracks = GetIniValue(AnimImportSection, "AddNoteTracks", true);
    addTimeTags = GetIniValue(AnimImportSection, "AddTimeTags", true);
 
+   rotate90Degrees = TokenizeString(GetIniValue<string>(NifImportSection, "Rotate90Degrees", "").c_str(), ";");
+
    // Collision
    bhkScaleFactor = GetIniValue<float>(CollisionSection, "bhkScaleFactor", 7.0f);
    ApplyAppSettings();
@@ -195,6 +203,9 @@ void NifImporter::ApplyAppSettings()
          dummyNodeMatches = appSettings->dummyNodeMatches;
       if (appSettings->applyOverallTransformToSkinAndBones != -1)
          applyOverallTransformToSkinAndBones = appSettings->applyOverallTransformToSkinAndBones ? true : false;
+      if (!appSettings->rotate90Degrees.empty())
+         rotate90Degrees = appSettings->rotate90Degrees;
+      supportPrnStrings = appSettings->supportPrnStrings;
    }
 }
 
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
index dbc191cb1cdb4774a7d4db7e66e0fb491b246c49..97718109b35b872dc9a439b0b397b15864f07711 100644
--- a/NifImport/NIFImporter.h
+++ b/NifImport/NIFImporter.h
@@ -68,6 +68,8 @@ public:
    bool uncontrolledDummies;
    bool ignoreRootNode;
    bool autoDetect;
+   stringlist rotate90Degrees;
+   bool supportPrnStrings;
 
    // Animation related Settings
    bool replaceTCBRotationWithBezier;
diff --git a/NifImport/NifDialog.cpp b/NifImport/NifDialog.cpp
index 4940baaa28e60eb465e39a11ed18e80b8d151227..2016256362afff9cf537e743d42eb84271812bbf 100644
--- a/NifImport/NifDialog.cpp
+++ b/NifImport/NifDialog.cpp
@@ -14,7 +14,7 @@ HISTORY:
 #include "MaxNifImport.h"
 #include "resource.h"
 #include "shellapi.h"
-
+#include "Hyperlinks.h"
 using namespace Niflib;
 
 static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
@@ -78,6 +78,8 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
                EnableWindow(GetDlgItem(hWnd, IDC_CHK_BIPED), FALSE);
                EnableWindow(GetDlgItem(hWnd, IDC_CHK_REM_BONES), FALSE);
             }
+            ConvertStaticToHyperlink(hWnd, IDC_LBL_LINK);
+            ConvertStaticToHyperlink(hWnd, IDC_LBL_WIKI);
          }
          return TRUE;
 
@@ -137,7 +139,7 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
                case IDC_BTN_BROWSE:
                   {
                      TCHAR filter[64], *pfilter=filter;
-                     pfilter = _tcscpy(filter, shortDescription.c_str());
+                     pfilter = _tcscpy(filter, shortDescription);
                      pfilter = _tcscat(pfilter, " (*.NIF)");
                      pfilter += strlen(pfilter);
                      *pfilter++ = '\0';
@@ -164,13 +166,14 @@ static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wP
                      }
                   }
                   break;
-               }
-            }
-            else if (HIWORD(wParam) == STN_CLICKED)
-            {
-               if (LOWORD(wParam) == IDC_LBL_LINK)
-               {
-                  ShellExecute(hWnd, "open", "http://www.niftools.org", NULL, NULL, SW_SHOWDEFAULT);
+
+               case IDC_LBL_LINK:
+                  ShellExecute(hWnd, "open", imp->webSite, NULL, NULL, SW_SHOWNORMAL);
+                  break;
+
+               case IDC_LBL_WIKI:
+                  ShellExecute(hWnd, "open", imp->wikiSite, NULL, NULL, SW_SHOWNORMAL);
+                  break;
                }
             }
             else if (HIWORD(wParam) == CBN_SELCHANGE)
diff --git a/NifImport/resource.h b/NifImport/resource.h
index 3462e80a54feb062d55c534a3311e0b2982f3c7e..8569df00dc6afcdb2861b0790e3244c23d7e2bba 100644
--- a/NifImport/resource.h
+++ b/NifImport/resource.h
@@ -40,6 +40,7 @@
 #define IDC_CHK_KEYNOTES                10024
 #define IDC_CHK_CAMERA                  10025
 #define IDC_CHK_TIMETAGS                10026
+#define IDC_LBL_WIKI                    10027
 
 // Next default values for new objects
 // 
diff --git a/NifPlugins_VC80.sln b/NifPlugins_VC80.sln
index 28cfd323c99f730f00c03f16b6084a0676c3c72d..e9cb84f106f0152d8c439afe74fbfd1cdbae54bb 100644
--- a/NifPlugins_VC80.sln
+++ b/NifPlugins_VC80.sln
@@ -38,8 +38,8 @@ Global
 		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 6|Win32.Build.0 = Release - PCH|Win32
 		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 7|Win32.ActiveCfg = Release - PCH|Win32
 		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 7|Win32.Build.0 = Release - PCH|Win32
-		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.ActiveCfg = Release|Win32
-		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.Build.0 = Release|Win32
+		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.ActiveCfg = Release - PCH|Win32
+		{19FD8EE6-79CC-4BAC-9744-D9573BE47C7E}.Release - Max 8|Win32.Build.0 = Release - PCH|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 5|Win32.ActiveCfg = Debug - Max 8|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 5|Win32.Build.0 = Debug - Max 8|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 6|Win32.ActiveCfg = Debug - Max 6|Win32
@@ -50,8 +50,8 @@ Global
 		{466F2D3E-2663-4583-A05C-128683677617}.Debug - Max 8|Win32.Build.0 = Debug - Max 8|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 6|Win32.ActiveCfg = Release - Max 6|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 6|Win32.Build.0 = Release - Max 6|Win32
-		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.ActiveCfg = Release - Max 8|Win32
-		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.Build.0 = Release - Max 8|Win32
+		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.ActiveCfg = Release - Max 7|Win32
+		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 7|Win32.Build.0 = Release - Max 7|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 8|Win32.ActiveCfg = Release - Max 8|Win32
 		{466F2D3E-2663-4583-A05C-128683677617}.Release - Max 8|Win32.Build.0 = Release - Max 8|Win32
 	EndGlobalSection