diff --git a/NifImport/BaseImporter.h b/NifImport/BaseImporter.h
new file mode 100644
index 0000000000000000000000000000000000000000..cec3fd50a9e6b098a266b6200344848bba3cb13b
--- /dev/null
+++ b/NifImport/BaseImporter.h
@@ -0,0 +1,87 @@
+/**********************************************************************
+*<
+FILE: BaseImporter.h
+
+DESCRIPTION:	Base Importer class
+
+CREATED BY: tazpn (Theo)
+
+HISTORY:
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+
+#ifndef __BASEIMPORTER_H__
+#define __BASEIMPORTER_H__
+
+extern IBipMaster * (_cdecl * Max8CreateNewBiped)(float,float,class Point3 const &,int,int,int,int,int,int,int,int,int,int,int,int,float,int,int,int,int,int,int,int,int);
+
+// Importer Base
+class BaseImporter
+{
+public:
+   string name;
+   string path;
+   ImpInterface *i;
+   Interface *gi;
+   BOOL suppressPrompts;
+   bool iniFileValid;
+   string iniFileName;
+   AppSettings *appSettings;
+
+   Niflib::NiObjectRef root;
+
+   BaseImporter(){}
+
+   void BaseInit(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
+   {
+      name = Name;
+      i=I;
+      gi=GI;
+      suppressPrompts = SuppressPrompts;
+      
+      char buffer[MAX_PATH] = {0}, *p = NULL;
+      GetFullPathName(Name, _countof(buffer), buffer, &p);
+      if (p) *p = 0;
+      path = buffer;
+      iniFileValid = false;
+
+      LPCTSTR Max8CreateNewBipedName = TEXT("?CreateNewBiped@@YAPAVIBipMaster@@MMABVPoint3@@HHHHHHHHHHHHMHHHHHHHH@Z");
+      HMODULE hBiped = GetModuleHandle("biped.dlc");
+      if (NULL != hBiped && 0 == Max8CreateNewBiped)
+         *(FARPROC*)&Max8CreateNewBiped = GetProcAddress(hBiped, Max8CreateNewBipedName);
+
+      // Load ini settings
+      iniFileValid = false;
+      LoadIniSettings();
+
+      ReadBlocks();
+
+      Initialize();
+   }
+
+   virtual void LoadIniSettings() = 0;
+   virtual void ReadBlocks() = 0;
+   virtual void Initialize() = 0;
+
+   virtual bool isValid() const { return (NULL != root); }
+
+   // Generic IniFile reading routine
+   template<typename T>
+   T GetIniValue(LPCTSTR Section, LPCTSTR Setting, T Default){
+      if (!iniFileValid) 
+         return Default;
+      return ::GetIniValue<T>(Section, Setting, Default, iniFileName.c_str());
+   }
+   // Generic IniFile reading routine
+   template<typename T>
+   void SetIniValue(LPCTSTR Section, LPCTSTR Setting, T value){
+      if (!iniFileValid) 
+         return;
+      ::SetIniValue<T>(Section, Setting, value, iniFileName.c_str());
+   }
+};
+
+extern void GoToSkeletonBindPosition(std::vector<Niflib::NiNodeRef>& blocks);
+
+#endif
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..350578c287ba202fead4dc9387bb25615773b078
--- /dev/null
+++ b/NifImport/ImportAnimation.cpp
@@ -0,0 +1,562 @@
+/**********************************************************************
+*<
+FILE: ImportAnimation.cpp
+
+DESCRIPTION:	Animation Import Routines
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#include "stdafx.h"
+#include "MaxNifImport.h"
+#include "NIFImporter.h"
+#include "KFMImporter.h"
+#include <obj/NiInterpolator.h>
+#include <obj/NiTransformInterpolator.h>
+#include <obj/NiTransformData.h>
+#include <obj/NiTimeController.h>
+#include <obj/NiTransformController.h>
+#include <obj/NiTextKeyExtraData.h>
+#include <obj/NiKeyframeController.h>
+#include <obj/NiKeyframeData.h>
+#include <obj/NiStringPalette.h>
+
+using namespace Niflib;
+
+const Class_ID IPOS_CONTROL_CLASS_ID = Class_ID(0x118f7e02,0xffee238a);
+enum {
+   IPOS_X_REF	=	0,
+   IPOS_Y_REF	=	1,
+   IPOS_Z_REF	=	2,
+   IPOS_W_REF	=	3,
+};
+
+const float FramesPerSecond = 30.0f;
+const int TicksPerFrame = GetTicksPerFrame();
+
+inline TimeValue TimeToFrame(float t) {
+   return TimeValue(t * FramesPerSecond * TicksPerFrame);
+}
+
+typedef Key<float> FloatKey;
+typedef Key<Quaternion> QuatKey;
+typedef Key<Vector3> Vector3Key;
+
+
+template<typename T, typename U>
+inline T MapKey(U& key, float time);
+
+
+template <typename T, typename U>
+inline T& InitLinKey(T& rKey, U& key, float time)
+{
+   rKey.time = TimeToFrame(time + key.time);
+   rKey.flags = 0;
+   return rKey;
+}
+template<>
+inline ILinFloatKey MapKey<ILinFloatKey,FloatKey>(FloatKey& key, float time)
+{
+   ILinFloatKey rKey;
+   rKey.val = key.data;
+   return InitLinKey(rKey, key, time);
+}
+
+template<>
+inline ILinRotKey MapKey<ILinRotKey, QuatKey>(QuatKey& key, float time)
+{
+   ILinRotKey rKey;
+   rKey.val = TOQUAT(key.data);
+   return InitLinKey(rKey, key, time);
+}
+
+template<>
+inline ILinScaleKey MapKey<ILinScaleKey, FloatKey>(FloatKey& key, float time)
+{
+   ILinScaleKey rKey;
+   rKey.val.s.Set(key.data, key.data, key.data);
+   rKey.val.q.Identity();
+   return InitLinKey(rKey, key, time);
+}
+
+template<>
+inline ILinPoint3Key MapKey<ILinPoint3Key, Vector3Key>(Vector3Key& key, float time)
+{
+   ILinPoint3Key rKey;
+   rKey.val = TOPOINT3(key.data);
+   return InitLinKey(rKey, key, time);
+}
+
+
+template <typename T, typename U>
+inline T& InitBezKey(T& rKey, U& key, float time)
+{
+   rKey.time = TimeToFrame(time + key.time);
+   rKey.flags = 0;
+   SetInTanType(rKey.flags,BEZKEY_FLAT);
+   SetOutTanType(rKey.flags,BEZKEY_FLAT);
+   return rKey;
+}
+template<>
+inline IBezFloatKey MapKey<IBezFloatKey, FloatKey>(FloatKey& key, float time)
+{
+   IBezFloatKey rKey;
+   rKey.val = key.data;
+   rKey.intan = key.forward_tangent;
+   rKey.outtan = key.backward_tangent;
+   return InitBezKey(rKey, key, time);
+}
+
+template<>
+inline IBezPoint3Key MapKey<IBezPoint3Key, Vector3Key>(Vector3Key& key, float time)
+{
+   IBezPoint3Key rKey;
+   rKey.val = TOPOINT3(key.data);
+   rKey.intan = TOPOINT3(key.forward_tangent);
+   rKey.outtan = TOPOINT3(key.backward_tangent);
+   return InitBezKey(rKey, key, time);
+}
+
+
+template<>
+inline IBezQuatKey MapKey<IBezQuatKey, QuatKey>(QuatKey& key, float time)
+{
+   IBezQuatKey rKey;
+   rKey.val = TOQUAT(key.data);
+   return InitBezKey(rKey, key, time);
+}
+
+template<>
+inline IBezScaleKey MapKey<IBezScaleKey, FloatKey>(FloatKey& key, float time)
+{
+   IBezScaleKey rKey;
+   rKey.val.s.Set(key.data, key.data, key.data);
+   rKey.val.q.Identity();
+   return InitBezKey(rKey, key, time);
+}
+
+template <typename T, typename U>
+inline T& InitTCBKey(T& rKey, U& key, float time)
+{
+   rKey.time = TimeToFrame(time + key.time);
+   rKey.tens = key.tension;
+   rKey.cont = key.continuity;
+   rKey.bias = key.bias;
+   rKey.easeIn = 0.0f;
+   rKey.easeOut = 0.0f;
+   return rKey;
+}
+
+template<>
+inline ITCBFloatKey MapKey<ITCBFloatKey, FloatKey>(FloatKey& key, float time)
+{
+   ITCBFloatKey rKey;
+   rKey.val = key.data;
+   return InitTCBKey(rKey, key, time);
+}
+
+template<>
+inline ITCBRotKey MapKey<ITCBRotKey, QuatKey>(QuatKey& key, float time)
+{
+   ITCBRotKey rKey;
+   rKey.val = TOQUAT(key.data);
+   InitTCBKey(rKey, key, time);
+   rKey.flags = TCBKEY_QUATVALID;
+   return rKey;
+}
+
+template<>
+inline ITCBPoint3Key MapKey<ITCBPoint3Key, Vector3Key>(Vector3Key& key, float time)
+{
+   ITCBPoint3Key rKey;
+   rKey.val = TOPOINT3(key.data);
+   return InitTCBKey(rKey, key, time);
+}
+
+template<>
+inline ITCBScaleKey MapKey<ITCBScaleKey, FloatKey>(FloatKey& key, float time)
+{
+   ITCBScaleKey rKey;
+   rKey.val.s.Set(key.data, key.data, key.data);
+   rKey.val.q.Identity();
+   return InitTCBKey(rKey, key, time);
+}
+
+template<typename T, typename U>
+inline void SetKeys(Control *subCtrl, vector<U>& keys, float time)
+{
+   if (subCtrl && !keys.empty()){
+      if (IKeyControl *ikeys = GetKeyControlInterface(subCtrl)){
+         ikeys->SetNumKeys(keys.size());
+         for (int i=0,n=keys.size(); i<n; ++i) {
+            ikeys->SetKey(i, &MapKey<T>(keys[i], time));
+         }
+      }
+   }
+}
+
+enum V3T
+{
+   V3T_X, V3T_Y, V3T_Z
+};
+inline float GetValue(Vector3& value, V3T type)
+{
+   switch (type) {
+   case V3T_X: return value.x;
+   case V3T_Y: return value.y;
+   case V3T_Z: return value.z;
+   }
+   return 0.0f;
+}
+
+FloatKey& CopyKey( FloatKey& dst, Vector3Key& src, V3T type)
+{
+   dst.time = src.time;
+   dst.bias = src.bias;
+   dst.continuity = src.continuity;
+   dst.tension = src.tension;
+   dst.backward_tangent = GetValue(src.backward_tangent, type);
+   dst.forward_tangent = GetValue(src.forward_tangent, type);
+   dst.data = GetValue(src.data, type);
+   return dst;
+}
+
+void SplitKeys(vector<Vector3Key>&keys, vector<FloatKey>&xkeys, vector<FloatKey>&ykeys, vector<FloatKey>&zkeys)
+{
+   int n = keys.size();
+   xkeys.resize(n), ykeys.resize(n), zkeys.resize(n);
+   for (int i=0,n=keys.size(); i<n; ++i) {
+      Vector3Key& key = keys[i];
+      CopyKey(xkeys[i], key, V3T_X), CopyKey(ykeys[i], key, V3T_Y), CopyKey(zkeys[i], key, V3T_Z);
+   }
+}
+
+typedef Key<string> KeyTextValue;
+
+struct AnimationImport
+{
+   AnimationImport(NifImporter& parent) : ni(parent) {}
+
+   NifImporter &ni;
+
+   vector<KeyTextValue> BuildKeyValues(NiObjectNETRef nref);
+   bool AddValues(NiObjectNETRef nref);
+   bool AddValues(vector<NiObjectNETRef>& nodes);
+
+   bool AddValues(Control *c, NiKeyframeDataRef data, float time);
+
+   Control* MakePositionXYZ(Control *tmCont, Class_ID clsid);
+   Control* MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid);
+   Control* MakeScale(Control *tmCont, Class_ID clsid);
+
+   Control* GetTMController(const string& name);
+};
+
+bool NifImporter::ImportAnimation()
+{
+   if (!enableAnimations)
+      return false;
+
+   AnimationImport ai(*this);
+   return ai.AddValues(DynamicCast<NiObjectNET>(nodes[0]->GetChildren()));
+}
+
+bool KFMImporter::ImportAnimation()
+{
+   bool ok = false;
+   const float FramesIncrement = 1.0f/30.0f;
+   int curFrame = 0;
+   // Read Kf files
+#ifdef USE_UNSUPPORTED_CODE
+   AnimationImport ai(*this);
+
+   float time = 0.0f;
+   for(vector<NiControllerSequenceRef>::iterator itr = kf.begin(); itr != kf.end(); ++itr){
+
+      NiControllerSequenceRef cntr = (*itr);
+      vector<ControllerLink> links = cntr->GetControllerData();
+      for (vector<ControllerLink>::iterator lnk=links.begin(); lnk != links.end(); ++lnk){
+
+         string name = (*lnk).targetName;
+         if (name.empty()) {
+            NiStringPaletteRef strings = lnk->stringPalette;
+            name = strings->GetSubStr((*lnk).nodeNameOffset);
+         }
+         if (name.empty())
+            continue;
+
+         Control *c = ai.GetTMController(name);
+         if (NULL == c)
+            continue;
+
+         float start = cntr->GetStartTime();
+         float stop = cntr->GetStopTime();
+         if ((*lnk).interpolator){
+            if (NiTransformInterpolatorRef interp = (*lnk).interpolator) {
+               if (NiTransformDataRef data = interp->GetData()){
+                  ok |= ai.AddValues(c, data, time);
+                  time += (stop-start) + FramesIncrement; // round to nearest 10f ?
+               }
+            }
+         } else if ((*lnk).controller) {
+            if (NiTransformControllerRef tc = DynamicCast<NiTransformController>((*lnk).controller)) {
+               if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) {
+                  if (NiTransformDataRef data = interp->GetData()){
+                     ok |= ai.AddValues(c, data, time);
+                     time += (stop-start) + FramesIncrement; // round to nearest 10f ?
+                  }
+               }
+            } else if (NiKeyframeControllerRef kfc = DynamicCast<NiKeyframeController>((*lnk).controller)) {
+               if (NiKeyframeDataRef kfData = kfc->GetData()) {
+                  ok |= ai.AddValues(c, kfData, time);
+                  time += (stop-start) + FramesIncrement; // round to nearest 10f ?
+               }
+            }
+         }
+      }
+   }
+#endif
+   return ok;
+}
+
+Control *AnimationImport::GetTMController(const string& name)
+{
+   INode *n = ni.gi->GetINodeByName(name.c_str());
+   if (NULL == n)
+      return NULL;
+
+   Control *c = n->GetTMController();
+   if (NULL == c)
+      return NULL;
+
+   // ignore bipeds for now.
+   if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) 
+      ||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID) 
+      ||(c->ClassID() == FOOTPRINT_CLASS_ID))
+      return NULL;
+
+   return c;
+}
+
+vector<KeyTextValue> AnimationImport::BuildKeyValues(NiObjectNETRef nref)
+{
+   if (NiTextKeyExtraDataRef keydata = SelectFirstObjectOfType<NiTextKeyExtraData>(nref->GetExtraData()))
+      return keydata->GetKeys();
+   return vector<KeyTextValue>();
+}
+
+bool AnimationImport::AddValues(vector<NiObjectNETRef>& nodes)
+{
+   bool ok = false;
+   for (vector<NiObjectNETRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr)
+   {
+      ok |= AddValues(*itr);
+      NiNodeRef ninode = (*itr);
+      if (ninode)
+         ok |= AddValues(DynamicCast<NiObjectNET>(ninode->GetChildren()));
+   }
+   return ok;
+}
+
+bool AnimationImport::AddValues(NiObjectNETRef nref)
+{
+   Control *c = GetTMController(nref->GetName().c_str());
+   if (NULL == c)
+      return false;
+
+ /*  vector<KeyTextValue> tkeys = BuildKeyValues(nref);
+   if (tkeys.empty())
+      return false;*/
+
+   float time = 0.0f;
+   list< NiTimeControllerRef > clist = nref->GetControllers();
+   if (NiTransformControllerRef tc = SelectFirstObjectOfType<NiTransformController>(clist)) {
+      if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) {
+         if (NiTransformDataRef data = interp->GetData()) {
+            return AddValues(c, data, time);
+         }
+      }
+   } else if (NiKeyframeControllerRef kf = SelectFirstObjectOfType<NiKeyframeController>(clist)) {
+      if (NiKeyframeDataRef kfData = kf->GetData()) {
+         return AddValues(c, kfData, time);
+      }
+   }
+   return false;
+}
+
+bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time)
+{
+   // Handle Translation
+   switch (data->GetTranslateType())
+   {
+   case LINEAR_KEY:
+      if (Control *subCtrl = MakePositionXYZ(c, Class_ID(LININTERP_FLOAT_CLASS_ID,0))) {
+         vector<FloatKey> xkeys, ykeys, zkeys;
+         SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys);
+         SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time);
+         SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time);
+         SetKeys<ILinFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time);
+      }
+      break;
+
+   case QUADRATIC_KEY:
+   case XYZ_ROTATION_KEY:
+      if (Control *subCtrl = MakePositionXYZ(c, Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
+         vector<FloatKey> xkeys, ykeys, zkeys;
+         SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys);
+         SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time);
+         SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time);
+         SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time);
+      }
+      break;
+
+   case TBC_KEY:
+      if (Control *subCtrl = MakePositionXYZ(c, Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) {
+         vector<FloatKey> xkeys, ykeys, zkeys;
+         SplitKeys(data->GetTranslateKeys(), xkeys, ykeys, zkeys);
+         SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time);
+         SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time);
+         SetKeys<ITCBFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time);
+      }
+      break;
+   }
+
+   // Handle Rotation
+   switch (data->GetRotateType())
+   {
+   case LINEAR_KEY:
+      if (Control *subCtrl = MakeRotation(c, Class_ID(LININTERP_ROTATION_CLASS_ID,0), Class_ID(LININTERP_FLOAT_CLASS_ID,0))) {
+         SetKeys<ILinRotKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
+      }
+      break;
+
+   case QUADRATIC_KEY:
+      if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
+         SetKeys<IBezQuatKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
+      }
+      break;
+
+   case XYZ_ROTATION_KEY:
+      if (Control *subCtrl = MakeRotation(c, Class_ID(EULER_CONTROL_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
+         SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), data->GetXRotateKeys(), time);
+         SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), data->GetYRotateKeys(), time);
+         SetKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), data->GetZRotateKeys(), time);
+      }
+      break;
+
+   case TBC_KEY:
+      if (ni.replaceTCBRotationWithBezier) {
+         // TCB simply is not working for me.  Better off with Bezier as a workaround
+         if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) {
+            SetKeys<IBezQuatKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
+         }
+      } else {
+         if (Control *subCtrl = MakeRotation(c, Class_ID(TCBINTERP_ROTATION_CLASS_ID,0), Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) {
+            SetKeys<ITCBRotKey, QuatKey>(subCtrl, data->GetQuatRotateKeys(), time);
+         }
+      }
+      break;
+   }
+   // Handle Scale
+   switch (data->GetScaleType())
+   {
+   case LINEAR_KEY:
+      if (Control *subCtrl = MakeScale(c, Class_ID(LININTERP_SCALE_CLASS_ID,0))) {
+         SetKeys<ILinScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time);
+      }
+      break;
+   case QUADRATIC_KEY:
+   case XYZ_ROTATION_KEY:
+      if (Control *subCtrl = MakeScale(c, Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0))) {
+         SetKeys<IBezScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time);
+      }
+      break;
+   case TBC_KEY:
+      if (Control *subCtrl = MakeScale(c, Class_ID(TCBINTERP_SCALE_CLASS_ID,0))) {
+         SetKeys<ITCBScaleKey, FloatKey>(subCtrl, data->GetScaleKeys(), time);
+      }
+      break;
+   }
+   return true;
+}
+
+Control* AnimationImport::MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid)
+{
+   Interface *ip = ni.gi;
+   if (Control *c = tmCont->GetRotationController()) {
+      if (c->ClassID()!=rotClsid) {
+         if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_ROTATION_CLASS_ID, rotClsid)) {
+            if (!tmCont->SetRotationController(tmpCtrl))
+               tmpCtrl->DeleteThis();
+            else
+               c = tmpCtrl;
+         }
+      }
+      if (Control *r = tmCont->GetRollController()) {
+         if (r->ClassID()!=rollClsid) {
+            if (Control *r = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID,rollClsid))
+               if (!tmCont->SetRollController(r))
+                  r->DeleteThis();
+         }
+      }
+      return c;
+   }
+   return NULL;
+}
+
+Control* AnimationImport::MakeScale(Control *tmCont, Class_ID clsid)
+{
+   Interface *ip = ni.gi;
+   if (Control *c = tmCont->GetScaleController()) {
+      if (c->ClassID()!=clsid) {
+         if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_SCALE_CLASS_ID, clsid)){
+            if (!tmCont->SetScaleController(tmpCtrl))
+               tmpCtrl->DeleteThis();
+            else
+               c = tmpCtrl;
+         }
+      }
+      return c;
+   }
+   return NULL;
+}
+
+Control* AnimationImport::MakePositionXYZ(Control *tmCont, Class_ID clsid)
+{
+   Interface *ip = ni.gi;
+   if (Control *c = tmCont->GetPositionController()){
+      // First make the controller and XYZ Independent position controller, then fix individuals
+      if (c->ClassID()!= IPOS_CONTROL_CLASS_ID) {
+         if (Control *tmp = (Control*)ip->CreateInstance(CTRL_POSITION_CLASS_ID, IPOS_CONTROL_CLASS_ID)) {
+            if (!tmCont->SetPositionController(tmp)) {
+               tmp->DeleteThis();
+            } else {
+               c = tmp;
+            }
+         }
+      }
+      if (Control *x = c->GetXController()){
+         if (x->ClassID()!= clsid) {
+            if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid))
+               c->SetReference(IPOS_X_REF, tmp);
+         }
+      }
+      if (Control *y = c->GetYController()){
+         if (y->ClassID()!= clsid) {
+            if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid))
+               c->SetReference(IPOS_Y_REF, tmp);
+         }
+      }
+      if (Control *z = c->GetZController()){
+         if (z->ClassID()!= clsid) {
+            if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid))
+               c->SetReference(IPOS_Z_REF, tmp);
+         }
+      }
+      return c;
+   }
+   return NULL;
+}
diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..28f9bcf6159f46c68a631e90cec94db154df2893
--- /dev/null
+++ b/NifImport/ImportMeshAndSkin.cpp
@@ -0,0 +1,364 @@
+/**********************************************************************
+*<
+FILE: ImportMeshAndSkin.cpp
+
+DESCRIPTION: Mesh and Skin Import routines
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#include "stdafx.h"
+#include "MaxNifImport.h"
+
+using namespace Niflib;
+
+// Locate a TriObject in an Object if it exists
+TriObject* GetTriObject(Object *o)
+{
+   if (o && o->CanConvertToType(triObjectClassID))
+      return (TriObject *)o->ConvertToType(0, triObjectClassID);
+   while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o)
+   {
+      IDerivedObject* dobj = (IDerivedObject *)(o);
+      o = dobj->GetObjRef();
+      if (o && o->CanConvertToType(triObjectClassID))
+         return (TriObject *)o->ConvertToType(0, triObjectClassID);
+   }
+   return NULL;
+}
+
+// Get or Create the Skin Modifier
+Modifier *GetSkin(INode *node)
+{
+   Object* pObj = node->GetObjectRef();
+   if (!pObj) return NULL;
+   while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
+   {
+      IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
+      int Idx = 0;
+      while (Idx < pDerObj->NumModifiers())
+      {
+         // Get the modifier. 
+         Modifier* mod = pDerObj->GetModifier(Idx);
+         if (mod->ClassID() == SKIN_CLASSID)
+         {
+            // is this the correct Physique Modifier based on index?
+            return mod;
+         }
+         Idx++;
+      }
+      pObj = pDerObj->GetObjRef();
+   }
+
+   IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
+
+   //create a skin modifier and add it
+   Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
+   dobj->SetAFlag(A_LOCK_TARGET);
+   dobj->AddModifier(skinMod);
+   dobj->ClearAFlag(A_LOCK_TARGET);
+   node->SetObjectRef(dobj);
+   return skinMod;
+}
+
+bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject)
+{
+   node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform()));   
+   return true;
+}
+
+
+bool NifImporter::ImportMeshes(NiNodeRef node)
+{
+   bool ok = true;
+   try 
+   {
+      vector<NiTriShapeRef> trinodes = DynamicCast<NiTriShape>(node->GetChildren());
+      for (vector<NiTriShapeRef>::iterator itr = trinodes.begin(), end = trinodes.end(); itr != end; ++itr){
+         ok |= ImportMesh(*itr);
+      }
+      vector<NiTriStripsRef> tristrips = DynamicCast<NiTriStrips>(node->GetChildren());
+      for (vector<NiTriStripsRef>::iterator itr = tristrips.begin(), end = tristrips.end(); itr != end; ++itr){
+         ok |= ImportMesh(*itr);
+      }
+      vector<NiNodeRef> nodes = DynamicCast<NiNode>(node->GetChildren());
+      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
+         ok |= ImportMeshes(*itr);
+      }
+   }
+   catch( exception & e ) 
+   {
+      e=e;
+      ok = false;
+   }
+   catch( ... ) 
+   {
+      ok = false;
+   }
+   return ok;
+}
+
+bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture)
+{
+   Mesh& mesh = o->GetMesh();
+
+   node->SetTransform(0,TOMATRIX3(triGeom->GetLocalTransform()));  
+
+   // Vertex info
+   {
+      int nVertices = triGeomData->GetVertexCount();
+      vector<Vector3> vertices = triGeomData->GetVertices();
+      mesh.setNumVerts(nVertices);
+      for (int i=0; i < nVertices; ++i){
+         Vector3 &v = vertices[i];
+         mesh.verts[i].Set(v.x, v.y, v.z);
+      }
+   }
+   // uv texture info
+   {
+      int nUVSet = triGeomData->GetUVSetCount();
+      int n = 0;
+      for (int j=0; j<nUVSet; j++){
+         vector<TexCoord> texCoords = triGeomData->GetUVSet(j);
+         n = texCoords.size();
+         mesh.setNumTVerts(n, FALSE);
+         for (int i=0; i<n; ++i) {
+            TexCoord& texCoord = texCoords[i];
+            mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? -texCoord.v : texCoord.v, 0);
+         }
+      }
+   }
+   if (removeDegenerateFaces)
+      mesh.RemoveDegenerateFaces();
+   if (removeIllegalFaces)
+      mesh.RemoveIllegalFaces();
+   if (enableAutoSmooth)
+      mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
+
+   // Normals
+   {
+      mesh.checkNormals(TRUE);
+      vector<Vector3> n = triGeomData->GetNormals();
+      for (int i=0; i<n.size(); i++){
+         Vector3 v = n[i];
+         mesh.setNormal(i, Point3(v.x, v.y, v.z));
+      }
+   }
+   // vertex coloring
+   {
+      bool hasAlpha = false;
+      vector<Color4> cv = triGeomData->GetColors();
+      mesh.setNumVertCol(cv.size());
+      for (int i=0; i<cv.size(); i++){
+         Color4& c = cv[i];
+         mesh.vertCol[i].Set(c.r, c.g, c.b);
+         hasAlpha |= (c.a != 0.0);
+      }
+   }
+   return true;
+}
+
+void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture)
+{
+   int n = v.size();
+   mesh.setNumFaces(n);
+   //if (hasTexture)
+      mesh.setNumTVFaces(n);
+   for (int i=0; i<n; ++i) {
+      Triangle& t = v[i];
+      Face& f = mesh.faces[i];
+      f.setVerts(t.v1, t.v2, t.v3);
+      f.Show();
+      f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);
+      //if (hasTexture) {
+         TVFace& tf = mesh.tvFace[i];
+         tf.setTVerts(t.v1, t.v2, t.v3);
+      //}
+   }
+}
+
+bool NifImporter::ImportMesh(NiTriShapeRef triShape)
+{
+   bool ok = true;
+
+   ImpNode *node = i->CreateNode();
+   if(!node) return false;
+   TriObject *triObject = CreateNewTriObject();
+   node->Reference(triObject);
+   string name = triShape->GetName();
+   node->SetName(name.c_str());
+
+   INode *inode = node->GetINode();
+
+   // Texture
+   bool hasTexture = ImportMaterialAndTextures(node, triShape);
+   Mesh& mesh = triObject->GetMesh();
+   NiTriShapeDataRef triShapeData = DynamicCast<NiTriShapeData>(triShape->GetData());
+   if (triShapeData == NULL)
+      return false;
+
+   ok |= ImportMesh(node, triObject, triShape, triShapeData, hasTexture);
+
+   // Triangles and texture vertices
+   if (ok)
+   {
+      vector<Triangle> v = triShapeData->GetTriangles();
+      SetTrangles(mesh, v, hasTexture);
+   }
+   if (enableSkinSupport)
+      ImportSkin(node, triShape);
+
+   i->AddNodeToScene(node);   
+
+   // attach child
+   if (INode *parent = GetNode(triShape->GetParent()))
+      parent->AttachChild(inode, 1);
+
+   inode->Hide(triShape->GetHidden() ? TRUE : FALSE);
+   
+   if (enableAutoSmooth){
+      if (TriObject *tri = GetTriObject(inode->GetObjectRef())){
+         tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
+      }
+   }
+
+   return ok;
+}
+
+bool NifImporter::ImportMesh(NiTriStripsRef triStrips)
+{
+   bool ok = true;
+
+   ImpNode *node = i->CreateNode();
+   if(!node) return false;
+   INode *inode = node->GetINode();
+   TriObject *triObject = CreateNewTriObject();
+   node->Reference(triObject);
+   string name = triStrips->GetName();
+   node->SetName(name.c_str());
+
+   // Texture
+   bool hasTexture = ImportMaterialAndTextures(node, triStrips);
+
+   Mesh& mesh = triObject->GetMesh();
+   NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData());
+   if (triStripsData == NULL)
+      return false;
+
+   ok |= ImportMesh(node, triObject, triStrips, triStripsData, hasTexture);
+
+   // Triangles and texture vertices
+   if (ok)
+   {
+      vector<Triangle> v = triStripsData->GetTriangles();
+      SetTrangles(mesh, v, hasTexture);
+   }
+   if (enableSkinSupport)
+      ImportSkin(node, triStrips);
+
+   i->AddNodeToScene(node);   
+
+   // attach child
+   if (INode *parent = GetNode(triStrips->GetParent())) {
+      parent->AttachChild(inode, 0);
+   }
+
+   inode->Hide(triStrips->GetHidden() ? TRUE : FALSE);
+
+   // apply autosmooth after object creation for it to take hold
+   if (enableAutoSmooth){
+      if (TriObject *tri = GetTriObject(inode->GetObjectRef())){
+         tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
+      }
+   }
+
+   return ok;
+}
+
+struct VertexHolder
+{
+   VertexHolder() : vertIndex(0), count(0) {}
+
+   int vertIndex;
+   int count;
+   Tab<INode*> boneNodeList;
+   Tab<float> weights;
+};
+
+bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom)
+{
+   bool ok = true;
+   NiSkinInstanceRef nifSkin = triGeom->GetSkinInstance();
+   if (!nifSkin) 
+      return false;
+
+   INode *tnode = node->GetINode();
+
+   NiSkinDataRef data = nifSkin->GetSkinData();
+   NiSkinPartitionRef part = nifSkin->GetSkinPartition();
+
+   vector<NiNodeRef> nifBones = nifSkin->GetBones();
+
+   //create a skin modifier and add it
+   Modifier *skinMod = GetSkin(tnode);
+   TriObject *triObject = GetTriObject(tnode->GetObjectRef());
+   Mesh& m = triObject->GetMesh();
+
+   //get the skin interface
+   if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){
+      ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA);
+
+      // Create Bone List
+      Tab<INode*> bones;
+      int i=0;
+      for (vector<NiNodeRef>::iterator itr = nifBones.begin(), end = nifBones.end(); itr != end; ++itr, ++i){
+         string name = (*itr)->GetName();
+         if (INode *boneRef = gi->GetINodeByName(name.c_str())) {
+            bones.Append(1, &boneRef);
+            iskinImport->AddBoneEx(boneRef, TRUE);
+         }
+
+         // Set Bone Transform
+         //Matrix3 tm = boneRef->GetObjectTM(0);
+         //Matrix3 m = TOMATRIX3(data->GetBoneTransform(i));
+         //iskinImport->SetBoneTm(boneRef, tm, m);
+      }
+      ObjectState os = tnode->EvalWorldState(0);
+
+      // Need to trigger ModifyObject in BonesDefMod prior to adding vertices or nothing is added
+      GetCOREInterface()->ForceCompleteRedraw();
+
+      // Need to get a list of bones and weights for each vertex.
+      vector<VertexHolder> vertexHolders;
+      vertexHolders.resize(m.numVerts);
+      for (int i=0, n=data->GetBoneCount();i<n; ++i){
+         if (INode *boneRef = bones[i]){
+            vector<SkinWeight> weights = data->GetBoneWeights(i);
+            for (vector<SkinWeight>::iterator itr=weights.begin(), end=weights.end(); itr != end; ++itr){
+               VertexHolder& h = vertexHolders[itr->index];
+               h.vertIndex = itr->index;
+               ++h.count;
+               h.weights.Append(1, &itr->weight);
+               h.boneNodeList.Append(1, &boneRef);
+            }
+         }
+      }
+
+      // Assign the weights 
+      for (vector<VertexHolder>::iterator itr=vertexHolders.begin(), end=vertexHolders.end(); itr != end; ++itr){
+         VertexHolder& h = (*itr);
+         if (h.count){
+            float sum = 0.0f;
+            for (int i=0; i<h.count; ++i)
+               sum += h.weights[i];
+            ASSERT(fabs(sum-1.0) < 0.001);
+            BOOL add = iskinImport->AddWeights(tnode, h.vertIndex, h.boneNodeList, h.weights);
+            add = add;
+         }
+      }
+   }
+   return ok;
+}
diff --git a/NifImport/ImportMtlAndTex.cpp b/NifImport/ImportMtlAndTex.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..038520d9884564974814a1f47f7e0a197bf08fda
--- /dev/null
+++ b/NifImport/ImportMtlAndTex.cpp
@@ -0,0 +1,142 @@
+/**********************************************************************
+*<
+FILE: ImportMtlAndTex.cpp
+
+DESCRIPTION:	Material and Texture Import routines
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#include "stdafx.h"
+#include <shaders.h>
+#include "MaxNifImport.h"
+#include "obj/NiWireframeProperty.h"
+#include "obj/NiAlphaProperty.h"
+#include "obj/NiStencilProperty.h"
+#include "objectParams.h"
+using namespace Niflib;
+
+Texmap* NifImporter::CreateTexture(TexDesc& desc)
+{
+   BitmapManager *bmpMgr = TheManager;
+   if (NiSourceTextureRef texSrc = desc.source){
+      string filename = texSrc->GetExternalFileName();
+      if (bmpMgr->CanImport(filename.c_str())){
+         BitmapTex *bmpTex = NewDefaultBitmapTex();
+         bmpTex->SetName(texSrc->GetName().c_str());
+         bmpTex->SetMapName(const_cast<TCHAR*>(FindImage(filename).c_str()));
+         bmpTex->SetAlphaAsMono(TRUE);
+         bmpTex->SetAlphaSource(ALPHA_NONE);
+         return bmpTex;
+      }
+   }
+   return NULL;
+}
+
+bool NifImporter::ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject)
+{
+   // Texture
+   vector<NiPropertyRef> props = avObject->GetProperties();
+   NiTexturingPropertyRef texRef = SelectFirstObjectOfType<NiTexturingProperty>(props);
+   NiMaterialPropertyRef matRef = SelectFirstObjectOfType<NiMaterialProperty>(props);
+   NiWireframePropertyRef wireRef = SelectFirstObjectOfType<NiWireframeProperty>(props);
+   NiAlphaPropertyRef alphaRef = SelectFirstObjectOfType<NiAlphaProperty>(props);
+   NiStencilPropertyRef stencilRef = SelectFirstObjectOfType<NiStencilProperty>(props);
+
+   bool hasTexture = (texRef && matRef);
+   if (matRef != NULL){
+      StdMat2 *m = NewDefaultStdMat();
+      m->SetName(matRef->GetName().c_str());
+      if (showTextures)
+         m->SetMtlFlag(MTL_DISPLAY_ENABLE_FLAGS, TRUE);
+      m->SetAmbient(TOCOLOR(matRef->GetAmbientColor()),0);
+      m->SetDiffuse(TOCOLOR(matRef->GetDiffuseColor()),0);
+      m->SetSpecular(TOCOLOR(matRef->GetSpecularColor()),0);
+      Color c = TOCOLOR(matRef->GetEmissiveColor());
+      if (c.r != 0 || c.b != 0 || c.g != 0) {
+         m->SetSelfIllumColorOn(TRUE);
+         m->SetSelfIllumColor(c,0);
+      }
+      m->SetShinStr(0.0,0);
+      m->SetShininess(matRef->GetGlossiness()/100.0,0);
+      m->SetOpacity(matRef->GetTransparency(),0);
+
+      bool hasShaderAttributes = (wireRef != NULL) || (stencilRef != NULL);
+      if (m->SupportsShaders() && hasShaderAttributes) {
+         if (Shader *s = m->GetShader()) {
+            if (wireRef != NULL && (wireRef->GetFlags() & 1)) {
+               BOOL value = TRUE;
+               setMAXScriptValue(s->GetReference(0), "wire", 0, value);
+            }
+            if (stencilRef != NULL) {
+
+            }
+         }
+      }
+      
+
+      if (NULL != texRef)
+      {
+         // Handle Base/Detail ???
+         if (texRef->HasTexture(DECAL_0_MAP)){
+            if (Texmap* tex = CreateTexture(texRef->GetTexture(DECAL_0_MAP)))
+               m->SetSubTexmap(ID_DI, tex);
+            if (texRef->HasTexture(BASE_MAP)){
+               m->LockAmbDiffTex(FALSE);
+               if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP)))
+                  m->SetSubTexmap(ID_AM, tex);
+            }
+         } else if (texRef->HasTexture(BASE_MAP)) {
+            if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP)))
+               m->SetSubTexmap(ID_DI, tex);
+         } 
+         // Handle Bump map
+         if (texRef->HasTexture(BUMP_MAP)) {
+            if (Texmap* tex = CreateTexture(texRef->GetTexture(BUMP_MAP)))
+               m->SetSubTexmap(ID_BU, tex);
+         }
+         // Shiny map
+         if (texRef->HasTexture(GLOSS_MAP)) {
+            if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOSS_MAP)))
+               m->SetSubTexmap(ID_SS, tex);
+         }
+
+         // Self illumination
+         if (texRef->HasTexture(GLOW_MAP)) {
+            if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOW_MAP)))
+               m->SetSubTexmap(ID_SI, tex);
+         }
+      }
+      gi->GetMaterialLibrary().Add(m);
+      node->GetINode()->SetMtl(m);
+   }
+   return hasTexture;
+}
+
+string NifImporter::FindImage(const string& name)
+{
+   TCHAR buffer[MAX_PATH];
+
+   // Simply check for fully qualified path
+   if (PathIsRoot(name.c_str())) {
+      if (-1 != _taccess(name.c_str(), 0))
+         return string(buffer);
+   } 
+   if (!path.empty()) {
+      PathCombine(buffer, path.c_str(), name.c_str()); // try as-is
+      if (-1 != _taccess(buffer, 0))
+         return string(buffer);
+
+      // try only filename in nif directory
+      PathCombine(buffer, path.c_str(), PathFindFileName(name.c_str()));
+      if (-1 != _taccess(buffer, 0))
+         return string(buffer);
+   }
+   if (appSettings != NULL) {
+      return appSettings->FindImage(name);
+   }
+   return name;
+}
\ No newline at end of file
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..926801979509242d99ba103afb0bad77d92c6bb1
--- /dev/null
+++ b/NifImport/ImportSkeleton.cpp
@@ -0,0 +1,446 @@
+/**********************************************************************
+*<
+FILE: ImportSkeleton.cpp
+
+DESCRIPTION:	Skeleton import routines
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#include "stdafx.h"
+#include "MaxNifImport.h"
+#include <obj/NiTriBasedGeom.h>
+#include <obj/NiTriBasedGeomData.h>
+#include <obj/NiTimeController.h>
+#include <float.h>
+#include <dummy.h>
+
+using namespace Niflib;
+
+struct NiNodeNameEquivalence : public NumericStringEquivalence
+{
+   bool operator()(const NiNodeRef& n1, const NiNodeRef& n2) const { 
+      return NumericStringEquivalence::operator()(n1->GetName(), n2->GetName());
+   }
+};
+
+void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks)
+{
+   //Send all skeleton roots to bind position
+   for (uint i = 0; i < blocks.size(); ++i) {
+      NiNodeRef node = blocks[i];
+      if ( node != NULL && node->IsSkeletonRoot() ) {
+         node->GoToSkeletonBindPosition();
+      }
+   }
+}
+
+bool NifImporter::HasSkeleton()
+{
+   if (!skeletonCheck.empty()){
+      vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, skeletonCheck.c_str());
+      return !bipedRoots.empty();
+   }
+   return false;
+}
+
+
+bool NifImporter::IsBiped()
+{
+   if (hasSkeleton){
+      list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
+      if (!extraData.empty()) {
+         return ( DynamicCast<BSBound>(extraData).size() != 0 );
+      }
+   }
+   return false;
+}
+
+void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes)
+{
+   IBipMaster* master = NULL;
+   try 
+   {
+      vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, "Bip??");
+      std::stable_sort(bipedRoots.begin(), bipedRoots.end(), NiNodeNameEquivalence());
+      for (vector<NiNodeRef>::iterator bipedItr = bipedRoots.begin(); bipedItr != bipedRoots.end(); ++bipedItr)
+      {
+         string bipname = (*bipedItr)->GetName();
+         string match = bipname + "*";
+         vector<NiNodeRef> bipedNodes = SelectNodesByName(nodes, match.c_str());
+
+         float height = this->bipedHeight;
+#if USE_CUSTOM_BSBOUND
+         list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
+         if (!extraData.empty()) {
+            BSBoundRef bound = SelectFirst<BSBound>(extraData);
+            if (bound) {
+               array<float,6> floats = bound->GetUnknownFloats();
+               height = floats[2] * 2.0f;
+            }
+         }
+#endif
+         float angle = TORAD(bipedAngle);
+         Point3 wpos(0.0f,0.0f,0.0f);
+         BOOL arms = (CountNodesByName(bipedNodes, FormatText("%s L UpperArm", bipname.c_str())) > 0) ? TRUE : FALSE;
+         BOOL triPelvis = bipedTrianglePelvis ? TRUE : FALSE;
+         int nnecklinks=CountNodesByName(bipedNodes, FormatText("%s Neck*", bipname.c_str()));
+         int nspinelinks=CountNodesByName(bipedNodes, FormatText("%s Spine*", bipname.c_str()));
+         int nleglinks = 3 + CountNodesByName(bipedNodes, FormatText("%s L HorseLink", bipname.c_str()));
+         int ntaillinks = CountNodesByName(bipedNodes, FormatText("%s Tail*", bipname.c_str()));
+         int npony1links = CountNodesByName(bipedNodes, FormatText("%s Ponytail1*", bipname.c_str()));
+         int npony2links = CountNodesByName(bipedNodes, FormatText("%s Ponytail2*", bipname.c_str()));
+         int numfingers = CountNodesByName(bipedNodes, FormatText("%s L Finger?", bipname.c_str()));
+         int nfinglinks = CountNodesByName(bipedNodes, FormatText("%s L Finger0*", bipname.c_str()));
+         int numtoes = CountNodesByName(bipedNodes, FormatText("%s L Toe?", bipname.c_str()));
+         int ntoelinks = CountNodesByName(bipedNodes, FormatText("%s L Toe0*", bipname.c_str()));
+         BOOL prop1exists = CountNodesByName(bipedNodes, FormatText("%s Prop1", bipname.c_str())) ? TRUE : FALSE;
+         BOOL prop2exists = CountNodesByName(bipedNodes, FormatText("%s Prop2", bipname.c_str())) ? TRUE : FALSE;
+         BOOL prop3exists = CountNodesByName(bipedNodes, FormatText("%s Prop3", bipname.c_str())) ? TRUE : FALSE;
+         int forearmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Fore*Twist*", bipname.c_str()));
+         int upperarmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Up*Twist*", bipname.c_str()));
+         int thighTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Thigh*Twist*", bipname.c_str()));
+         int calfTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Calf*Twist*", bipname.c_str()));
+         int horseTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Horse*Twist*", bipname.c_str()));
+
+         NiNodeRef root = nodes[0];
+         IBipMaster* master = Max8CreateNewBiped(height, angle, wpos, arms, triPelvis, 
+            nnecklinks, nspinelinks, nleglinks, ntaillinks, npony1links, npony2links, 
+            numfingers, nfinglinks, numtoes, ntoelinks, bipedAnkleAttach, prop1exists, 
+            prop2exists, prop3exists, forearmTwistLinks, upperarmTwistLinks, thighTwistLinks,
+            calfTwistLinks, horseTwistLinks);
+         master->SetRootName(const_cast<TCHAR*>(bipname.c_str()));
+         if (master)
+         {
+            master->BeginModes(BMODE_FIGURE, FALSE);
+            master->SetTrianglePelvis(FALSE);
+            master->SetDisplaySettings(BDISP_BONES);
+            LPCTSTR bipname = master->GetRootName();
+
+            // Rename twists, if necessary
+            RenameNode(gi, FormatText("%s L ForeTwist", bipname), FormatText("%s L ForearmTwist", bipname));
+            RenameNode(gi, FormatText("%s R ForeTwist", bipname), FormatText("%s R ForearmTwist", bipname));
+            RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s L UpperArmTwist", bipname));
+            RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s R UpperArmTwist", bipname));
+
+            NiNodeRef nifBip = FindNodeByName(nodes, bipname);
+            //AlignBiped(master, nifBip);
+            ScaleBiped(master, nifBip, true);
+            PositionBiped(master, nifBip, true);
+            RotateBiped(master, nifBip, true);
+         }
+      }
+   }
+   catch( exception & e ) 
+   {
+      e=e;
+   }
+   catch( ... ) 
+   {
+   }
+   if (master)
+      master->EndModes(BMODE_FIGURE, TRUE);
+}
+
+void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef block)
+{
+   string name = block->GetName();
+   Matrix44 wt = block->GetWorldTransform();
+   Matrix44 lt = block->GetLocalTransform();
+
+   Vector3 pos; Matrix33 rot; float scale;
+   wt.Decompose(pos, rot, scale);
+
+   INode *node = gi->GetINodeByName(name.c_str());
+   if (node != NULL) 
+   {
+      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
+      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
+
+      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
+      Matrix3 im = Inverse(m);
+
+      Point3 p; Quat q; Point3 s;
+      DecomposeMatrix(im, p, q, s);
+      master->SetBipedRot(q, 0, node, TRUE);
+   }
+   else
+   {
+
+   }
+   vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
+   for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
+      AlignBiped(master, *itr);
+   }
+}
+
+void NifImporter::PositionBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
+{
+   string name = block->GetName();
+
+   Matrix44 wt = block->GetWorldTransform();
+   Matrix44 lt = block->GetLocalTransform();
+
+   Vector3 pos; Matrix33 rot; float scale;
+   wt.Decompose(pos, rot, scale);
+
+   INode *node = gi->GetINodeByName(name.c_str());
+   if (node != NULL) 
+   {
+      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
+      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
+
+      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
+      Matrix3 im = Inverse(m);
+      Point3 p; Quat q; Point3 s;
+      DecomposeMatrix(im, p, q, s);
+      master->SetBipedRot(q, 0, node, TRUE);
+   }
+   else
+   {
+
+   }
+   if (Recurse)
+   {
+      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
+      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
+         PositionBiped(master, *itr, Recurse);
+      }
+   }
+}
+
+void NifImporter::RotateBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
+{
+   string name = block->GetName();
+
+   Matrix44 wt = block->GetWorldTransform();
+   Matrix44 lt = block->GetLocalTransform();
+
+   Vector3 pos; Matrix33 rot; float scale;
+   wt.Decompose(pos, rot, scale);
+
+   INode *node = gi->GetINodeByName(name.c_str());
+   if (node != NULL) 
+   {
+      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
+      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
+
+      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
+      Matrix3 im = Inverse(m);
+
+      Point3 p; Quat q; Point3 s;
+      DecomposeMatrix(im, p, q, s);
+      master->SetBipedRot(q, 0, node, TRUE);
+   }
+   else
+   {
+
+   }
+   if (Recurse)
+   {
+      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
+      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
+         RotateBiped(master, *itr, Recurse);
+      }
+   }
+}
+
+void NifImporter::ScaleBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
+{
+   string name = block->GetName();
+
+   Matrix44 wt = block->GetWorldTransform();
+   Matrix44 lt = block->GetLocalTransform();
+
+   Vector3 pos; Matrix33 rot; float scale;
+   wt.Decompose(pos, rot, scale);
+
+   INode *node = gi->GetINodeByName(name.c_str());
+   if (node != NULL) 
+   {
+      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
+      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
+
+      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
+      Matrix3 im = Inverse(m);
+
+      Point3 p; Quat q; Point3 s;
+      DecomposeMatrix(im, p, q, s);
+      master->SetBipedRot(q, 0, node, TRUE);
+   }
+   else
+   {
+
+   }
+   if (Recurse)
+   {
+      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
+      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
+         ScaleBiped(master, *itr, Recurse);
+      }
+   }
+}
+
+
+INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis)
+{
+   if (FPInterface * fpBones = GetCOREInterface(Interface_ID(0x438aff72, 0xef9675ac)))
+   {
+      FunctionID createBoneID = fpBones->FindFn(TEXT("createBone"));
+      FPValue result;
+      FPParams params (3, TYPE_POINT3, &startPos, TYPE_POINT3, &endPos, TYPE_POINT3, &zAxis);     
+      FPStatus status = fpBones->Invoke(createBoneID, result, &params);
+      if (status == FPS_OK && result.type == TYPE_INODE)
+      {
+         if (INode *n = result.n)
+         {
+            n->SetName(const_cast<TCHAR*>(name.c_str()));
+            float len = Length(endPos-startPos);
+            float width = max(minBoneWidth, min(maxBoneWidth, len * boneWidthToLengthRatio));
+            if (Object* o = n->GetObjectRef())
+            {
+               setMAXScriptValue(o->GetReference(0), "width", 0, width);
+               setMAXScriptValue(o->GetReference(0), "height", 0, width);
+            }
+            n->ShowBone(1);
+         }
+         return result.n;
+      }
+      fpBones->ReleaseInterface();
+   }
+   return NULL;
+}
+
+INode *NifImporter::CreateHelper(const string& name, Point3 startPos)
+{
+   //POINTHELP_CLASS_ID
+   if (DummyObject *ob = (DummyObject *)gi->CreateInstance(HELPER_CLASS_ID,Class_ID(DUMMY_CLASS_ID,0))) {
+      const float DUMSZ = 1.0f;
+      ob->SetBox(Box3(Point3(-DUMSZ,-DUMSZ,-DUMSZ),Point3(DUMSZ,DUMSZ,DUMSZ)));
+      if (INode *n = gi->CreateObjectNode(ob)) {
+         n->SetName(const_cast<TCHAR*>(name.c_str()));
+         Quat q; q.Identity();
+         PosRotScaleNode(n, startPos, q, 1.0f, prsPos);
+         return n;
+      }
+   }
+   return  NULL;
+}
+
+void NifImporter::ImportBones(vector<NiNodeRef>& bones)
+{
+   for (vector<NiNodeRef>::iterator itr = bones.begin(), end = bones.end(); itr != end; ++itr){
+      ImportBones(*itr);
+   }
+}
+
+float GetObjectLength(NiAVObjectRef obj)
+{
+   float clen = obj->GetLocalTranslation().Magnitude();
+   if (clen < (FLT_EPSILON*10)) {
+      if (NiTriBasedGeomRef geom = DynamicCast<NiTriBasedGeom>(obj)) {
+         if (NiTriBasedGeomDataRef data = geom->GetData()) {
+            clen = data->GetRadius() * 2.0f;
+         }
+      }
+   }
+   return clen;
+}
+
+void NifImporter::ImportBones(NiNodeRef node)
+{
+   try 
+   {
+      string name = node->GetName();
+      vector<NiAVObjectRef> children = node->GetChildren();
+      vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
+
+      NiAVObject::CollisionType cType = node->GetCollision();
+      if (cType == NiAVObject::ctBoundingBox && children.empty() && name=="Bounding Box")
+         return;
+
+      NiNodeRef parent = node->GetParent();
+
+      PosRotScale prs = prsDefault;
+      Matrix44 m4 = node->GetWorldTransform();
+      Vector3 pos; Matrix33 rot; float scale;
+      m4.Decompose(pos, rot, scale);
+
+      Matrix3 im = TOMATRIX3(m4);
+      Point3 p = im.GetTrans();
+      Quat q(im);
+      //q.Normalize();
+      Vector3 ppos;
+      Point3 zAxis(0,1,0);
+      bool hasChildren = !children.empty();
+      if (hasChildren) {
+         float len = 0.0f;
+         for (vector<NiAVObjectRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) {
+            len += GetObjectLength(*itr);
+         }
+         len /= float(children.size());
+         ppos = pos + Vector3(len, 0.0f, 0.0f); // just really need magnitude as rotation will take care of positioning
+      }
+      else if (parent)
+      {
+         float len = node->GetLocalTranslation().Magnitude();
+         ppos = pos + Vector3(len/3.0f, 0.0f, 0.0f);
+      }
+      Point3 pp(ppos.x, ppos.y, ppos.z);
+
+      INode *bone = gi->GetINodeByName(name.c_str());
+      if (bone)
+      {
+         // Is there a better way of "Affect Pivot Only" behaviors?
+         INode *pinode = bone->GetParentNode();
+         if (pinode)
+            bone->Detach(0,1);
+         PosRotScaleNode(bone, p, q, scale, prs);
+         if (pinode)
+            pinode->AttachChild(bone, 1);
+      }
+      else
+      {
+         list<NiTimeControllerRef> ctrls = node->GetControllers();
+         if (parent == NULL || parent->GetParent() == NULL || ctrls.empty())
+         {
+            bone = CreateHelper(name, p);
+            if (ctrls.empty())
+               bone->Hide(TRUE);
+         }
+         else if (bone = CreateBone(name, p, pp, zAxis))
+         {
+            PosRotScaleNode(bone, p, q, scale, prs);
+            if (createNubsForBones && childNodes.empty()){
+               if (INode *helper = CreateHelper(string().assign(name).append(" Nub"), pp)){
+                  helper->Hide(TRUE);
+                  bone->AttachChild(helper, 1);
+               }
+            }
+         }
+         if (bone)
+         {
+            if (parent)
+            {
+               if (INode *pn = gi->GetINodeByName(parent->GetName().c_str()))
+                  pn->AttachChild(bone, 1);
+            }
+            bone->Hide(node->GetHidden() ? TRUE : FALSE);
+         }
+      }
+      if (bone)
+      {
+         ImportBones(childNodes);
+      }
+   }
+   catch( exception & e ) 
+   {
+      e=e;
+   }
+   catch( ... ) 
+   {
+   }
+}
\ No newline at end of file
diff --git a/NifImport/KFMImporter.cpp b/NifImport/KFMImporter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dcdc4ef98c0e6c9cdf2990dc619812826a55bbb0
--- /dev/null
+++ b/NifImport/KFMImporter.cpp
@@ -0,0 +1,63 @@
+#include "StdAfx.h"
+#include "KFMImporter.h"
+#include <kfm.h>
+#include "gen/ControllerLink.h"
+using namespace Niflib;
+
+KFMImporter::KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
+   : BaseClass()
+{
+   BaseInit(Name, I, GI, SuppressPrompts);
+}
+
+KFMImporter::~KFMImporter(void)
+{
+}
+
+void KFMImporter::ReadBlocks()
+{
+   try
+   {
+      Kfm kfm;
+      int ver = kfm.Read(name);
+      if (ver != VER_UNSUPPORTED)
+      {
+         TCHAR buffer[MAX_PATH];
+         GetFullPathName(name.c_str(), MAX_PATH, buffer, NULL);
+         PathRemoveFileSpec(buffer);
+#ifdef USE_UNSUPPORTED_CODE
+         //root = kfm.MergeActions(string(buffer));
+         BuildNodes();
+
+         string last_file;
+         vector<NiControllerSequenceRef> ctrllist;
+
+         for ( vector<KfmAction>::iterator it = kfm.actions.begin(); it != kfm.actions.end(); it++ ) {
+            string action_filename = path + '\\' + it->action_filename;
+            if (-1 != _taccess(action_filename.c_str(), 0)){
+               if (action_filename != last_file) {
+                   ctrllist = DynamicCast<NiControllerSequence>(ReadNifList(action_filename));
+               }
+               if ((*it).unk_int2 && (*it).unk_int2 <= ctrllist.size()) {
+                  if (NiControllerSequenceRef ctrlSeq = ctrllist[(*it).unk_int2-1])
+                     kf.push_back(ctrlSeq);
+               }
+            }
+         }
+#endif
+      }
+   }
+   catch (std::exception&)
+   {
+   }
+   catch (...) 
+   {
+   }
+}
+
+bool KFMImporter::DoImport()
+{
+   // might check if blocks exist and if not go ahead and import nif.
+   return ImportAnimation();
+   //return BaseClass::DoImport();
+}
diff --git a/NifImport/KFMImporter.h b/NifImport/KFMImporter.h
new file mode 100644
index 0000000000000000000000000000000000000000..241f706fd1a90a3a08c0c87717414a165235fcdf
--- /dev/null
+++ b/NifImport/KFMImporter.h
@@ -0,0 +1,41 @@
+/**********************************************************************
+*<
+FILE: KFMImporter.h
+
+DESCRIPTION:	KFM Importer helper class 
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#ifndef _KFMIMPORTER_H_
+#define _KFMIMPORTER_H_
+
+#pragma once
+#include "NIFImporter.h"
+#include "niutils.h"
+#include "obj/NiControllerSequence.h"
+
+class KFMImporter : public NifImporter
+{
+   typedef NifImporter BaseClass;
+public:
+   KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts);
+   ~KFMImporter(void);
+
+   void ReadBlocks();
+   virtual bool DoImport();
+
+   virtual bool isValid() const { return (NULL != root) || !kf.empty(); }
+
+   // Implemented in ImportAnimation.cpp
+   virtual bool ImportAnimation();
+
+
+   std::vector<Niflib::NiControllerSequenceRef> kf;
+};
+
+
+#endif 
\ No newline at end of file
diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp
index d862c802121665c7e8d5218c5692216621020831..aa456df63a2da1cf64eb602b2c09feabc80ce4e3 100644
--- a/NifImport/MaxNifImport.cpp
+++ b/NifImport/MaxNifImport.cpp
@@ -11,200 +11,13 @@
  *>	Copyright (c) 2006, All Rights Reserved.
  **********************************************************************/
 #include "stdafx.h"
-#include <math.h>
-#include <io.h>
-#include <sstream>
 #include "MaxNifImport.h"
-#include <cs/Biped8Api.h>
-#include <scenetraversal.h> 
-#include <plugapi.h>
-#include <triobj.h> 
-#include <bitmap.h>
-#include <modstack.h>
-#include <iskin.h>
-#include <strclass.h>
-#include "objectParams.h"
-#undef ALPHA_NONE
-
-#include <math.h>
-#include <string.h>
-
-#include "niflib.h"
-#include "obj\NiObject.h"
-#include "obj\NiNode.h"
-#include "obj\NiTriShape.h"
-#include "obj\NiTriShapeData.h"
-#include "obj\NiTriStrips.h"
-#include "obj\NiTriStripsData.h"
-#include "obj\NiMaterialProperty.h"
-#include "obj\NiTexturingProperty.h"
-#include "obj\NiSourceTexture.h"
-#include "obj\NiExtraData.h"
-#include "obj\BSBound.h"
-#include "obj\NiSkinData.h"
-#include "obj\NiSkinInstance.h"
-#include "obj\NiSkinPartition.h"
-
-#include "niutils.h"
-#include "AppSettings.h"
-
-#ifndef ASSERT
-#ifdef _DEBUG
-#include <crtdbg.h>
-#define ASSERT _ASSERTE
-#else
-#define ASSERT(exp)
-#endif
-#endif
-
+#include "KFMImporter.h"
 using namespace Niflib;
 
 #define MaxNifImport_CLASS_ID	Class_ID(0x794ac1c1, 0x8b4c64c7)
 
-// Define the standard section names used in the ini file
-LPCTSTR NifImportSection = TEXT("MaxNifImport");
-LPCTSTR SystemSection = TEXT("System");
-LPCTSTR BipedImportSection = TEXT("BipedImport");
-
-struct NiNodeNameEquivalence : public NumericStringEquivalence
-{
-   bool operator()(const NiNodeRef& n1, const NiNodeRef& n2) const { 
-      return NumericStringEquivalence::operator()(n1->GetName(), n2->GetName());
-   }
-};
-
-// NIF Importer
-class NifImporter
-{
-public:
-   string name;
-   string path;
-   ImpInterface *i;
-   Interface *gi;
-   BOOL suppressPrompts;
-   bool iniFileValid;
-   string iniFileName;
-   AppSettings *appSettings;
-
-   // Ini settings
-   bool showTextures; // show textures in viewport
-   bool removeIllegalFaces;
-   bool removeDegenerateFaces;
-   bool enableAutoSmooth;
-   float autoSmoothAngle;
-   bool flipUVTextures;
-   bool enableSkinSupport;
-   bool goToSkeletonBindPosition;
-
-   // Biped/Bones related settings
-   string skeleton;
-   float bipedHeight;
-   string skeletonCheck;
-   float bipedAngle;
-   float bipedAnkleAttach;
-   bool bipedTrianglePelvis;
-   bool importSkeleton;
-   bool useBiped;
-   bool hasSkeleton;
-   bool isBiped;
-   bool removeUnusedImportedBones;
-   bool forceRotation;
-   bool browseForSkeleton;
-   string defaultSkeletonName;
-
-   float minBoneWidth;
-   float maxBoneWidth;
-   float boneWidthToLengthRatio;
-
-   vector<NiObjectRef> blocks;
-   vector<NiNodeRef> nodes;
-
-   NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
-      : name(Name), i(I), gi(GI), suppressPrompts(SuppressPrompts)
-   {
-      char buffer[MAX_PATH] = {0}, *p = NULL;
-      GetFullPathName(Name, _countof(buffer), buffer, &p);
-      if (p) *p = 0;
-      path = buffer;
-
-      // Load ini settings
-      iniFileValid = false;
-      LoadIniSettings();
-
-      // load file
-      blocks = ReadNifList( name );
-      nodes = DynamicCast<NiNode>(blocks);
-
-      // Apply post processing checks after reading blocks
-      if (isValid()){
-         if (goToSkeletonBindPosition && !nodes.empty())
-            GoToSkeletonBindPosition(nodes);
-
-         hasSkeleton = HasSkeleton();
-         isBiped = IsBiped();
-         skeleton = (appSettings != NULL) ? appSettings->Skeleton : "";
-         importSkeleton &= hasSkeleton;
-
-         // Guess that the skeleton is the same one in the current directory
-         if (importSkeleton && !defaultSkeletonName.empty()) {
-            TCHAR buffer[MAX_PATH];
-            GetFullPathName(name.c_str(), _countof(buffer), buffer, NULL);
-            PathRemoveFileSpec(buffer);
-            PathAddBackslash(buffer);
-            PathAppend(buffer, defaultSkeletonName.c_str());
-            if (-1 != _taccess(buffer, 0))
-               skeleton = buffer;
-         }
-      }
-   }
-   bool isValid() const { return (0 != blocks.size()); }
-
-   // Ini File related routines
-   void LoadIniSettings();
-   void SaveIniSettings();
-
-   // Generic IniFile reading routine
-   template<typename T>
-   T GetIniValue(LPCTSTR Section, LPCTSTR Setting, T Default){
-      if (!iniFileValid) 
-         return Default;
-      return ::GetIniValue<T>(Section, Setting, Default, iniFileName.c_str());
-   }
-   // Generic IniFile reading routine
-   template<typename T>
-   void SetIniValue(LPCTSTR Section, LPCTSTR Setting, T value){
-      if (!iniFileValid) 
-         return;
-      ::SetIniValue<T>(Section, Setting, value, iniFileName.c_str());
-   }
-
-   bool HasSkeleton();
-   bool IsBiped();
-   void ImportBones(vector<NiNodeRef>& bones);
-   void ImportBones(NiNodeRef blocks);
-   void ImportBipeds(vector<NiNodeRef>& blocks);
-   void AlignBiped(IBipMaster* master, NiNodeRef block);
-   void PositionBiped(IBipMaster* master, NiNodeRef block, bool Recurse = false);
-   void RotateBiped(IBipMaster* master, NiNodeRef block, bool Recurse = false);
-   void ScaleBiped(IBipMaster* master, NiNodeRef block, bool Recurse = false);
-   bool ImportMeshes(NiNodeRef block);
-   string FindImage(const string& name);
-
-   void SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture);
-   bool ImportMesh(NiTriShapeRef triShape);
-   bool ImportMesh(NiTriStripsRef triStrips);
-   bool ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject);
-   bool ImportTransform(ImpNode *node, NiAVObjectRef avObject);
-   bool ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture);
-
-   bool ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom);
-   Texmap* CreateTexture(TexDesc& desc);
-
-   INode *CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis);
-};
-
-
-
+string shortDescription;
 
 class MaxNifImport : public SceneImport {
 	public:
@@ -228,7 +41,6 @@ class MaxNifImport : public SceneImport {
 		~MaxNifImport();		
 
       string iniFileName;
-      string shortDescription;
 };
 
 
@@ -307,7 +119,7 @@ MaxNifImport::MaxNifImport()
       PathAppend(iniName, "MaxNifTools.ini");
    }
    iniFileName = iniName;
-   shortDescription = GetIniValue<string>(SystemSection, "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str());
+   shortDescription = GetIniValue<string>("System", "ShortDescription", "Netimmerse/Gamebryo", iniFileName.c_str());
 }
 
 MaxNifImport::~MaxNifImport() 
@@ -317,14 +129,21 @@ MaxNifImport::~MaxNifImport()
 
 int MaxNifImport::ExtCount()
 {
-	//TODO: Returns the number of file name extensions supported by the plug-in.
-	return 1;
+#ifdef USE_UNSUPPORTED_CODE
+	return 2;
+#else
+   return 1;
+#endif
 }
 
 const TCHAR *MaxNifImport::Ext(int n)
 {		
-	//TODO: Return the 'i-th' file name extension (i.e. "3DS").
-	return _T("NIF");
+   switch (n)
+   {
+   case 0: return _T("NIF");
+   case 1: return _T("KFM");
+   }
+   return _T("");	
 }
 
 const TCHAR *MaxNifImport::LongDesc()
@@ -401,96 +220,24 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi,
 
       std::string current_file = filename;
 
-      NifImporter importer(filename, i, gi, suppressPrompts);
-      if (!importer.isValid())
-         return FALSE;
-
-      //if(!suppressPrompts)
-      //	DialogBoxParam(hInstance, 
-      //			MAKEINTRESOURCE(IDD_PANEL), 
-      //			GetActiveWindow(), 
-      //			MaxNifImportOptionsDlgProc, (LPARAM)&importer);
+      LPCTSTR ext = PathFindExtension(filename);
 
-      // 
-      if (importer.isBiped)
+      if (_tcsicmp(ext, ".KFM") == 0)
       {
-         if (importer.useBiped){
-            importer.ImportBipeds(importer.nodes);
-         } else {
-            importer.ImportBones(DynamicCast<NiNode>(importer.nodes[0]->GetChildren()));
-         }
+         KFMImporter importer(filename, i, gi, suppressPrompts);
+         if (!importer.isValid())
+            return FALSE;
+
+         ok = importer.DoImport();
+
       }
       else
       {
-         vector<string> importedBones;
-         if (importer.importSkeleton)
-         {
-            if (importer.browseForSkeleton)
-            {
-               TCHAR filter[64], *pfilter=filter;
-               pfilter = _tcscpy(filter, this->ShortDesc());
-               pfilter = _tcscat(pfilter, " (*.NIF)");
-               pfilter += strlen(pfilter);
-               *pfilter++ = '\0';
-               _tcscpy(pfilter, "*.NIF");
-               pfilter += strlen(pfilter);
-               *pfilter++ = '\0';
-               *pfilter++ = '\0';
-
-               TCHAR filename[MAX_PATH];
-               GetFullPathName(importer.skeleton.c_str(), _countof(filename), filename, NULL);
-
-               OPENFILENAME ofn;
-               memset(&ofn, 0, sizeof(ofn));
-               ofn.lStructSize = sizeof(ofn);
-               ofn.hwndOwner = gi->GetMAXHWnd();
-               ofn.lpstrFilter = filter;
-               ofn.lpstrFile = filename;
-               ofn.nMaxFile = _countof(filename);
-               ofn.lpstrTitle = TEXT("Browse for Skeleton NIF...");
-               ofn.lpstrDefExt = TEXT("NIF");
-               ofn.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST;
-               importer.importSkeleton = GetOpenFileName(&ofn) ? true : false;
-               if (importer.importSkeleton) {
-                  importer.skeleton = filename;
-               }
-            }
-            if (importer.importSkeleton && !importer.skeleton.empty()) {
-               NifImporter skelImport(importer.skeleton.c_str(), i, gi, suppressPrompts);
-               if (skelImport.isValid())
-               {
-                  if (skelImport.useBiped){
-                     skelImport.ImportBipeds(skelImport.nodes);
-                  } else {
-                     skelImport.ImportBones(DynamicCast<NiNode>(skelImport.nodes[0]->GetChildren()));
-                     if (importer.removeUnusedImportedBones){
-                        importedBones = GetNamesOfNodes(skelImport.nodes);
-                     }
-                  }
-               }
-            }
-         }
-         if (importer.isValid()) {
-
-            importer.ImportBones(DynamicCast<NiNode>(importer.nodes[0]->GetChildren()));
-            ok = importer.ImportMeshes(importer.nodes[0]);
+         NifImporter importer(filename, i, gi, suppressPrompts);
+         if (!importer.isValid())
+            return FALSE;
 
-            if (importer.importSkeleton && importer.removeUnusedImportedBones){
-               vector<string> importedNodes = GetNamesOfNodes(importer.nodes);
-               sort(importedBones.begin(), importedBones.end());
-               sort(importedNodes.begin(), importedNodes.end());
-               vector<string> results;
-               results.resize(importedBones.size());
-               vector<string>::iterator end = set_difference ( 
-                  importedBones.begin(), importedBones.end(),
-                  importedNodes.begin(), importedNodes.end(), results.begin());
-               for (vector<string>::iterator itr = results.begin(); itr != end; ++itr){
-                  if (INode *node = gi->GetINodeByName((*itr).c_str())){
-                     node->Delete(0, TRUE);
-                  }
-               }
-            }
-         }
+         ok = importer.DoImport();
       }
    }
    catch( exception & e ) 
@@ -510,808 +257,3 @@ int MaxNifImport::DoImport(const TCHAR *filename,ImpInterface *i, Interface *gi,
    return ok ? TRUE : FALSE;
 }
 
-void NifImporter::LoadIniSettings()
-{
-   TCHAR iniName[MAX_PATH];
-   LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR);
-   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
-   this->iniFileName = iniName;
-   iniFileValid = (-1 != _access(iniName, 0));
-
-   // Locate which application to use. If Auto, find first app where this file appears in the root path list
-   appSettings = NULL;
-   string curapp = GetIniValue<string>(NifImportSection, "CurrentApp", "AUTO");
-   if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
-      // Scan Root paths
-      for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
-         if ((*itr).IsFileInRootPaths(this->name)) {
-            appSettings = &(*itr);
-            break;
-         }
-      }
-   } else {
-      appSettings = FindAppSetting(curapp);
-   }
-   if (appSettings == NULL && !TheAppSettings.empty()){
-      appSettings = &TheAppSettings.front();
-   }
-
-   useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false);
-   skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*");
-   showTextures = GetIniValue<bool>(NifImportSection, "ShowTextures", true);
-
-   removeIllegalFaces = GetIniValue<bool>(NifImportSection, "RemoveIllegalFaces", true);
-   removeDegenerateFaces = GetIniValue<bool>(NifImportSection, "RemoveDegenerateFaces", true);
-   enableAutoSmooth = GetIniValue<bool>(NifImportSection, "EnableAutoSmooth", true);
-   autoSmoothAngle = GetIniValue<float>(NifImportSection, "AutoSmoothAngle", 30.0f);
-   flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true);
-   enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true);
-
-   bipedHeight = GetIniValue<float>(BipedImportSection, "BipedHeight", 131.90f);
-   bipedAngle = GetIniValue<float>(BipedImportSection, "BipedAngle", 90.0f);
-   bipedAnkleAttach = GetIniValue<float>(BipedImportSection, "BipedAnkleAttach", 0.2f);
-   bipedTrianglePelvis = GetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", false);
-   removeUnusedImportedBones = GetIniValue<bool>(BipedImportSection, "RemoveUnusedImportedBones", false);
-   forceRotation = GetIniValue<bool>(BipedImportSection, "ForceRotation", true);
-   browseForSkeleton = GetIniValue<bool>(BipedImportSection, "BrowseForSkeleton", true);
-   defaultSkeletonName = GetIniValue<string>(BipedImportSection, "DefaultSkeletonName", "Skeleton.Nif");
-
-   minBoneWidth = GetIniValue<float>(BipedImportSection, "MinBoneWidth", 0.5f);
-   maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f);
-   boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f);
-
-   goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false);
-}
-
-void NifImporter::SaveIniSettings()
-{
-   SetIniValue<bool>(NifImportSection, "UseBiped", useBiped);
-   SetIniValue<string>(NifImportSection, "Skeleton", skeleton);
-   SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck);
-
-   SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight);
-   SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle);
-   SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach);
-   SetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis);
-}
-
-bool NifImporter::HasSkeleton()
-{
-   if (!skeletonCheck.empty()){
-      vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, skeletonCheck.c_str());
-      return !bipedRoots.empty();
-   }
-   return false;
-}
-
-bool NifImporter::IsBiped()
-{
-   if (hasSkeleton){
-      list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
-      if (!extraData.empty()) {
-         return ( DynamicCast<BSBound>(extraData).size() != 0 );
-      }
-   }
-   return false;
-}
-
-void NifImporter::ImportBipeds(vector<NiNodeRef>& nodes)
-{
-   IBipMaster* master = NULL;
-   try 
-   {
-      vector<NiNodeRef> bipedRoots = SelectNodesByName(nodes, "Bip??");
-      std::stable_sort(bipedRoots.begin(), bipedRoots.end(), NiNodeNameEquivalence());
-      for (vector<NiNodeRef>::iterator bipedItr = bipedRoots.begin(); bipedItr != bipedRoots.end(); ++bipedItr)
-      {
-         string bipname = (*bipedItr)->GetName();
-         string match = bipname + "*";
-         vector<NiNodeRef> bipedNodes = SelectNodesByName(nodes, match.c_str());
-
-         float height = this->bipedHeight;
-#if USE_CUSTOM_BSBOUND
-         list<NiExtraDataRef> extraData = nodes[0]->GetExtraData();
-         if (!extraData.empty()) {
-            BSBoundRef bound = SelectFirst<BSBound>(extraData);
-            if (bound) {
-               array<float,6> floats = bound->GetUnknownFloats();
-               height = floats[2] * 2.0f;
-            }
-         }
-#endif
-         float angle = TORAD(bipedAngle);
-         Point3 wpos(0.0f,0.0f,0.0f);
-         BOOL arms = (CountNodesByName(bipedNodes, FormatText("%s L UpperArm", bipname.c_str())) > 0) ? TRUE : FALSE;
-         BOOL triPelvis = bipedTrianglePelvis ? TRUE : FALSE;
-         int nnecklinks=CountNodesByName(bipedNodes, FormatText("%s Neck*", bipname.c_str()));
-         int nspinelinks=CountNodesByName(bipedNodes, FormatText("%s Spine*", bipname.c_str()));
-         int nleglinks = 3 + CountNodesByName(bipedNodes, FormatText("%s L HorseLink", bipname.c_str()));
-         int ntaillinks = CountNodesByName(bipedNodes, FormatText("%s Tail*", bipname.c_str()));
-         int npony1links = CountNodesByName(bipedNodes, FormatText("%s Ponytail1*", bipname.c_str()));
-         int npony2links = CountNodesByName(bipedNodes, FormatText("%s Ponytail2*", bipname.c_str()));
-         int numfingers = CountNodesByName(bipedNodes, FormatText("%s L Finger?", bipname.c_str()));
-         int nfinglinks = CountNodesByName(bipedNodes, FormatText("%s L Finger0*", bipname.c_str()));
-         int numtoes = CountNodesByName(bipedNodes, FormatText("%s L Toe?", bipname.c_str()));
-         int ntoelinks = CountNodesByName(bipedNodes, FormatText("%s L Toe0*", bipname.c_str()));
-         BOOL prop1exists = CountNodesByName(bipedNodes, FormatText("%s Prop1", bipname.c_str())) ? TRUE : FALSE;
-         BOOL prop2exists = CountNodesByName(bipedNodes, FormatText("%s Prop2", bipname.c_str())) ? TRUE : FALSE;
-         BOOL prop3exists = CountNodesByName(bipedNodes, FormatText("%s Prop3", bipname.c_str())) ? TRUE : FALSE;
-         int forearmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Fore*Twist*", bipname.c_str()));
-         int upperarmTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Up*Twist*", bipname.c_str()));
-         int thighTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Thigh*Twist*", bipname.c_str()));
-         int calfTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Calf*Twist*", bipname.c_str()));
-         int horseTwistLinks = CountNodesByName(bipedNodes, FormatText("%s L Horse*Twist*", bipname.c_str()));
-
-         NiNodeRef root = nodes[0];
-         IBipMaster* master = CreateNewBiped(height, angle, wpos, arms, triPelvis, nnecklinks, nspinelinks, 
-            nleglinks, ntaillinks, npony1links, npony2links, numfingers, nfinglinks, numtoes, 
-            ntoelinks, bipedAnkleAttach, prop1exists, prop2exists, prop3exists,
-            forearmTwistLinks, upperarmTwistLinks, thighTwistLinks,
-            calfTwistLinks, horseTwistLinks);
-         master->SetRootName(const_cast<TCHAR*>(bipname.c_str()));
-         if (master)
-         {
-            master->BeginModes(BMODE_FIGURE, FALSE);
-            master->SetTrianglePelvis(FALSE);
-            master->SetDisplaySettings(BDISP_BONES);
-            LPCTSTR bipname = master->GetRootName();
-
-            // Rename twists, if necessary
-            RenameNode(gi, FormatText("%s L ForeTwist", bipname), FormatText("%s L ForearmTwist", bipname));
-            RenameNode(gi, FormatText("%s R ForeTwist", bipname), FormatText("%s R ForearmTwist", bipname));
-            RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s L UpperArmTwist", bipname));
-            RenameNode(gi, FormatText("%s R LUpArmTwist", bipname), FormatText("%s R UpperArmTwist", bipname));
-
-            NiNodeRef nifBip = FindNodeByName(nodes, bipname);
-            //AlignBiped(master, nifBip);
-            ScaleBiped(master, nifBip, true);
-            PositionBiped(master, nifBip, true);
-            RotateBiped(master, nifBip, true);
-         }
-      }
-   }
-   catch( exception & e ) 
-   {
-      e=e;
-   }
-   catch( ... ) 
-   {
-   }
-   if (master)
-      master->EndModes(BMODE_FIGURE, TRUE);
-}
-
-void NifImporter::AlignBiped(IBipMaster* master, NiNodeRef block)
-{
-   string name = block->GetName();
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
-   {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
-
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
-
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
-   }
-   else
-   {
-
-   }
-   vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-   for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-      AlignBiped(master, *itr);
-   }
-}
-
-void NifImporter::PositionBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
-{
-   string name = block->GetName();
-
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
-   {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
-
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
-   }
-   else
-   {
-
-   }
-   if (Recurse)
-   {
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         PositionBiped(master, *itr, Recurse);
-      }
-   }
-}
-
-void NifImporter::RotateBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
-{
-   string name = block->GetName();
-
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
-   {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
-
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
-
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
-   }
-   else
-   {
-
-   }
-   if (Recurse)
-   {
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         RotateBiped(master, *itr, Recurse);
-      }
-   }
-}
-
-void NifImporter::ScaleBiped(IBipMaster* master, NiNodeRef block, bool Recurse)
-{
-   string name = block->GetName();
-
-   Matrix44 wt = block->GetWorldTransform();
-   Matrix44 lt = block->GetLocalTransform();
-
-   Vector3 pos; Matrix33 rot; float scale;
-   wt.Decompose(pos, rot, scale);
-
-   INode *node = gi->GetINodeByName(name.c_str());
-   if (node != NULL) 
-   {
-      Matrix3 m3 = node->GetNodeTM(TimeValue(), NULL); // local translation m
-      master->SetBipedPos(Point3(pos.x, pos.y, pos.z), 0, node, TRUE);
-
-      Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3(0,0,0));
-      Matrix3 im = Inverse(m);
-
-      Point3 p; Quat q; Point3 s;
-      DecomposeMatrix(im, p, q, s);
-      master->SetBipedRot(q, 0, node, TRUE);
-   }
-   else
-   {
-
-   }
-   if (Recurse)
-   {
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(block->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         ScaleBiped(master, *itr, Recurse);
-      }
-   }
-}
-
-
-INode *NifImporter::CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis)
-{
-   if (FPInterface * fpBones = GetCOREInterface(Interface_ID(0x438aff72, 0xef9675ac)))
-   {
-      FunctionID createBoneID = fpBones->FindFn(TEXT("createBone"));
-      FPValue result;
-      FPParams params (3, TYPE_POINT3, &startPos, TYPE_POINT3, &endPos, TYPE_POINT3, &zAxis);     
-      FPStatus status = fpBones->Invoke(createBoneID, result, &params);
-      if (status == FPS_OK && result.type == TYPE_INODE)
-      {
-         if (INode *n = result.n)
-         {
-            n->SetName(const_cast<TCHAR*>(name.c_str()));
-            float len = fabs(Length(startPos)-Length(endPos));
-            float width = max(minBoneWidth, min(maxBoneWidth, len * boneWidthToLengthRatio));
-            if (Object* o = n->GetObjectRef())
-            {
-               setMAXScriptValue(o->GetReference(0), "width", 0, width);
-               setMAXScriptValue(o->GetReference(0), "height", 0, width);
-            }
-         }
-         return result.n;
-      }
-      fpBones->ReleaseInterface();
-   }
-   return NULL;
-}
-
-void NifImporter::ImportBones(vector<NiNodeRef>& bones)
-{
-   for (vector<NiNodeRef>::iterator itr = bones.begin(), end = bones.end(); itr != end; ++itr){
-      ImportBones(*itr);
-   }
-}
-
-void NifImporter::ImportBones(NiNodeRef node)
-{
-   try 
-   {
-      string name = node->GetName();
-      vector<NiNodeRef> children = DynamicCast<NiNode>(node->GetChildren());
-      NiNodeRef parent = node->GetParent();
-
-      PosRotScale prs = prsDefault;
-      Matrix3 im = TOMATRIX3(node->GetWorldTransform(), true);
-      Point3 p = im.GetTrans();
-      Quat q(im);
-      q.Normalize();
-      Vector3 ppos;
-      Point3 zAxis(0,1,0);
-      if (!children.empty()) {
-         for (vector<NiNodeRef>::iterator itr=children.begin(), end = children.end(); itr != end; ++itr) {
-            Matrix44 cwt = (*itr)->GetWorldTransform();
-            Vector3 cpos; Matrix33 crot; float cscale;
-            cwt.Decompose(cpos, crot, cscale);
-            ppos += cpos;
-         }
-         ppos /= children.size();
-      }
-      else if (parent)
-      {
-         Matrix44 pwt = parent->GetWorldTransform();
-         Matrix33 prot; float pscale;
-         pwt.Decompose(ppos, prot, pscale);
-         if (forceRotation)
-            prs = prsPos;
-      }
-      else
-      {
-         if (forceRotation)
-            prs = prsPos;
-      }
-      Point3 pp(ppos.x, ppos.y, ppos.z);
-
-      INode *bone = gi->GetINodeByName(name.c_str());
-      if (bone)
-      {
-         // Is there a better way of "Affect Pivot Only" behaviors?
-         INode *pinode = bone->GetParentNode();
-         if (pinode)
-            bone->Detach(0,1);
-         PositionAndRotateNode(bone, p, q, prs);
-         if (pinode)
-            pinode->AttachChild(bone, 1);
-      }
-      else
-      {
-         if (bone = CreateBone(name, p, pp, zAxis))
-         {
-            PositionAndRotateNode(bone, p, q, prs);
-            if (parent)
-            {
-               if (INode *pn = gi->GetINodeByName(parent->GetName().c_str()))
-                  pn->AttachChild(bone, 1);
-            }
-         }
-      }
-      if (bone)
-      {
-         ImportBones(children);
-      }
-   }
-   catch( exception & e ) 
-   {
-      e=e;
-   }
-   catch( ... ) 
-   {
-   }
-}
-
-bool NifImporter::ImportMeshes(NiNodeRef node)
-{
-   bool ok = true;
-   try 
-   {
-      vector<NiTriShapeRef> trinodes = DynamicCast<NiTriShape>(node->GetChildren());
-      for (vector<NiTriShapeRef>::iterator itr = trinodes.begin(), end = trinodes.end(); ok && itr != end; ++itr){
-         ok |= ImportMesh(*itr);
-      }
-      vector<NiTriStripsRef> tristrips = DynamicCast<NiTriStrips>(node->GetChildren());
-      for (vector<NiTriStripsRef>::iterator itr = tristrips.begin(), end = tristrips.end(); ok && itr != end; ++itr){
-         ok |= ImportMesh(*itr);
-      }
-      vector<NiNodeRef> nodes = DynamicCast<NiNode>(node->GetChildren());
-      for (vector<NiNodeRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr){
-         ok |= ImportMeshes(*itr);
-      }
-   }
-   catch( exception & e ) 
-   {
-      e=e;
-      ok = false;
-   }
-   catch( ... ) 
-   {
-      ok = false;
-   }
-   return ok;
-}
-
-Texmap* NifImporter::CreateTexture(TexDesc& desc)
-{
-   BitmapManager *bmpMgr = TheManager;
-   if (NiSourceTextureRef texSrc = desc.source){
-      string filename = texSrc->GetExternalFileName();
-      if (bmpMgr->CanImport(filename.c_str())){
-         BitmapTex *bmpTex = NewDefaultBitmapTex();
-         bmpTex->SetName(texSrc->GetName().c_str());
-         bmpTex->SetMapName(const_cast<TCHAR*>(FindImage(filename).c_str()));
-         bmpTex->SetAlphaAsMono(TRUE);
-         bmpTex->SetAlphaSource(ALPHA_NONE);
-         return bmpTex;
-      }
-   }
-   return NULL;
-}
-
-bool NifImporter::ImportMaterialAndTextures(ImpNode *node, NiAVObjectRef avObject)
-{
-   // Texture
-   NiTexturingPropertyRef texRef = avObject->GetPropertyByType( NiTexturingProperty::TypeConst() );
-   NiMaterialPropertyRef matRef = avObject->GetPropertyByType( NiMaterialProperty::TypeConst() );
-   
-   bool hasTexture = (texRef && matRef);
-   if (hasTexture){
-      StdMat2 *m = NewDefaultStdMat();
-      m->SetName(matRef->GetName().c_str());
-      if (showTextures)
-         m->SetMtlFlag(MTL_DISPLAY_ENABLE_FLAGS, TRUE);
-      m->SetAmbient(TOCOLOR(matRef->GetAmbientColor()),0);
-      m->SetDiffuse(TOCOLOR(matRef->GetDiffuseColor()),0);
-      m->SetSpecular(TOCOLOR(matRef->GetSpecularColor()),0);
-      Color c = TOCOLOR(matRef->GetEmissiveColor());
-      if (c.r != 0 || c.b != 0 || c.g != 0) {
-         m->SetSelfIllumColorOn(TRUE);
-         m->SetSelfIllumColor(c,0);
-      }
-      m->SetShinStr(0.0,0);
-      m->SetShininess(matRef->GetGlossiness()/100.0,0);
-      m->SetOpacity(matRef->GetTransparency(),0);
-
-      // Handle Base/Detail ???
-      if (texRef->HasTexture(DECAL_0_MAP)){
-         if (Texmap* tex = CreateTexture(texRef->GetTexture(DECAL_0_MAP)))
-            m->SetSubTexmap(ID_DI, tex);
-         if (texRef->HasTexture(BASE_MAP)){
-            m->LockAmbDiffTex(FALSE);
-            if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP)))
-               m->SetSubTexmap(ID_AM, tex);
-         }
-      } else if (texRef->HasTexture(BASE_MAP)) {
-         if (Texmap* tex = CreateTexture(texRef->GetTexture(BASE_MAP)))
-            m->SetSubTexmap(ID_DI, tex);
-      } 
-      // Handle Bump map
-      if (texRef->HasTexture(BUMP_MAP)) {
-         if (Texmap* tex = CreateTexture(texRef->GetTexture(BUMP_MAP)))
-            m->SetSubTexmap(ID_BU, tex);
-      }
-      // Shiny map
-      if (texRef->HasTexture(GLOSS_MAP)) {
-         if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOSS_MAP)))
-            m->SetSubTexmap(ID_SS, tex);
-      }
-
-      // Self illumination
-      if (texRef->HasTexture(GLOW_MAP)) {
-         if (Texmap* tex = CreateTexture(texRef->GetTexture(GLOW_MAP)))
-            m->SetSubTexmap(ID_SI, tex);
-      }
-
-      gi->GetMaterialLibrary().Add(m);
-      node->GetINode()->SetMtl(m);
-   }
-   return hasTexture;
-}
-
-bool NifImporter::ImportTransform(ImpNode *node, NiAVObjectRef avObject)
-{
-   node->SetTransform(0,TOMATRIX3(avObject->GetWorldTransform()));   
-   return true;
-}
-
-bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triGeom, NiTriBasedGeomDataRef triGeomData, bool hasTexture)
-{
-   Mesh& mesh = o->GetMesh();
-
-   // Vertex info
-   {
-      int nVertices = triGeomData->GetVertexCount();
-      vector<Vector3> vertices = triGeomData->GetVertices();
-      mesh.setNumVerts(nVertices);
-      for (int i=0; i < nVertices; ++i){
-         Vector3 &v = vertices[i];
-         mesh.verts[i].Set(v.x, v.y, v.z);
-      }
-   }
-   if (hasTexture) // uv texture info
-   {
-      int nUVSet = triGeomData->GetUVSetCount();
-      int n = 0;
-      for (int j=0; j<nUVSet; j++){
-         vector<TexCoord> texCoords = triGeomData->GetUVSet(j);
-         n = texCoords.size();
-         mesh.setNumTVerts(n, FALSE);
-         for (int i=0; i<n; ++i) {
-            TexCoord& texCoord = texCoords[i];
-            mesh.tVerts[i].Set(texCoord.u, (flipUVTextures) ? -texCoord.v : texCoord.v, 0);
-         }
-      }
-   }
-   if (removeDegenerateFaces)
-      mesh.RemoveDegenerateFaces();
-   if (removeIllegalFaces)
-      mesh.RemoveIllegalFaces();
-   if (enableAutoSmooth)
-      mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
-
-   // Normals
-   {
-      mesh.checkNormals(TRUE);
-      vector<Vector3> n = triGeomData->GetNormals();
-      for (int i=0; i<n.size(); i++){
-         Vector3 v = n[i];
-         mesh.setNormal(i, Point3(v.x, v.y, v.z));
-      }
-   }
-   // vertex coloring
-   {
-      bool hasAlpha = false;
-      vector<Color4> cv = triGeomData->GetColors();
-      mesh.setNumVertCol(cv.size());
-      for (int i=0; i<cv.size(); i++){
-         Color4& c = cv[i];
-         mesh.vertCol[i].Set(c.r, c.g, c.b);
-         hasAlpha |= (c.a != 0.0);
-      }
-   }
-   return true;
-}
-
-void NifImporter::SetTrangles(Mesh& mesh, vector<Triangle>& v, bool hasTexture)
-{
-   int n = v.size();
-   mesh.setNumFaces(n);
-   if (hasTexture)
-      mesh.setNumTVFaces(n);
-   for (int i=0; i<n; ++i) {
-      Triangle& t = v[i];
-      Face& f = mesh.faces[i];
-      f.setVerts(t.v1, t.v2, t.v3);
-      f.Show();
-      f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);
-      if (hasTexture) {
-         TVFace& tf = mesh.tvFace[i];
-         tf.setTVerts(t.v1, t.v2, t.v3);
-      }
-   }
-}
-
-bool NifImporter::ImportMesh(NiTriShapeRef triShape)
-{
-   bool ok = true;
-
-   ImpNode *node = i->CreateNode();
-   if(!node) return false;
-   TriObject *triObject = CreateNewTriObject();
-   node->Reference(triObject);
-   string name = triShape->GetName();
-   node->SetName(name.c_str());
-
-   // Texture
-   bool hasTexture = ImportMaterialAndTextures(node, triShape);
-   ImportTransform(node, triShape);
-
-   Mesh& mesh = triObject->GetMesh();
-   NiTriShapeDataRef triShapeData = DynamicCast<NiTriShapeData>(triShape->GetData());
-   if (triShapeData == NULL)
-      return false;
-
-   ok |= ImportMesh(node, triObject, triShape, triShapeData, hasTexture);
-
-   // Triangles and texture vertices
-   if (ok)
-   {
-      vector<Triangle> v = triShapeData->GetTriangles();
-      SetTrangles(mesh, v, hasTexture);
-   }
-   if (enableSkinSupport)
-      ImportSkin(node, triShape);
-
-   i->AddNodeToScene(node);   
-
-   if (enableAutoSmooth){
-      if (TriObject *tri = GetTriObject(node->GetINode()->GetObjectRef())){
-        tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
-      }
-   }
-
-   return ok;
-}
-
-bool NifImporter::ImportMesh(NiTriStripsRef triStrips)
-{
-   bool ok = true;
-
-   ImpNode *node = i->CreateNode();
-   if(!node) return false;
-   TriObject *triObject = CreateNewTriObject();
-   node->Reference(triObject);
-   string name = triStrips->GetName();
-   node->SetName(name.c_str());
-
-   // Texture
-   bool hasTexture = ImportMaterialAndTextures(node, triStrips);
-   ImportTransform(node, triStrips);
-
-   Mesh& mesh = triObject->GetMesh();
-   NiTriStripsDataRef triStripsData = DynamicCast<NiTriStripsData>(triStrips->GetData());
-   if (triStripsData == NULL)
-      return false;
-
-   ok |= ImportMesh(node, triObject, triStrips, triStripsData, hasTexture);
-
-   // Triangles and texture vertices
-   if (ok)
-   {
-      vector<Triangle> v = triStripsData->GetTriangles();
-      SetTrangles(mesh, v, hasTexture);
-   }
-   if (enableSkinSupport)
-      ImportSkin(node, triStrips);
-
-   i->AddNodeToScene(node);   
-
-   // apply autosmooth after object creation for it to take hold
-   if (enableAutoSmooth){
-      if (TriObject *tri = GetTriObject(node->GetINode()->GetObjectRef())){
-         tri->GetMesh().AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
-      }
-   }
-
-   return ok;
-}
-
-struct VertexHolder
-{
-   VertexHolder() : vertIndex(0), count(0) {}
-
-   int vertIndex;
-   int count;
-   Tab<INode*> boneNodeList;
-   Tab<float> weights;
-};
-
-bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom)
-{
-   bool ok = true;
-   NiSkinInstanceRef nifSkin = triGeom->GetSkinInstance();
-   if (!nifSkin) 
-      return false;
-
-   INode *tnode = node->GetINode();
-
-   NiSkinDataRef data = nifSkin->GetSkinData();
-   NiSkinPartitionRef part = nifSkin->GetSkinPartition();
-
-   vector<NiNodeRef> nifBones = nifSkin->GetBones();
-
-   //create a skin modifier and add it
-   Modifier *skinMod = GetSkin(tnode);
-   TriObject *triObject = GetTriObject(tnode->GetObjectRef());
-   Mesh& m = triObject->GetMesh();
-
-   //get the skin interface
-   if (ISkin *skin = (ISkin *) skinMod->GetInterface(I_SKIN)){
-      ISkinImportData* iskinImport = (ISkinImportData*) skinMod->GetInterface(I_SKINIMPORTDATA);
-
-      // Create Bone List
-      Tab<INode*> bones;
-      int i=0;
-      for (vector<NiNodeRef>::iterator itr = nifBones.begin(), end = nifBones.end(); itr != end; ++itr, ++i){
-         string name = (*itr)->GetName();
-         INode *boneRef = gi->GetINodeByName(name.c_str());
-         bones.Append(1, &boneRef);
-         iskinImport->AddBoneEx(boneRef, TRUE);
-
-         // Set Bone Transform
-         //Matrix3 tm = boneRef->GetObjectTM(0);
-         //Matrix3 m = TOMATRIX3(data->GetBoneTransform(i));
-         //iskinImport->SetBoneTm(boneRef, tm, m);
-      }
-      ObjectState os = tnode->EvalWorldState(0);
-
-      // Need to trigger ModifyObject in BonesDefMod prior to adding vertices or nothing is added
-      GetCOREInterface()->ForceCompleteRedraw();
-
-      // Need to get a list of bones and weights for each vertex.
-      vector<VertexHolder> vertexHolders;
-      vertexHolders.resize(m.numVerts);
-      for (int i=0, n=data->GetBoneCount();i<n; ++i){
-         if (INode *boneRef = bones[i]){
-            vector<SkinWeight> weights = data->GetBoneWeights(i);
-            for (vector<SkinWeight>::iterator itr=weights.begin(), end=weights.end(); itr != end; ++itr){
-               VertexHolder& h = vertexHolders[itr->index];
-               h.vertIndex = itr->index;
-               ++h.count;
-               h.weights.Append(1, &itr->weight);
-               h.boneNodeList.Append(1, &boneRef);
-            }
-         }
-      }
-
-      // Assign the weights 
-      for (vector<VertexHolder>::iterator itr=vertexHolders.begin(), end=vertexHolders.end(); itr != end; ++itr){
-         VertexHolder& h = (*itr);
-         if (h.count){
-            float sum = 0.0f;
-            for (int i=0; i<h.count; ++i)
-               sum += h.weights[i];
-            ASSERT(fabs(sum-1.0) < 0.001);
-            BOOL add = iskinImport->AddWeights(tnode, h.vertIndex, h.boneNodeList, h.weights);
-            add = add;
-         }
-      }
-   }
-   return ok;
-}
-
-string NifImporter::FindImage(const string& name)
-{
-   TCHAR buffer[MAX_PATH];
-
-   // Simply check for fully qualified path
-   if (PathIsRoot(name.c_str())) {
-      if (-1 != _taccess(name.c_str(), 0))
-         return string(buffer);
-   } 
-   if (!path.empty()) {
-      PathCombine(buffer, path.c_str(), name.c_str()); // try as-is
-      if (-1 != _taccess(buffer, 0))
-         return string(buffer);
-
-      // try only filename in nif directory
-      PathCombine(buffer, path.c_str(), PathFindFileName(name.c_str()));
-      if (-1 != _taccess(buffer, 0))
-         return string(buffer);
-   }
-   if (appSettings != NULL) {
-      return appSettings->FindImage(name);
-   }
-   return name;
-}
\ No newline at end of file
diff --git a/NifImport/MaxNifImport.h b/NifImport/MaxNifImport.h
index 574cd65752e302dd91004b91711d20d18bdd4ffa..2106fa8139ef87b7c1192d05a4f72b84755cf89f 100644
--- a/NifImport/MaxNifImport.h
+++ b/NifImport/MaxNifImport.h
@@ -14,19 +14,64 @@
 #ifndef __MaxNifImport__H
 #define __MaxNifImport__H
 
-#include "Max.h"
+#include <math.h>
+#include <io.h>
+#include <sstream>
+
+#include <Max.h>
 #include "resource.h"
-#include "istdplug.h"
-#include "iparamb2.h"
-#include "iparamm2.h"
-//SIMPLE TYPE
+#include <istdplug.h>
+#include <iparamb2.h>
+#include <iparamm2.h>
+#include <cs/Biped8Api.h>
+#include <scenetraversal.h> 
+#include <plugapi.h>
+#include <triobj.h> 
+#include <bitmap.h>
+#include <modstack.h>
+#include <iskin.h>
+#include <strclass.h>
+#include "objectParams.h"
+#undef ALPHA_NONE
+
+#include <math.h>
+#include <string.h>
+
+#include "niflib.h"
+#include "obj\NiObject.h"
+#include "obj\NiNode.h"
+#include "obj\NiTriShape.h"
+#include "obj\NiTriShapeData.h"
+#include "obj\NiTriStrips.h"
+#include "obj\NiTriStripsData.h"
+#include "obj\NiMaterialProperty.h"
+#include "obj\NiTexturingProperty.h"
+#include "obj\NiSourceTexture.h"
+#include "obj\NiExtraData.h"
+#include "obj\BSBound.h"
+#include "obj\NiSkinData.h"
+#include "obj\NiSkinInstance.h"
+#include "obj\NiSkinPartition.h"
+
+#include "niutils.h"
+#include "AppSettings.h"
+
+#ifndef ASSERT
+#ifdef _DEBUG
+#include <crtdbg.h>
+#define ASSERT _ASSERTE
+#else
+#define ASSERT(exp)
+#endif
+#endif
 
 #include <direct.h>
 #include <commdlg.h>
 
+#include "NifImporter.h"
 
 extern TCHAR *GetString(int id);
-
+extern string shortDescription;
 extern HINSTANCE hInstance;
 
 #endif
diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc
index 1161745f77540028eaf0692ad78e3ba706cb2624..d181dc0d091f4acf5a990cc2245387806be43da5 100644
--- a/NifImport/MaxNifImport.rc
+++ b/NifImport/MaxNifImport.rc
@@ -88,7 +88,7 @@ END
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,1,3,0
+ FILEVERSION 0,1,3,2
  PRODUCTVERSION 0,1,3,0
  FILEFLAGSMASK 0x37L
 #ifdef _DEBUG
@@ -105,7 +105,7 @@ BEGIN
         BLOCK "040904b0"
         BEGIN
             VALUE "FileDescription", "3ds Max Nif Importer"
-            VALUE "FileVersion", "0, 1, 3, 0"
+            VALUE "FileVersion", "0, 1, 3, 2"
             VALUE "InternalName", "MaxNifImport.dli"
             VALUE "LegalCopyright", "Copyright (c) 2006, NIF File Format Library and Tools\r\nAll rights reserved."
             VALUE "OriginalFilename", "MaxNifImport.dli"
diff --git a/NifImport/MaxNifImport.vcproj b/NifImport/MaxNifImport.vcproj
index 28757c46f1b069443ae2635566e4e622eace420b..8ef3a13b730a8f6c03aaa51146fe854744036a26 100644
--- a/NifImport/MaxNifImport.vcproj
+++ b/NifImport/MaxNifImport.vcproj
@@ -52,6 +52,7 @@
 				AdditionalIncludeDirectories="C:\3dsmax8\maxsdk\include"
 				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;USE_NIFLIB_TEMPLATE_HELPERS;_USE_MATH_DEFINES"
 				StringPooling="true"
+				ExceptionHandling="2"
 				RuntimeLibrary="0"
 				EnableFunctionLevelLinking="true"
 				ForceConformanceInForLoopScope="false"
@@ -62,6 +63,7 @@
 				ProgramDataBaseFileName=".\Release\"
 				WarningLevel="3"
 				SuppressStartupBanner="true"
+				DebugInformationFormat="3"
 				CompileAs="0"
 			/>
 			<Tool
@@ -78,7 +80,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalOptions="/MACHINE:I386"
-				AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib biped.lib bmm.lib niflib.lib"
+				AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib bmm.lib niflib.lib"
 				OutputFile="C:\3dsmax8\plugins\MaxNifImport.dli"
 				LinkIncremental="1"
 				SuppressStartupBanner="true"
@@ -115,7 +117,7 @@
 			/>
 			<Tool
 				Name="VCPostBuildEventTool"
-				CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A;if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A;copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A;)&#x0D;&#x0A;)&#x0D;&#x0A;if exist &quot;C:\3dsmax8&quot; (&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)..\MaxNifPlugins_Readme.txt&quot; &quot;$(ProjectDir)Staging\&quot;&#x0D;&#x0A;)&#x0D;&#x0A;xcopy /D /Y /I &quot;$(TargetPath)&quot; &quot;$(ProjectDir)Staging\plugins\&quot;&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;$(ProjectDir)Staging\plugcfg&quot;"
+				CommandLine="if exist &quot;C:\3dsmax8\plugcfg&quot; (&#x0D;&#x0A;if not exist &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot; (&#x0D;&#x0A;copy &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;C:\3dsmax8\plugcfg\MaxNifTools.ini&quot;&#x0D;&#x0A;)&#x0D;&#x0A;)&#x0D;&#x0A;if exist &quot;C:\3dsmax8&quot; (&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)..\MaxNifPlugins_Readme.txt&quot; &quot;$(ProjectDir)Staging\&quot;&#x0D;&#x0A;)&#x0D;&#x0A;xcopy /D /Y /I &quot;$(TargetPath)&quot; &quot;$(ProjectDir)Staging\plugins\&quot;&#x0D;&#x0A;xcopy /D /Y /I &quot;$(ProjectDir)MaxNifTools.ini&quot; &quot;$(ProjectDir)Staging\plugcfg&quot;&#x0D;&#x0A;"
 			/>
 		</Configuration>
 		<Configuration
@@ -164,7 +166,7 @@
 				ProgramDataBaseFileName=".\Debug\"
 				WarningLevel="3"
 				SuppressStartupBanner="true"
-				DebugInformationFormat="4"
+				DebugInformationFormat="3"
 				CompileAs="0"
 			/>
 			<Tool
@@ -181,7 +183,7 @@
 			<Tool
 				Name="VCLinkerTool"
 				AdditionalOptions="/MACHINE:I386"
-				AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib biped.lib bmm.lib niflibd.lib"
+				AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib shlwapi.lib core.lib geom.lib gfx.lib mesh.lib maxutil.lib maxscrpt.lib paramblk2.lib bmm.lib niflibd.lib"
 				OutputFile="C:\3dsmax8\plugins\MaxNifImport.dli"
 				LinkIncremental="2"
 				SuppressStartupBanner="true"
@@ -298,6 +300,34 @@
 				RelativePath=".\AppSettings.h"
 				>
 			</File>
+			<File
+				RelativePath=".\BaseImporter.h"
+				>
+			</File>
+			<File
+				RelativePath=".\ImportAnimation.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ImportMeshAndSkin.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ImportMtlAndTex.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ImportSkeleton.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\KFMImporter.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\KFMImporter.h"
+				>
+			</File>
 			<File
 				RelativePath=".\MaxNifImport.cpp"
 				>
@@ -314,11 +344,23 @@
 				RelativePath=".\MaxNifImport.h"
 				>
 			</File>
+			<File
+				RelativePath=".\NIFImport.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\NIFImporter.h"
+				>
+			</File>
 			<File
 				RelativePath=".\niutils.cpp"
 				>
 			</File>
 		</Filter>
+		<File
+			RelativePath="..\MaxNifPlugins_Readme.txt"
+			>
+		</File>
 		<File
 			RelativePath=".\MaxNifTools.ini"
 			>
diff --git a/NifImport/MaxNifTools.ini b/NifImport/MaxNifTools.ini
index 2667f20bc5223edd7afd3412a3394f6dd839fc1a..c2b63fb5e163e571b90ac012d8e510c45ad2f504 100644
--- a/NifImport/MaxNifTools.ini
+++ b/NifImport/MaxNifTools.ini
@@ -44,15 +44,23 @@ BipedTrianglePelvis=0
 ; Remove unused bones from the biped on import of a mesh. Default: 1
 RemoveUnusedImportedBones=1
 ; Minimum Bone Width / Maximum Bone Width / Ratio of Width to Length
-MinBoneWidth=0.5
-MaxBoneWidth=3
-BoneWidthToLengthRatio=0.25
+MinBoneWidth=0.001
+MaxBoneWidth=0.1
+BoneWidthToLengthRatio=0.01
 ; Force nub to point back to parent at expense of loss of rotation data. Default: 1
-ForceRotation=1
+ForceRotation=0
 ; Browse for skeleton on import. Default: 1
 BrowseForSkeleton=1
 ; DefaultName for Skeletons (use if in same directory as imported nif)
 DefaultSkeletonName=skeleton.nif
+; Create Nubs for final bone in chain (not supported yet)
+CreateNubsForBones=1
+
+[AnimationImport]
+; Enable Animation Import.  Default: 1
+EnableAnimations=1
+; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations)
+ReplaceTCBRotationWithBezier=1
 
 ; [Applications]
 ; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from
diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ee9a21b3b8db278cf9e5a0f8f332a38c308da44a
--- /dev/null
+++ b/NifImport/NIFImport.cpp
@@ -0,0 +1,256 @@
+/**********************************************************************
+*<
+FILE: ImporterCore.cpp
+
+DESCRIPTION:	Core Import helper routines
+
+CREATED BY: tazpn (Theo)
+
+HISTORY: 
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+#include "stdafx.h"
+#include "MaxNifImport.h"
+
+using namespace Niflib;
+
+// Define the standard section names used in the ini file
+LPCTSTR NifImportSection = TEXT("MaxNifImport");
+LPCTSTR SystemSection = TEXT("System");
+LPCTSTR BipedImportSection = TEXT("BipedImport");
+LPCTSTR AnimImportSection = TEXT("AnimationImport");
+
+class IBipMaster;
+IBipMaster * (_cdecl * Max8CreateNewBiped)(float,float,class Point3 const &,int,int,int,int,int,int,int,int,int,int,int,int,float,int,int,int,int,int,int,int,int) = 0;
+
+NifImporter::NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
+   : BaseImporter()
+{
+   BaseInit(Name,I,GI,SuppressPrompts);
+}
+
+NifImporter::NifImporter()
+{
+}
+
+void NifImporter::ReadBlocks()
+{
+   //blocks = ReadNifList( name );
+   root = ReadNifTree(name);
+   BuildNodes();
+}
+
+
+static void BuildNodes(NiNodeRef object, vector<NiNodeRef>& nodes)
+{
+   if (!object)
+      return;
+   vector<NiNodeRef> links = DynamicCast<NiNode>(object->GetChildren());
+   nodes.insert(nodes.end(), links.begin(), links.end());
+   for (vector<NiNodeRef>::iterator itr = links.begin(), end = links.end(); itr != end; ++itr)
+      BuildNodes(*itr, nodes);
+}
+
+void NifImporter::BuildNodes()
+{
+   ::BuildNodes(root, nodes);
+   std::sort(nodes.begin(), nodes.end(), NodeEquivalence());
+}
+
+void NifImporter::Initialize()
+{
+   // Apply post processing checks after reading blocks
+   if (isValid()){
+      if (goToSkeletonBindPosition && !nodes.empty())
+         GoToSkeletonBindPosition(nodes);
+
+      // Only support biped if CreateNewBiped can be found.
+      useBiped &= (Max8CreateNewBiped != NULL);
+
+      hasSkeleton = HasSkeleton();
+      isBiped = IsBiped();
+      skeleton = (appSettings != NULL) ? appSettings->Skeleton : "";
+      importSkeleton = (appSettings != NULL) ? appSettings->useSkeleton : false;
+      importSkeleton &= hasSkeleton;
+
+      // Guess that the skeleton is the same one in the current directory
+      if (importSkeleton && !defaultSkeletonName.empty()) {
+         TCHAR buffer[MAX_PATH];
+         GetFullPathName(name.c_str(), _countof(buffer), buffer, NULL);
+         PathRemoveFileSpec(buffer);
+         PathAddBackslash(buffer);
+         PathAppend(buffer, defaultSkeletonName.c_str());
+         if (-1 != _taccess(buffer, 0))
+            skeleton = buffer;
+      }
+   }
+}
+
+void NifImporter::LoadIniSettings()
+{
+   TCHAR iniName[MAX_PATH];
+   LPCTSTR pluginDir = gi->GetDir(APP_PLUGCFG_DIR);
+   PathCombine(iniName, pluginDir, "MaxNifTools.ini");
+   this->iniFileName = iniName;
+   iniFileValid = (-1 != _access(iniName, 0));
+
+   // Locate which application to use. If Auto, find first app where this file appears in the root path list
+   appSettings = NULL;
+   string curapp = GetIniValue<string>(NifImportSection, "CurrentApp", "AUTO");
+   if (0 == _tcsicmp(curapp.c_str(), "AUTO")) {
+      // Scan Root paths
+      for (AppSettingsMap::iterator itr = TheAppSettings.begin(), end = TheAppSettings.end(); itr != end; ++itr){
+         if ((*itr).IsFileInRootPaths(this->name)) {
+            appSettings = &(*itr);
+            break;
+         }
+      }
+   } else {
+      appSettings = FindAppSetting(curapp);
+   }
+   if (appSettings == NULL && !TheAppSettings.empty()){
+      appSettings = &TheAppSettings.front();
+   }
+
+   useBiped = GetIniValue<bool>(NifImportSection, "UseBiped", false);
+   skeletonCheck = GetIniValue<string>(NifImportSection, "SkeletonCheck", "Bip*");
+   showTextures = GetIniValue<bool>(NifImportSection, "ShowTextures", true);
+
+   removeIllegalFaces = GetIniValue<bool>(NifImportSection, "RemoveIllegalFaces", true);
+   removeDegenerateFaces = GetIniValue<bool>(NifImportSection, "RemoveDegenerateFaces", true);
+   enableAutoSmooth = GetIniValue<bool>(NifImportSection, "EnableAutoSmooth", true);
+   autoSmoothAngle = GetIniValue<float>(NifImportSection, "AutoSmoothAngle", 30.0f);
+   flipUVTextures = GetIniValue<bool>(NifImportSection, "FlipUVTextures", true);
+   enableSkinSupport = GetIniValue<bool>(NifImportSection, "EnableSkinSupport", true);
+
+   bipedHeight = GetIniValue<float>(BipedImportSection, "BipedHeight", 131.90f);
+   bipedAngle = GetIniValue<float>(BipedImportSection, "BipedAngle", 90.0f);
+   bipedAnkleAttach = GetIniValue<float>(BipedImportSection, "BipedAnkleAttach", 0.2f);
+   bipedTrianglePelvis = GetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", false);
+   removeUnusedImportedBones = GetIniValue<bool>(BipedImportSection, "RemoveUnusedImportedBones", false);
+   forceRotation = GetIniValue<bool>(BipedImportSection, "ForceRotation", true);
+   browseForSkeleton = GetIniValue<bool>(BipedImportSection, "BrowseForSkeleton", true);
+   defaultSkeletonName = GetIniValue<string>(BipedImportSection, "DefaultSkeletonName", "Skeleton.Nif");
+   minBoneWidth = GetIniValue<float>(BipedImportSection, "MinBoneWidth", 0.5f);
+   maxBoneWidth = GetIniValue<float>(BipedImportSection, "MaxBoneWidth", 3.0f);
+   boneWidthToLengthRatio = GetIniValue<float>(BipedImportSection, "BoneWidthToLengthRatio", 0.25f);
+   createNubsForBones = GetIniValue<bool>(BipedImportSection, "CreateNubsForBones", true);
+
+   replaceTCBRotationWithBezier = GetIniValue<bool>(AnimImportSection, "ReplaceTCBRotationWithBezier", true);
+   enableAnimations = GetIniValue<bool>(AnimImportSection, "EnableAnimations", true);
+
+   goToSkeletonBindPosition = (appSettings ? appSettings->goToSkeletonBindPosition : false);
+}
+
+void NifImporter::SaveIniSettings()
+{
+   SetIniValue<bool>(NifImportSection, "UseBiped", useBiped);
+   SetIniValue<string>(NifImportSection, "Skeleton", skeleton);
+   SetIniValue<string>(NifImportSection, "SkeletonCheck", skeletonCheck);
+
+   SetIniValue<float>(BipedImportSection, "BipedHeight", bipedHeight);
+   SetIniValue<float>(BipedImportSection, "BipedAngle", bipedAngle);
+   SetIniValue<float>(BipedImportSection, "BipedAnkleAttach", bipedAnkleAttach);
+   SetIniValue<bool>(BipedImportSection, "BipedTrianglePelvis", bipedTrianglePelvis);
+}
+
+INode *NifImporter::GetNode(Niflib::NiNodeRef node)
+{
+   // may want to make this a map if its hit a lot
+   if (NULL == node) return NULL;
+   return gi->GetINodeByName(node->GetName().c_str());
+}
+
+bool NifImporter::DoImport()
+{
+   bool ok = true;
+   NiNodeRef rootNode = root;
+   if (isBiped)
+   {
+      if (useBiped){
+         ImportBipeds(nodes);
+      } else {
+         ImportBones(DynamicCast<NiNode>(rootNode->GetChildren()));
+      }
+   }
+   else
+   {
+      vector<string> importedBones;
+      if (importSkeleton)
+      {
+         if (browseForSkeleton)
+         {
+            TCHAR filter[64], *pfilter=filter;
+            pfilter = _tcscpy(filter, shortDescription.c_str());
+            pfilter = _tcscat(pfilter, " (*.NIF)");
+            pfilter += strlen(pfilter);
+            *pfilter++ = '\0';
+            _tcscpy(pfilter, "*.NIF");
+            pfilter += strlen(pfilter);
+            *pfilter++ = '\0';
+            *pfilter++ = '\0';
+
+            TCHAR filename[MAX_PATH];
+            GetFullPathName(skeleton.c_str(), _countof(filename), filename, NULL);
+
+            OPENFILENAME ofn;
+            memset(&ofn, 0, sizeof(ofn));
+            ofn.lStructSize = sizeof(ofn);
+            ofn.hwndOwner = gi->GetMAXHWnd();
+            ofn.lpstrFilter = filter;
+            ofn.lpstrFile = filename;
+            ofn.nMaxFile = _countof(filename);
+            ofn.lpstrTitle = TEXT("Browse for Skeleton NIF...");
+            ofn.lpstrDefExt = TEXT("NIF");
+            ofn.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST;
+            importSkeleton = GetOpenFileName(&ofn) ? true : false;
+            if (importSkeleton) {
+               skeleton = filename;
+            }
+         }
+         if (importSkeleton && !skeleton.empty()) {
+            NifImporter skelImport(skeleton.c_str(), i, gi, suppressPrompts);
+            if (skelImport.isValid())
+            {
+               if (skelImport.useBiped){
+                  skelImport.ImportBipeds(skelImport.nodes);
+               } else {
+                  NiNodeRef skelRoot = skelImport.root;
+                  skelImport.ImportBones(DynamicCast<NiNode>(skelRoot->GetChildren()));
+                  if (removeUnusedImportedBones){
+                     importedBones = GetNamesOfNodes(skelImport.nodes);
+                  }
+               }
+            }
+         }
+      } else if (hasSkeleton && useBiped) {
+         ImportBipeds(nodes);
+      }
+
+      if (isValid()) {
+         ImportBones(DynamicCast<NiNode>(rootNode->GetChildren()));
+         ok = ImportMeshes(rootNode);
+
+         if (importSkeleton && removeUnusedImportedBones){
+            vector<string> importedNodes = GetNamesOfNodes(nodes);
+            sort(importedBones.begin(), importedBones.end());
+            sort(importedNodes.begin(), importedNodes.end());
+            vector<string> results;
+            results.resize(importedBones.size());
+            vector<string>::iterator end = set_difference ( 
+               importedBones.begin(), importedBones.end(),
+               importedNodes.begin(), importedNodes.end(), results.begin());
+            for (vector<string>::iterator itr = results.begin(); itr != end; ++itr){
+               if (INode *node = gi->GetINodeByName((*itr).c_str())){
+                  node->Delete(0, TRUE);
+               }
+            }
+         }
+      }
+   }
+
+   // Kick of animation import
+   ImportAnimation();
+   return true;
+}
\ No newline at end of file
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
new file mode 100644
index 0000000000000000000000000000000000000000..2bec037bc4c09282aaf542c87c0379585724519d
--- /dev/null
+++ b/NifImport/NIFImporter.h
@@ -0,0 +1,103 @@
+/**********************************************************************
+*<
+FILE: NifImporter.h
+
+DESCRIPTION:	NIF Importer 
+
+CREATED BY: tazpn (Theo)
+
+HISTORY:
+
+*>	Copyright (c) 2006, All Rights Reserved.
+**********************************************************************/
+
+#ifndef __NIFIMPORTER_H__
+#define __NIFIMPORTER_H__
+
+#include "BaseImporter.h"
+// NIF Importer
+class NifImporter : public BaseImporter
+{
+public:
+   // Ini settings
+   bool showTextures; // show textures in viewport
+   bool removeIllegalFaces;
+   bool removeDegenerateFaces;
+   bool enableAutoSmooth;
+   float autoSmoothAngle;
+   bool flipUVTextures;
+   bool enableSkinSupport;
+   bool goToSkeletonBindPosition;
+
+   // Biped/Bones related settings
+   string skeleton;
+   float bipedHeight;
+   string skeletonCheck;
+   float bipedAngle;
+   float bipedAnkleAttach;
+   bool bipedTrianglePelvis;
+   bool importSkeleton;
+   bool useBiped;
+   bool hasSkeleton;
+   bool isBiped;
+   bool removeUnusedImportedBones;
+   bool forceRotation;
+   bool browseForSkeleton;
+   string defaultSkeletonName;
+   float minBoneWidth;
+   float maxBoneWidth;
+   float boneWidthToLengthRatio;
+   bool createNubsForBones;
+
+   // Animation related Settings
+   bool replaceTCBRotationWithBezier;
+   bool enableAnimations;
+
+   vector<Niflib::NiObjectRef> blocks;
+   vector<Niflib::NiNodeRef> nodes;
+
+   NifImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts);
+   virtual void Initialize();
+   virtual void ReadBlocks();
+   void BuildNodes();
+
+   // Ini File related routines
+   void LoadIniSettings();
+   void SaveIniSettings();
+
+   bool HasSkeleton();
+   bool IsBiped();
+   void ImportBones(vector<Niflib::NiNodeRef>& bones);
+   void ImportBones(Niflib::NiNodeRef blocks);
+   void ImportBipeds(vector<Niflib::NiNodeRef>& blocks);
+   void AlignBiped(IBipMaster* master, Niflib::NiNodeRef block);
+   void PositionBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
+   void RotateBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
+   void ScaleBiped(IBipMaster* master, Niflib::NiNodeRef block, bool Recurse = false);
+   bool ImportMeshes(Niflib::NiNodeRef block);
+   string FindImage(const string& name);
+
+   void SetTrangles(Mesh& mesh, vector<Niflib::Triangle>& v, bool hasTexture);
+   bool ImportMesh(Niflib::NiTriShapeRef triShape);
+   bool ImportMesh(Niflib::NiTriStripsRef triStrips);
+   bool ImportMaterialAndTextures(ImpNode *node, Niflib::NiAVObjectRef avObject);
+   bool ImportTransform(ImpNode *node, Niflib::NiAVObjectRef avObject);
+   bool ImportMesh(ImpNode *node, TriObject *o, Niflib::NiTriBasedGeomRef triGeom, Niflib::NiTriBasedGeomDataRef triGeomData, bool hasTexture);
+
+   bool ImportSkin(ImpNode *node, Niflib::NiTriBasedGeomRef triGeom);
+   Texmap* CreateTexture(Niflib::TexDesc& desc);
+   INode *CreateBone(const string& name, Point3 startPos, Point3 endPos, Point3 zAxis);
+   INode *CreateHelper(const string& name, Point3 startPos);
+
+   INode *GetNode(Niflib::NiNodeRef node);
+
+   virtual bool DoImport();
+
+   // Animation Helpers
+   bool ImportAnimation();
+
+   protected: 
+      NifImporter();
+};
+
+#endif
diff --git a/NifImport/Staging/plugcfg/MaxNifTools.ini b/NifImport/Staging/plugcfg/MaxNifTools.ini
index 2667f20bc5223edd7afd3412a3394f6dd839fc1a..c2b63fb5e163e571b90ac012d8e510c45ad2f504 100644
--- a/NifImport/Staging/plugcfg/MaxNifTools.ini
+++ b/NifImport/Staging/plugcfg/MaxNifTools.ini
@@ -44,15 +44,23 @@ BipedTrianglePelvis=0
 ; Remove unused bones from the biped on import of a mesh. Default: 1
 RemoveUnusedImportedBones=1
 ; Minimum Bone Width / Maximum Bone Width / Ratio of Width to Length
-MinBoneWidth=0.5
-MaxBoneWidth=3
-BoneWidthToLengthRatio=0.25
+MinBoneWidth=0.001
+MaxBoneWidth=0.1
+BoneWidthToLengthRatio=0.01
 ; Force nub to point back to parent at expense of loss of rotation data. Default: 1
-ForceRotation=1
+ForceRotation=0
 ; Browse for skeleton on import. Default: 1
 BrowseForSkeleton=1
 ; DefaultName for Skeletons (use if in same directory as imported nif)
 DefaultSkeletonName=skeleton.nif
+; Create Nubs for final bone in chain (not supported yet)
+CreateNubsForBones=1
+
+[AnimationImport]
+; Enable Animation Import.  Default: 1
+EnableAnimations=1
+; Replace TCB Rotation with Bezier (workaround for unexpected behavior with TCB rotations)
+ReplaceTCBRotationWithBezier=1
 
 ; [Applications]
 ; RootPaths - Semicolon separated list of base directories to use when determining which app the imported file is from
diff --git a/NifImport/Staging/plugins/MaxNifImport.dli b/NifImport/Staging/plugins/MaxNifImport.dli
index 443d6d01b59fb740a6b2eabaed571a697786bc1a..b9316bfd64e32b4dadd5e7285510553699794aa2 100644
Binary files a/NifImport/Staging/plugins/MaxNifImport.dli and b/NifImport/Staging/plugins/MaxNifImport.dli differ
diff --git a/NifImport/niutils.cpp b/NifImport/niutils.cpp
index 52353b86788966fb6ce912f9235861623cbce087..8b94298dbb80cb64c6874a6418e32ac2708e3fbb 100644
--- a/NifImport/niutils.cpp
+++ b/NifImport/niutils.cpp
@@ -306,61 +306,20 @@ void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName)
    if (node != NULL) node->SetName(const_cast<LPTSTR>(DstName));
 }
 
-// Locate a TriObject in an Object if it exists
-TriObject* GetTriObject(Object *o)
+void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs, TimeValue t)
 {
-   if (o && o->CanConvertToType(triObjectClassID))
-      return (TriObject *)o->ConvertToType(0, triObjectClassID);
-   while (o->SuperClassID() == GEN_DERIVOB_CLASS_ID && o)
-   {
-      IDerivedObject* dobj = (IDerivedObject *)(o);
-      o = dobj->GetObjRef();
-      if (o && o->CanConvertToType(triObjectClassID))
-         return (TriObject *)o->ConvertToType(0, triObjectClassID);
-   }
-   return NULL;
-}
-
-// Get or Create the Skin Modifier
-Modifier *GetSkin(INode *node)
-{
-   Object* pObj = node->GetObjectRef();
-   if (!pObj) return NULL;
-   while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
-   {
-      IDerivedObject* pDerObj = (IDerivedObject *)(pObj);
-      int Idx = 0;
-      while (Idx < pDerObj->NumModifiers())
-      {
-         // Get the modifier. 
-         Modifier* mod = pDerObj->GetModifier(Idx);
-         if (mod->ClassID() == SKIN_CLASSID)
-         {
-            // is this the correct Physique Modifier based on index?
-            return mod;
-         }
-         Idx++;
-      }
-      pObj = pDerObj->GetObjRef();
-   }
-
-   IDerivedObject *dobj = CreateDerivedObject(node->GetObjectRef());
-
-   //create a skin modifier and add it
-   Modifier *skinMod = (Modifier*) CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
-   dobj->SetAFlag(A_LOCK_TARGET);
-   dobj->AddModifier(skinMod);
-   dobj->ClearAFlag(A_LOCK_TARGET);
-   node->SetObjectRef(dobj);
-   return skinMod;
+   Point3 p = m3.GetTrans();
+   Quat q = m3;
+   PosRotScaleNode(n, p, q, 1.0f, prs, t);
 }
 
 // Set Position and Rotation on a standard controller will need to handle bipeds
 //   Always in World Transform coordinates
-void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeValue t)
+void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs, TimeValue t)
 {
    if (Control *c = n->GetTMController()) {
 
+      ScaleValue sv(Point3(s,s,s));
       // Bipeds are special.  And will crash if you dont treat them with care
       if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) 
          ||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID) 
@@ -369,6 +328,8 @@ void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeVal
          // Get the Biped Export Interface from the controller 
          //IBipedExport *BipIface = (IBipedExport *) c->GetInterface(I_BIPINTERFACE);
          IOurBipExport *BipIface = (IOurBipExport *) c->GetInterface(I_OURINTERFACE);
+         if (prs & prsScale)
+            BipIface->SetBipedScale(sv, t, n);
          if (prs & prsRot)
             BipIface->SetBipedRotation(q, t, n, 0/*???*/);
          if (prs & prsPos)
@@ -376,6 +337,9 @@ void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs, TimeVal
       }
       else
       {
+         if (prs & prsScale)
+            if (Control *sclCtrl = c->GetScaleController())
+               sclCtrl->SetValue(t, &sv, 1, CTRL_ABSOLUTE);
          if (prs & prsRot)
             if (Control *rotCtrl = c->GetRotationController())
                rotCtrl->SetValue(t, &q, 1, CTRL_ABSOLUTE);
@@ -516,13 +480,3 @@ void FindImages(NameValueCollection& images, const string& rootPath, const strin
 }
 
 
-void GoToSkeletonBindPosition(vector<NiNodeRef>& blocks)
-{
-   //Send all skeleton roots to bind position
-   for (uint i = 0; i < blocks.size(); ++i) {
-   	NiNodeRef node = blocks[i];
-   	if ( node != NULL && node->IsSkeletonRoot() ) {
-   		node->GoToSkeletonBindPosition();
-   	}
-   }
-}
\ No newline at end of file
diff --git a/NifImport/niutils.h b/NifImport/niutils.h
index 2458ee3c5f5d1ae3992d9a650b4c254d03f6c752..b52b713728411b4deb9cdccb06281cd6ca4d0beb 100644
--- a/NifImport/niutils.h
+++ b/NifImport/niutils.h
@@ -31,6 +31,9 @@ INFO: See Implementation for minimalist comments
 #include <color.h>
 
 // Niflib Headers
+#include <obj\NiObject.h>
+#include <obj\NiAVObject.h>
+#include <obj\NiObjectNET.h>
 #include <obj\NiNode.h>
 #include <nif_math.h>
 
@@ -192,8 +195,6 @@ extern string ExpandEnvironment(const string& src);
 extern void FindImages(NameValueCollection& images, const string& rootPath, const stringlist& searchpaths, const stringlist& extensions);
 
 extern void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName);
-extern TriObject* GetTriObject(Object *o);
-extern Modifier *GetSkin(INode *node);
 
 enum PosRotScale
 {
@@ -202,13 +203,37 @@ enum PosRotScale
    prsScale = 0x4,
    prsDefault = prsPos | prsRot | prsScale,
 };
-extern void PositionAndRotateNode(INode *n, Point3 p, Quat& q, PosRotScale prs = prsDefault, TimeValue t = 0);
+extern void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs = prsDefault, TimeValue t = 0);
+extern void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs = prsDefault, TimeValue t = 0);
 
 extern Niflib::NiNodeRef FindNodeByName( const vector<Niflib::NiNodeRef>& blocks, const string& name );
 extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match);
 extern int CountNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match );
 extern std::vector<std::string> GetNamesOfNodes( const vector<Niflib::NiNodeRef>& blocks );
-extern void GoToSkeletonBindPosition(std::vector<Niflib::NiNodeRef>& blocks);
+extern std::vector<Niflib::NiNodeRef> SelectNodesByName( const vector<Niflib::NiNodeRef>& blocks, LPCTSTR match);
+
+struct NodeEquivalence
+{
+   bool operator()(const Niflib::NiNodeRef& lhs, const Niflib::NiNodeRef& rhs) const{
+      return (!lhs || !rhs) ? (lhs < rhs) : (lhs->GetName() < rhs->GetName());
+   }
+   bool operator()(const Niflib::NiNodeRef& lhs, const std::string& rhs) const{
+      return (lhs->GetName() < rhs);
+   }
+   bool operator()(const std::string& lhs, const Niflib::NiNodeRef& rhs) const{
+      return (lhs < rhs->GetName());
+   }
+};
+
+inline Niflib::NiNodeRef BinarySearch(vector<Niflib::NiNodeRef> &nodes, const string& name)
+{
+   typedef std::pair<vector<Niflib::NiNodeRef>::iterator, vector<Niflib::NiNodeRef>::iterator> NiNodePair;
+   NiNodePair pair = std::equal_range(nodes.begin(), nodes.end(), name, NodeEquivalence());
+   if (pair.first != pair.second) {
+      return (*pair.first);
+   }
+   return Niflib::NiNodeRef();
+}
 
 // Simple conversion helpers
 static inline float TODEG(float x) { return x * 180.0f / PI; }
@@ -218,7 +243,7 @@ static inline Color TOCOLOR(const Niflib::Color3& c3) {
    return Color(c3.r, c3.g, c3.b);
 }
 
-static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = true){
+static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false){
    Niflib::Vector3 pos; Niflib::Matrix33 rot; float scale;
    tm.Decompose(pos, rot, scale);
    Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3());
@@ -227,8 +252,39 @@ static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = true){
    return m;
 }
 
-static inline Quat TOQUAT(const Niflib::Quaternion& q){
-   return Quat(q.x, q.y, q.z, q.w);
+
+static inline Point3 TOPOINT3(const Niflib::Vector3& v){
+   return Point3(v.x, v.y, v.z);
+}
+
+static inline Quat TOQUAT(const Niflib::Quaternion& q, bool inverse = false){
+   Quat qt(q.x, q.y, q.z, q.w);
+   return (inverse) ? qt.Inverse() : qt;
+}
+
+static inline AngAxis TOANGAXIS(const Niflib::Quaternion& q, bool inverse = false){
+   Quat qt(q.x, q.y, q.z, q.w);
+   if (inverse) qt.Invert();
+   return AngAxis(q.x, q.y, q.z, q.w);
+}
+
+
+template <typename U, typename T>
+inline Niflib::Ref<U> SelectFirstObjectOfType( vector<Niflib::Ref<T> > const & objs ) {
+   for (vector<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) {
+      Niflib::Ref<U> obj = DynamicCast<U>(*itr);
+      if (obj) return obj;
+   }
+   return Niflib::Ref<U>();
+}
+
+template <typename U, typename T>
+inline Niflib::Ref<U> SelectFirstObjectOfType( list<Niflib::Ref<T> > const & objs ) {
+   for (list<Niflib::Ref<T> >::const_iterator itr = objs.begin(), end = objs.end(); itr != end; ++itr) {
+      Niflib::Ref<U> obj = DynamicCast<U>(*itr);
+      if (obj) return obj;
+   }
+   return Niflib::Ref<U>();
 }
 
 #endif // _NIUTILS_H_
\ No newline at end of file
diff --git a/NifImport/objectParams.h b/NifImport/objectParams.h
index d0367e6f0e28ba5b42ba0fb9ba8db7220d3ef306..faa7c15cb4b77adac8948ae010edf6153e84f1e8 100644
--- a/NifImport/objectParams.h
+++ b/NifImport/objectParams.h
@@ -190,7 +190,7 @@ bool getMAXScriptValue(ReferenceTarget* obj, LPTSTR name, TimeValue t, T& value)
 }
 
 // Get the parameter controller
-Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimension*& dim)
+inline Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimension*& dim)
 {
 	Control* rval = NULL;
 	assert(obj != NULL);
@@ -217,7 +217,7 @@ Control* getMAXScriptController(ReferenceTarget* obj, LPTSTR name, ParamDimensio
 }
 
 // Set the parameter controller
-bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* control, ParamDimension* dim)
+inline bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* control, ParamDimension* dim)
 {
 	bool rval = false;
 	assert(obj != NULL);
diff --git a/NifImport/stdafx.h b/NifImport/stdafx.h
index 9f724ac266426cd0f9fe74c0a6f68ca963e1b76e..5b53f29d025cbac02581dc56aa11e6a98e06cb93 100644
--- a/NifImport/stdafx.h
+++ b/NifImport/stdafx.h
@@ -21,8 +21,4 @@
 #include <commctrl.h>
 #include <shlwapi.h>
 
-#include "Max.h"
-#include "resource.h"
-#include "istdplug.h"
-#include "iparamb2.h"
-#include "iparamm2.h"
+#include "MaxNifImport.h"