diff --git a/MaxNifPlugins_Readme.txt b/MaxNifPlugins_Readme.txt
index dcf265b09e7f2ecddbf14ec5307ed6f76d69984d..810cd274d867a31ef9ab860ae796c5418dc79003 100644
--- a/MaxNifPlugins_Readme.txt
+++ b/MaxNifPlugins_Readme.txt
@@ -1,4 +1,4 @@
-                        MaxPlugins 0.2.2
+                        MaxPlugins 0.2.3
                         ================
 
  
@@ -32,9 +32,18 @@
     o Exporter
       - Fix issue when exporting a mesh with a skin modifier that had bones
          that were not used.
+      - Fix bug with normals introduced in 0.2.2 when fixing previous problem
+      - Changed code to scale the mesh so that Reset XForm is not required
+      - Added support for Bone LOD code.  Works with Civ4 Bone LOD Manager.
+      - Added support for Lights and Cameras
       
     o Importer
       - Alter code to create Camera nodes when name begins with Camera.
+      - Initial pass at Lights
+      - Fixed issues with skin modifier on Civ4 Units and Leaderheads
+      - Added support for Bone LOD code.  Works with Civ4 Bone LOD Manager.
+      - Added support for Lights and Cameras
+      - Fixed issues with Textures with muliple UV Sets      
     
       0.2.2
       -----
diff --git a/MaxNifTools.ini b/MaxNifTools.ini
index e63efe16c4aba8548ac1021a6d3d207833a07ff3..f7d60decc18e5d43dbe610a223d29861d5aec5ff 100644
--- a/MaxNifTools.ini
+++ b/MaxNifTools.ini
@@ -74,6 +74,10 @@ EnableCollision=1
 VertexColorMode=1
 ; Merge NonAccum transforms into base node. Default: 1
 MergeNonAccum=1
+; Import Lights. Default: 0
+Lights=0
+; Import Cameras. Default: 0
+Cameras=0
 
 [BipedImport]
 ; Top level bone import setting. Default:1
diff --git a/NifCommon/AnimKey.h b/NifCommon/AnimKey.h
index cc66775f5ca0b3610ceaedb6b186b5ac7cd613a8..1cb3462565c993e4d696429b80a3ac51184b4a1b 100644
--- a/NifCommon/AnimKey.h
+++ b/NifCommon/AnimKey.h
@@ -33,7 +33,7 @@ inline float FrameToTime(TimeValue t) {
 typedef Key<float> FloatKey;
 typedef Key<Quaternion> QuatKey;
 typedef Key<Vector3> Vector3Key;
-
+typedef Key<string> StringKey;
 
 template<typename T, typename U> T MapKey(U& key, float time);
 
diff --git a/NifCommon/AppSettings.cpp b/NifCommon/AppSettings.cpp
index efb8b9c9ab6cbdde700f327a4554aa36801eab97..17d4fbba2b0c832adeb2e607b429c3906bd1d499 100644
--- a/NifCommon/AppSettings.cpp
+++ b/NifCommon/AppSettings.cpp
@@ -62,6 +62,7 @@ void AppSettings::ReadSettings(string iniFile)
    goToSkeletonBindPosition = GetSetting<bool>("GoToSkeletonBindPosition", goToSkeletonBindPosition);
    disableCreateNubsForBones = GetSetting<bool>("DisableCreateNubsForBones", disableCreateNubsForBones);
    applyOverallTransformToSkinAndBones = GetSetting<int>("ApplyOverallTransformToSkinAndBones", -1);
+   textureUseFullPath = GetSetting<bool>("TextureUseFullPath", textureUseFullPath);
 
    dummyNodeMatches = TokenizeString(GetSetting<string>("DummyNodeMatches").c_str(), ";");
 }
@@ -144,6 +145,11 @@ bool AppSettings::IsFileInRootPaths(const std::string& fname)
 std::string AppSettings::GetRelativeTexPath(const std::string& fname, const std::string& prefix)
 {
    TCHAR buffer[MAX_PATH];
+   if (textureUseFullPath)
+   {
+      GetFullPathName(fname.c_str(), _countof(buffer), buffer, NULL);
+      return string(buffer);
+   }
    if (!PathIsRelative(fname.c_str())) 
    {
       TCHAR root[MAX_PATH];
diff --git a/NifCommon/AppSettings.h b/NifCommon/AppSettings.h
index 3e009eec0add25fbc42bd39a6795a13506c15810..87d93ad389f5b13fbd1f1a1383ab83dc217388be 100644
--- a/NifCommon/AppSettings.h
+++ b/NifCommon/AppSettings.h
@@ -25,6 +25,7 @@ public:
       , useSkeleton(false)
       , goToSkeletonBindPosition(true)
       , disableCreateNubsForBones(false)
+      , textureUseFullPath(false)
    {}
 
    std::string Name;
@@ -38,6 +39,7 @@ public:
    bool useSkeleton;
    bool goToSkeletonBindPosition;
    bool disableCreateNubsForBones;
+   bool textureUseFullPath;
    NameValueCollection Environment;
    NameValueCollection imgTable;
    stringlist dummyNodeMatches;
diff --git a/NifCommon/MAX_Mem.h b/NifCommon/MAX_Mem.h
new file mode 100644
index 0000000000000000000000000000000000000000..6cfc773cf57a2d96788c4c1df341b2f42eb7d39f
--- /dev/null
+++ b/NifCommon/MAX_Mem.h
@@ -0,0 +1,1010 @@
+/**********************************************************************
+ *<
+	FILE: MAX_Mem.h
+
+	DESCRIPTION:
+
+	CREATED BY: Michaelson Britt
+
+	HISTORY:
+
+ *>	Copyright (c) 2002, All Rights Reserved.
+ **********************************************************************/
+
+
+#ifndef __MAX_MEM_H
+#define __MAX_MEM_H
+
+#if (_MSC_VER >= 1300)  // Visual Studio .NET
+
+
+#include <malloc.h>
+#include <new.h>
+//Notes:
+//- No handling for new/delete "placement form" operators
+//- The Microsoft documentation claims that the functions _heapadd(), _heapchk() etc.
+//  are supported only under WinNT
+
+
+//FIXME? should we wrap the header files for these functions?
+
+
+//CodeExport void *__cdecl MAX_new_placement(size_t,void*);
+//CodeExport void __cdecl MAX_delete_placement(void*,void*);
+
+//__forceinline void* operator new(size_t size, void* _P)
+//{
+//	return MAX_new_placement(size,_P);
+//}
+
+//__forceinline void operator delete(void* memblock, void* _P)
+//{
+//	MAX_delete_placement(memblock,_P);
+//}
+
+//Allocate block of memory from heap 
+//_CRTIMP void * __cdecl malloc(size_t);
+CoreExport void *	(__cdecl *MAX_malloc)(size_t);
+#define malloc(size)	(*MAX_malloc)(size)
+
+//Allocate storage for array, initializing every byte in allocated block to 0 
+//_CRTIMP void * __cdecl calloc(size_t, size_t);
+CoreExport void *	(__cdecl *MAX_calloc)(size_t, size_t);
+#define calloc(num,size)	(*MAX_calloc)(num,size)
+
+//Reallocate block to new size 
+//_CRTIMP void * __cdecl realloc(void *, size_t);
+CoreExport void *	(__cdecl *MAX_realloc)(void *, size_t);
+#define realloc(memblock,size)	(*MAX_realloc)(memblock,size)
+
+//Expand or shrink block of memory without moving it 
+//_CRTIMP void *  __cdecl _expand(void *, size_t);
+CoreExport void *	(__cdecl *MAX_expand)(void *, size_t);
+#define _expand(memblock,size)	(*MAX_expand)(memblock,size)
+
+//Free allocated block 
+//_CRTIMP void   __cdecl free(void *);
+CoreExport void	(__cdecl *MAX_free)(void *);
+#define free(memblock)	(*MAX_free)(memblock)
+
+//Return size of allocated block 
+//_CRTIMP size_t  __cdecl _msize(void *);
+CoreExport size_t	(__cdecl *MAX_msize)(void *);
+#define _msize(memblock)	(*MAX_msize)(memblock)
+
+// Set hook function 
+//_CRTIMP _HEAPHOOK __cdecl _setheaphook(_HEAPHOOK);
+//CoreExport _HEAPHOOK (__cdecl *MAX_setheaphook)(_HEAPHOOK);
+//Warning! Disabled because HEAPHOOK does not seem to be enabled in practice
+//#define MAX_setheaphook(_HEAPHOOK)	(*MAX_setheaphook)(_HEAPHOOK)
+
+//Add memory to heap 
+//_CRTIMP int     __cdecl _heapadd(void *, size_t);
+CoreExport int	(__cdecl *MAX_heapadd)(void *, size_t);
+#define _heapadd(memblock,size)	(*MAX_heapadd)(memblock,size)
+
+//Check heap for consistency 
+//_CRTIMP int     __cdecl _heapchk(void);
+CoreExport int	(__cdecl *MAX_heapchk)(void);
+#define _heapchk()	(*MAX_heapchk)()
+
+//Release unused memory in heap 
+//_CRTIMP int     __cdecl _heapmin(void);
+CoreExport int	(__cdecl *MAX_heapmin)(void);
+#define _heapmin()	(*MAX_heapmin)()
+
+//Fill free heap entries with specified value 
+//_CRTIMP int     __cdecl _heapset(unsigned int);
+CoreExport int	(__cdecl *MAX_heapset)(unsigned int);
+#define _heapset(fill)	(*MAX_heapset)(fill)
+
+//Return information about each entry in heap 
+//_CRTIMP int     __cdecl _heapwalk(_HEAPINFO *);
+CoreExport int	(__cdecl *MAX_heapwalk)(_HEAPINFO *);
+#define _heapwalk(entryinfo)	(*MAX_heapwalk)(entryinfo)
+
+//Return address of current new handler routine as set by _set_new_handler 
+//_CRTIMP _PNH __cdecl _query_new_handler( void );
+CoreExport _PNH	(__cdecl *MAX_query_new_handler)( void );
+#define _query_new_handler()	(*MAX_query_new_handler)()
+
+//Enable error-handling mechanism when new operator fails (to allocate memory) and enable compilation of Standard Template Libraries (STL) 
+//_CRTIMP _PNH __cdecl _set_new_handler( _PNH );
+CoreExport _PNH	(__cdecl *MAX_set_new_handler)( _PNH );
+#define _set_new_handler(pNewHandler)	(*MAX_set_new_handler)(pNewHandler)
+
+//Return integer indicating new handler mode set by _set_new_mode for malloc 
+//_CRTIMP int __cdecl _query_new_mode( void );
+CoreExport int	(__cdecl *MAX_query_new_mode)( void );
+#define _query_new_mode()	(*MAX_query_new_mode)()
+
+//Set new handler mode for malloc 
+//_CRTIMP int __cdecl _set_new_mode( int );
+CoreExport int	(__cdecl *MAX_set_new_mode)( int );
+#define _set_new_mode(newhandlermode)	(*MAX_set_new_mode)(newhandlermode)
+
+//Get/Set the upper limit for the size of a memory allocation that will be supported by the small-block heap 
+//_CRTIMP size_t  __cdecl _get_sbh_threshold(void);
+//_CRTIMP int     __cdecl _set_sbh_threshold(size_t);
+CoreExport size_t	(__cdecl *MAX_get_sbh_threshold)(void);
+#define _get_sbh_threshold()	(*MAX_get_sbh_threshold)()
+
+CoreExport int	(__cdecl *MAX_set_sbh_threshold)(size_t);
+#define _set_sbh_threshold(size)	(*MAX_set_sbh_threshold)(size)
+
+
+//Allocate memory from stack
+//NOTE: no implementation needed.  Only heap allocation causes a problem
+//void *          __cdecl _alloca(size_t);
+//CoreExport void *          __cdecl MAX_alloca(size_t);
+//#define _alloca(size)	MAX_alloca(size)
+
+
+
+/***
+*
+* MAX_Mem wrapper for Microsoft crtdbg.h geader file
+*
+****/
+
+
+/***
+*crtdbg.h - Supports debugging features of the C runtime library.
+*
+*       Copyright (c) 1994-2001, Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+*       Support CRT debugging features.
+*
+*       [Public]
+*
+****/
+
+//#if (_MSC_VER >= 1300)  // Visual Studio .NET
+
+
+//#if     _MSC_VER > 1000
+//#pragma once
+//#endif
+
+#if     !defined(_WIN32)
+#error ERROR: Only Win32 target supported!
+#endif
+
+
+
+CoreExport void *__cdecl MAX_new(size_t size);
+CoreExport void __cdecl MAX_delete(void* mem);
+
+__forceinline void* operator new(size_t size)
+{
+	return MAX_new(size);
+}
+
+__forceinline void operator delete(void* memblock)
+{
+	MAX_delete(memblock);
+}
+
+
+
+ /****************************************************************************
+ *
+ * CRTDBG.H Support
+ *
+ ***************************************************************************/
+
+
+#ifdef  __cplusplus
+//extern "C" {
+#endif  /* __cplusplus */
+
+ /****************************************************************************
+ *
+ * Constants and types
+ *
+ ***************************************************************************/
+
+#if !defined(_W64)
+#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#define _W64 __w64
+#else
+#define _W64
+#endif
+#endif
+
+#ifndef _SIZE_T_DEFINED
+#ifdef  _WIN64
+typedef unsigned __int64    size_t;
+#else
+typedef _W64 unsigned int   size_t;
+#endif
+#define _SIZE_T_DEFINED
+#endif
+
+/* Define NULL pointer value */
+
+#ifndef NULL
+#ifdef  __cplusplus
+#define NULL    0
+#else
+#define NULL    ((void *)0)
+#endif
+#endif
+
+ /****************************************************************************
+ *
+ * Debug Reporting
+ *
+ ***************************************************************************/
+
+typedef void *_HFILE; /* file handle pointer */
+
+#define _CRT_WARN           0
+#define _CRT_ERROR          1
+#define _CRT_ASSERT         2
+#define _CRT_ERRCNT         3
+
+#define _CRTDBG_MODE_FILE      0x1
+#define _CRTDBG_MODE_DEBUG     0x2
+#define _CRTDBG_MODE_WNDW      0x4
+#define _CRTDBG_REPORT_MODE    -1
+
+#define _CRTDBG_INVALID_HFILE ((_HFILE)-1)
+#define _CRTDBG_HFILE_ERROR   ((_HFILE)-2)
+#define _CRTDBG_FILE_STDOUT   ((_HFILE)-4)
+#define _CRTDBG_FILE_STDERR   ((_HFILE)-5)
+#define _CRTDBG_REPORT_FILE   ((_HFILE)-6)
+
+typedef int (__cdecl * _CRT_REPORT_HOOK)(int, char *, int *);
+
+#define _CRT_RPTHOOK_INSTALL  0
+#define _CRT_RPTHOOK_REMOVE   1
+
+ /****************************************************************************
+ *
+ * Heap
+ *
+ ***************************************************************************/
+
+
+
+ /****************************************************************************
+ *
+ * Heap
+ *
+ ***************************************************************************/
+
+ /****************************************************************************
+ *
+ * Client-defined allocation hook
+ *
+ ***************************************************************************/
+
+#define _HOOK_ALLOC     1
+#define _HOOK_REALLOC   2
+#define _HOOK_FREE      3
+
+typedef int (__cdecl * _CRT_ALLOC_HOOK)(int, void *, size_t, int, long, const unsigned char *, int);
+
+ /****************************************************************************
+ *
+ * Memory management
+ *
+ ***************************************************************************/
+
+/*
+ * Bit values for _crtDbgFlag flag:
+ *
+ * These bitflags control debug heap behavior.
+ */
+
+#define _CRTDBG_ALLOC_MEM_DF        0x01  /* Turn on debug allocation */
+#define _CRTDBG_DELAY_FREE_MEM_DF   0x02  /* Don't actually free memory */
+#define _CRTDBG_CHECK_ALWAYS_DF     0x04  /* Check heap every alloc/dealloc */
+#define _CRTDBG_RESERVED_DF         0x08  /* Reserved - do not use */
+#define _CRTDBG_CHECK_CRT_DF        0x10  /* Leak check/diff CRT blocks */
+#define _CRTDBG_LEAK_CHECK_DF       0x20  /* Leak check at program exit */
+
+/*
+ * Some bit values for _crtDbgFlag which correspond to frequencies for checking
+ * the the heap.
+ */
+#define _CRTDBG_CHECK_EVERY_16_DF   0x00100000  /* check heap every 16 heap ops */
+#define _CRTDBG_CHECK_EVERY_128_DF  0x00800000  /* check heap every 128 heap ops */
+#define _CRTDBG_CHECK_EVERY_1024_DF 0x04000000  /* check heap every 1024 heap ops */
+#define _CRTDBG_CHECK_DEFAULT_DF    _CRTDBG_CHECK_EVERY_1024_DF
+
+#define _CRTDBG_REPORT_FLAG         -1    /* Query bitflag status */
+
+#define _BLOCK_TYPE(block)          (block & 0xFFFF)
+#define _BLOCK_SUBTYPE(block)       (block >> 16 & 0xFFFF)
+
+
+ /****************************************************************************
+ *
+ * Memory state
+ *
+ ***************************************************************************/
+
+/* Memory block identification */
+#define _FREE_BLOCK      0
+#define _NORMAL_BLOCK    1
+#define _CRT_BLOCK       2
+#define _IGNORE_BLOCK    3
+#define _CLIENT_BLOCK    4
+#define _MAX_BLOCKS      5
+
+typedef void (__cdecl * _CRT_DUMP_CLIENT)(void *, size_t);
+
+struct _CrtMemBlockHeader;
+typedef struct _CrtMemState
+{
+        struct _CrtMemBlockHeader * pBlockHeader;
+        size_t lCounts[_MAX_BLOCKS];
+        size_t lSizes[_MAX_BLOCKS];
+        size_t lHighWaterCount;
+        size_t lTotalCount;
+} _CrtMemState;
+
+
+ /****************************************************************************
+ *
+ * Declarations, prototype and function-like macros
+ *
+ ***************************************************************************/
+
+
+#ifndef _DEBUG
+
+ /****************************************************************************
+ *
+ * Debug OFF
+ * Debug OFF
+ * Debug OFF
+ *
+ ***************************************************************************/
+
+#define _ASSERT(expr) ((void)0)
+
+#define _ASSERTE(expr) ((void)0)
+
+
+#define _RPT0(rptno, msg)
+
+#define _RPT1(rptno, msg, arg1)
+
+#define _RPT2(rptno, msg, arg1, arg2)
+
+#define _RPT3(rptno, msg, arg1, arg2, arg3)
+
+#define _RPT4(rptno, msg, arg1, arg2, arg3, arg4)
+
+
+#define _RPTF0(rptno, msg)
+
+#define _RPTF1(rptno, msg, arg1)
+
+#define _RPTF2(rptno, msg, arg1, arg2)
+
+#define _RPTF3(rptno, msg, arg1, arg2, arg3)
+
+#define _RPTF4(rptno, msg, arg1, arg2, arg3, arg4)
+
+#define _malloc_dbg(s, t, f, l)         MAX_malloc(s)
+#define _calloc_dbg(c, s, t, f, l)      MAX_calloc(c, s)
+#define _realloc_dbg(p, s, t, f, l)     MAX_realloc(p, s)
+#define _expand_dbg(p, s, t, f, l)      MAX_expand(p, s)
+#define _free_dbg(p, t)                 MAX_free(p)
+#define _msize_dbg(p, t)                MAX_msize(p)
+
+/* WARNING! There are no VC6 equivalents for these
+#define _aligned_malloc_dbg(s, a, f, l)     _aligned_malloc(s, a)
+#define _aligned_realloc_dbg(p, s, a, f, l) _aligned_realloc(p, s, a)
+#define _aligned_free_dbg(p)                _aligned_free(p)
+#define _aligned_offset_malloc_dbg(s, a, o, f, l)       _aligned_offset_malloc(s, a, o)
+#define _aligned_offset_realloc_dbg(p, s, a, o, f, l)   _aligned_offset_realloc(p, s, a, o)
+*/
+
+#define _CrtSetReportHook(f)                ((_CRT_REPORT_HOOK)0)
+#define _CrtSetReportHook2(t, f)            ((int)0)
+#define _CrtSetReportMode(t, f)             ((int)0)
+#define _CrtSetReportFile(t, f)             ((_HFILE)0)
+
+#define _CrtDbgBreak()                      ((void)0)
+
+#define _CrtSetBreakAlloc(a)                ((long)0)
+
+#define _CrtSetAllocHook(f)                 ((_CRT_ALLOC_HOOK)0)
+
+#define _CrtCheckMemory()                   ((int)1)
+#define _CrtSetDbgFlag(f)                   ((int)0)
+#define _CrtDoForAllClientObjects(f, c)     ((void)0)
+#define _CrtIsValidPointer(p, n, r)         ((int)1)
+#define _CrtIsValidHeapPointer(p)           ((int)1)
+#define _CrtIsMemoryBlock(p, t, r, f, l)    ((int)1)
+#define _CrtReportBlockType(p)              ((int)-1)
+
+#define _CrtSetDumpClient(f)                ((_CRT_DUMP_CLIENT)0)
+
+#define _CrtMemCheckpoint(s)                ((void)0)
+#define _CrtMemDifference(s1, s2, s3)       ((int)0)
+#define _CrtMemDumpAllObjectsSince(s)       ((void)0)
+#define _CrtMemDumpStatistics(s)            ((void)0)
+#define _CrtDumpMemoryLeaks()               ((int)0)
+
+
+#else   /* _DEBUG */
+
+
+ /****************************************************************************
+ *
+ * Debug ON
+ * Debug ON
+ * Debug ON
+ *
+ ***************************************************************************/
+
+
+/* Define _CRTIMP */
+
+#ifndef _CRTIMP
+#ifdef  _DLL
+#define _CRTIMP __declspec(dllimport)
+#else   /* ndef _DLL */
+#define _CRTIMP
+#endif  /* _DLL */
+#endif  /* _CRTIMP */
+
+ /****************************************************************************
+ *
+ * Debug Reporting
+ *
+ ***************************************************************************/
+
+//_CRTIMP extern long _crtAssertBusy;
+CoreExport extern long& MAX_crtAssertBusy;
+#define _crtAssertBusy (MAX_crtAssertBusy)
+
+//Install a client-defined reporting function by hooking it into the C run-time debug reporting process
+//_CRTIMP _CRT_REPORT_HOOK __cdecl _CrtSetReportHook(
+//        _CRT_REPORT_HOOK
+//        );
+CoreExport _CRT_REPORT_HOOK	(__cdecl *MAX_CrtSetReportHook)(_CRT_REPORT_HOOK);
+#define _CrtSetReportHook(reportHook)	(*MAX_CrtSetReportHook)(reportHook)
+
+
+
+/* WARNING! There is no VC6 equivalent
+_CRTIMP int __cdecl _CrtSetReportHook2(
+        int,
+        _CRT_REPORT_HOOK
+        );
+*/
+
+//Install a client-defined reporting function by hooking it into the C run-time debug reporting process
+//_CRTIMP int __cdecl _CrtSetReportMode(
+//        int,
+//        int
+//        );
+CoreExport int	(__cdecl *MAX_CrtSetReportMode)(int,int);
+#define _CrtSetReportMode(reportType,reportMode)	(*MAX_CrtSetReportMode)(reportType,reportMode)
+
+
+//Identify the file or stream to be used as a destination for a specific report type by _CrtDbgReport
+//_CRTIMP _HFILE __cdecl _CrtSetReportFile(
+//        int,
+//        _HFILE
+//        );
+CoreExport _HFILE	(__cdecl *MAX_CrtSetReportFile)(int,_HFILE);
+#define _CrtSetReportFile(reportType,reportFile)	(*MAX_CrtSetReportFile)(reportType,reportFile)
+
+//Generate a debug report with a user message and send the report to three possible destinations
+//_CRTIMP int __cdecl _CrtDbgReport(
+//        int,
+//        const char *,
+//        int,
+//        const char *,
+//        const char *,
+//        ...);
+CoreExport int	(__cdecl *MAX_CrtDbgReport)(int,const char *,int,const char *,const char *,...);
+#define _CrtDbgReport	(*MAX_CrtDbgReport) //NOTE: using a standard define, rather than a macro
+
+
+/* Asserts */
+
+#if     _MSC_VER >= 1300 || !defined(_M_IX86) || defined(_CRT_PORTABLE)
+#define _ASSERT_BASE(expr, msg) \
+        (void) ((expr) || \
+                (1 != MAX_CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, msg)) || \
+                (MAX_CrtDbgBreak(), 0))
+#else
+#define _ASSERT_BASE(expr, msg) \
+        do { if (!(expr) && \
+                (1 == MAX_CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, msg))) \
+             MAX_CrtDbgBreak(); } while (0)
+#endif
+
+#define _ASSERT(expr)   _ASSERT_BASE((expr), NULL)
+
+#define _ASSERTE(expr)  _ASSERT_BASE((expr), #expr)
+
+/* Reports with no file/line info */
+
+#if     _MSC_VER >= 1300 || !defined(_M_IX86) || defined(_CRT_PORTABLE)
+#define _RPT_BASE(args) \
+        (void) ((1 != MAX_CrtDbgReport args) || \
+                (MAX_CrtDbgBreak(), 0))
+#else
+#define _RPT_BASE(args) \
+        do { if ((1 == MAX_CrtDbgReport args)) \
+                MAX_CrtDbgBreak(); } while (0)
+#endif
+
+#define _RPT0(rptno, msg) \
+        _RPT_BASE((rptno, NULL, 0, NULL, "%s", msg))
+
+#define _RPT1(rptno, msg, arg1) \
+        _RPT_BASE((rptno, NULL, 0, NULL, msg, arg1))
+
+#define _RPT2(rptno, msg, arg1, arg2) \
+        _RPT_BASE((rptno, NULL, 0, NULL, msg, arg1, arg2))
+
+#define _RPT3(rptno, msg, arg1, arg2, arg3) \
+        _RPT_BASE((rptno, NULL, 0, NULL, msg, arg1, arg2, arg3))
+
+#define _RPT4(rptno, msg, arg1, arg2, arg3, arg4) \
+        _RPT_BASE((rptno, NULL, 0, NULL, msg, arg1, arg2, arg3, arg4))
+
+
+/* Reports with file/line info */
+
+#define _RPTF0(rptno, msg) \
+        _RPT_BASE((rptno, __FILE__, __LINE__, NULL, "%s", msg))
+
+#define _RPTF1(rptno, msg, arg1) \
+        _RPT_BASE((rptno, __FILE__, __LINE__, NULL, msg, arg1))
+
+#define _RPTF2(rptno, msg, arg1, arg2) \
+        _RPT_BASE((rptno, __FILE__, __LINE__, NULL, msg, arg1, arg2))
+
+#define _RPTF3(rptno, msg, arg1, arg2, arg3) \
+        _RPT_BASE((rptno, __FILE__, __LINE__, NULL, msg, arg1, arg2, arg3))
+
+#define _RPTF4(rptno, msg, arg1, arg2, arg3, arg4) \
+        _RPT_BASE((rptno, __FILE__, __LINE__, NULL, msg, arg1, arg2, arg3, arg4))
+
+
+//_CRTIMP void __cdecl _CrtDbgBreak(void);
+CoreExport void	__cdecl MAX_CrtDbgBreak(void);
+
+#define _CrtDbgBreak()	MAX_CrtDbgBreak()
+
+/*
+#if     _MSC_VER >= 1300 && !defined(_CRT_PORTABLE)
+#define _CrtDbgBreak() __debugbreak()
+#elif   defined(_M_IX86) && !defined(_CRT_PORTABLE)
+#define _CrtDbgBreak() __asm { int 3 }
+#elif   defined(_M_ALPHA) && !defined(_CRT_PORTABLE)
+void _BPT();
+#pragma intrinsic(_BPT)
+#define _CrtDbgBreak() _BPT()
+#elif   defined(_M_IA64) && !defined(_CRT_PORTABLE)
+void __break(int);
+#pragma intrinsic (__break)
+#define _CrtDbgBreak() __break(0x80016)
+#else
+_CRTIMP void __cdecl _CrtDbgBreak(
+        void
+        );
+#endif
+*/
+
+ /****************************************************************************
+ *
+ * Heap routines
+ *
+ ***************************************************************************/
+
+#ifdef  _CRTDBG_MAP_ALLOC
+
+#define   malloc(s)         MAX_malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
+#define   calloc(c, s)      MAX_calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__)
+#define   realloc(p, s)     MAX_realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
+#define   _expand(p, s)     MAX_expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
+#define   free(p)           MAX_free_dbg(p, _NORMAL_BLOCK)
+#define   _msize(p)         MAX_msize_dbg(p, _NORMAL_BLOCK)
+/* WARNING! No VC6 equivalent
+#define   _aligned_malloc(s, a)                 _aligned_malloc_dbg(s, a, __FILE__, __LINE__)
+#define   _aligned_realloc(p, s, a)             _aligned_realloc_dbg(p, s, a, __FILE__, __LINE__)
+#define   _aligned_offset_malloc(s, a, o)       _aligned_offset_malloc_dbg(s, a, o, __FILE__, __LINE__)
+#define   _aligned_offset_realloc(p, s, a, o)   _aligned_offset_realloc_dbg(p, s, a, o, __FILE__, __LINE__)
+#define   _aligned_free(p)  _aligned_free_dbg(p)
+*/
+#endif  /* _CRTDBG_MAP_ALLOC */
+
+//_CRTIMP extern long _crtBreakAlloc;      /* Break on this allocation */
+CoreExport extern long& MAX_crtBreakAlloc;
+#define _crtBreakAlloc (MAX_crtBreakAlloc)
+
+//Set a breakpoint on a specified object allocation order number
+//_CRTIMP long __cdecl _CrtSetBreakAlloc(
+//        long
+//        );
+CoreExport long	(__cdecl *MAX_CrtSetBreakAlloc)(long);
+#define _CrtSetBreakAlloc(lBreakAlloc)	(*MAX_CrtSetBreakAlloc)(lBreakAlloc)
+
+
+/*
+ * Prototypes for malloc, free, realloc, etc are in malloc.h
+ */
+
+//Debug version of malloc; only available in the debug versions of the run-time libraries 
+//_CRTIMP void * __cdecl _malloc_dbg(
+//        size_t,
+//        int,
+//        const char *,
+//        int
+//        );
+CoreExport void *	(__cdecl *MAX_malloc_dbg)(size_t,int,const char *,int);
+#define _malloc_dbg(size,blockType,filename,lineNumber)	(*MAX_malloc_dbg)(size,blockType,filename,lineNumber)
+
+//Debug version of calloc; only available in the debug versions of the run-time libraries 
+//_CRTIMP void * __cdecl _calloc_dbg(
+//        size_t,
+//        size_t,
+//        int,
+//        const char *,
+//        int
+//        );
+CoreExport void *	(__cdecl *MAX_calloc_dbg)(size_t, size_t, int, const char *, int);
+#define _calloc_dbg(num,size,blockType,filename,lineNumber)	(*MAX_calloc_dbg)(num,size,blockType,filename,lineNumber)
+
+//Debug version of realloc; only available in the debug versions of the run-time libraries 
+//_CRTIMP void * __cdecl _realloc_dbg(
+//        void *,
+//        size_t,
+//        int,
+//        const char *,
+//        int
+//        );
+CoreExport void *	(__cdecl *MAX_realloc_dbg)(void *, size_t, int, const char *, int);
+#define _realloc_dbg(userData,newSize,blockType,filename,lineNumber)	(*MAX_realloc_dbg)(userData,newSize,blockType,filename,lineNumber)
+
+//Debug version of _expand; only available in the debug versions of the run-time libraries 
+//_CRTIMP void * __cdecl _expand_dbg(
+//        void *,
+//        size_t,
+//        int,
+//        const char *,
+//        int
+//        );
+CoreExport void *	(__cdecl *MAX_expand_dbg)(void *, size_t, int, const char *, int);
+#define _expand_dbg(userData,newSize,blockType,filename,lineNumber)	(*MAX_expand_dbg)(userData,newSize,blockType,filename,lineNumber)
+
+//Debug version of free; only available in the debug versions of the run-time libraries 
+//_CRTIMP void __cdecl _free_dbg(
+//        void *,
+//        int
+//        );
+CoreExport void	(__cdecl *MAX_free_dbg)(void *, int);
+#define _free_dbg(userData,blockType)	(*MAX_free_dbg)(userData,blockType)
+
+//Debug version of _msize; only available in the debug versions of the run-time libraries 
+//_CRTIMP size_t __cdecl _msize_dbg (
+//        void *,
+//        int
+//        );
+CoreExport size_t	(__cdecl *MAX_msize_dbg)(void *, int);
+#define _msize_dbg(userData,blockType)	(*MAX_msize_dbg)(userData,blockType)
+
+
+/* WARNING! No VC6 equivalent
+
+_CRTIMP void * __cdecl _aligned_malloc_dbg(
+        size_t,
+        size_t,
+        const char *,
+        int
+        );
+
+_CRTIMP void * __cdecl _aligned_realloc_dbg(
+        void *,
+        size_t,
+        size_t,
+        const char *,
+        int
+        );
+
+_CRTIMP void * __cdecl _aligned_offset_malloc_dbg(
+        size_t,
+        size_t,
+        size_t,
+        const char *,
+        int
+        );
+
+_CRTIMP void * __cdecl _aligned_offset_realloc_dbg(
+        void *,
+        size_t,
+        size_t,
+        size_t,
+        const char *,
+        int
+        );
+
+_CRTIMP void __cdecl _aligned_free_dbg(
+        void *
+        );
+*/
+
+ /****************************************************************************
+ *
+ * Client-defined allocation hook
+ *
+ ***************************************************************************/
+
+//Install a client-defined allocation function by hooking it into the C run-time debug memory allocation process
+//_CRTIMP _CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook(
+//        _CRT_ALLOC_HOOK
+//        );
+CoreExport _CRT_ALLOC_HOOK	(__cdecl *MAX_CrtSetAllocHook)(_CRT_ALLOC_HOOK);
+#define _CrtSetAllocHook(allocHook)	(*MAX_CrtSetAllocHook)(allocHook)
+
+
+ /****************************************************************************
+ *
+ * Memory management
+ *
+ ***************************************************************************/
+
+/*
+ * Bitfield flag that controls CRT heap behavior
+ * Default setting is _CRTDBG_ALLOC_MEM_DF
+ */
+
+//_CRTIMP extern int _crtDbgFlag;
+CoreExport extern int& MAX_crtDbgFlag;
+#define _crtDbgFlag (MAX_crtDbgFlag)
+
+//Confirm the integrity of the memory blocks allocated on the debug heap
+//_CRTIMP int __cdecl _CrtCheckMemory(
+//        void
+//        );
+CoreExport int	(__cdecl *MAX_CrtCheckMemory)(void);
+#define _CrtCheckMemory()	(*MAX_CrtCheckMemory)()
+
+//Retrieve or modify the state of the _crtDbgFlag flag to control the allocation behavior of the debug heap manager
+//_CRTIMP int __cdecl _CrtSetDbgFlag(
+//        int
+//        );
+CoreExport int	(__cdecl *MAX_CrtSetDbgFlag)(int);
+#define _CrtSetDbgFlag(newFlag)	(*MAX_CrtSetDbgFlag)(newFlag)
+
+//Call an application-supplied function for all _CLIENT_BLOCK types on the heap
+//_CRTIMP void __cdecl _CrtDoForAllClientObjects(
+//        void (*pfn)(void *, void *),
+//        void *
+//        );
+CoreExport void	(__cdecl *MAX_CrtDoForAllClientObjects)(void (*pfn)(void *, void *),void *);
+#define _CrtDoForAllClientObjects(pfn,context)	(*MAX_CrtDoForAllClientObjects)(pfn,context)
+
+//Verify that a specified memory range is valid for reading and writing
+//_CRTIMP int __cdecl _CrtIsValidPointer(
+//        const void *,
+//        unsigned int,
+//        int
+//        );
+CoreExport int	(__cdecl *MAX_CrtIsValidPointer)(const void *,unsigned int,int);
+#define _CrtIsValidPointer(address,size,access) (*MAX_CrtIsValidPointer)(address,size,access)
+
+//Verify that a specified pointer is in the local heap
+//_CRTIMP int __cdecl _CrtIsValidHeapPointer(
+//        const void *
+//        );
+CoreExport int	(__cdecl *MAX_CrtIsValidHeapPointer)(const void *);
+#define _CrtIsValidHeapPointer(userData)	(*MAX_CrtIsValidHeapPointer)(userData)
+
+//Verify that a specified memory block is located within the local heap and that it has a valid debug heap block type identifier
+//_CRTIMP int __cdecl _CrtIsMemoryBlock(
+//        const void *,
+//        unsigned int,
+//        long *,
+//        char **,
+//        int *
+//        );
+CoreExport int	(__cdecl *MAX_CrtIsMemoryBlock)(const void *,unsigned int,long *,char **,int *);
+#define _CrtIsMemoryBlock(userData,size,requestNumber,filename,linenumber)	(*MAX_CrtIsMemoryBlock)(userData,size,requestNumber,filename,linenumber)
+
+
+/* WARNING! No VC6 equivalent
+_CRTIMP int __cdecl _CrtReportBlockType(
+        const void *
+        );
+*/
+
+
+ /****************************************************************************
+ *
+ * Memory state
+ *
+ ***************************************************************************/
+
+//Install an application-defined function that is called every time a debug dump function is called to dump _CLIENT_BLOCK type memory blocks
+//_CRTIMP _CRT_DUMP_CLIENT __cdecl _CrtSetDumpClient(
+//        _CRT_DUMP_CLIENT
+//        );
+CoreExport _CRT_DUMP_CLIENT	(__cdecl *MAX_CrtSetDumpClient)(_CRT_DUMP_CLIENT);
+#define _CrtSetDumpClient(dumpClient)	(*MAX_CrtSetDumpClient)(dumpClient)
+
+//Obtain the current state of the debug heap and store it in an application-supplied _CrtMemState structure
+//_CRTIMP void __cdecl _CrtMemCheckpoint(
+//        _CrtMemState *
+//        );
+CoreExport void	(__cdecl *MAX_CrtMemCheckpoint)(_CrtMemState *);
+#define _CrtMemCheckpoint(state)	(*MAX_CrtMemCheckpoint)(state)
+
+//Compare two memory states for significant differences and return the results
+//_CRTIMP int __cdecl _CrtMemDifference(
+//        _CrtMemState *,
+//        const _CrtMemState *,
+//        const _CrtMemState *
+//        );
+CoreExport int	(__cdecl *MAX_CrtMemDifference)(_CrtMemState *,const _CrtMemState *,const _CrtMemState *);
+#define _CrtMemDifference(stateDiff,oldState,newState)	(*MAX_CrtMemDifference)(stateDiff,oldState,newState)
+
+//Dump information about objects on the heap since a specified checkpoint was taken or from the start of program execution
+//_CRTIMP void __cdecl _CrtMemDumpAllObjectsSince(
+//        const _CrtMemState *
+//        );
+CoreExport void	(__cdecl *MAX_CrtMemDumpAllObjectsSince)(const _CrtMemState *);
+#define _CrtMemDumpAllObjectsSince(state)	(*MAX_CrtMemDumpAllObjectsSince)(state)
+
+//Dump the debug header information for a specified memory state in a user-readable form
+//_CRTIMP void __cdecl _CrtMemDumpStatistics(
+//        const _CrtMemState *
+//        );
+CoreExport void	(__cdecl *MAX_CrtMemDumpStatistics)(const _CrtMemState *);
+#define _CrtMemDumpStatistics(state)	(*MAX_CrtMemDumpStatistics)(state)
+
+//Dump all of the memory blocks on the debug heap when a significant memory leak has occurred
+//_CRTIMP int __cdecl _CrtDumpMemoryLeaks(
+//        void
+//        );
+CoreExport int	(__cdecl *MAX_CrtDumpMemoryLeaks)(void);
+#define _CrtDumpMemoryLeaks()	(*MAX_CrtDumpMemoryLeaks)()
+
+
+#endif  /* _DEBUG */
+
+#ifdef  __cplusplus
+//}
+
+#ifndef _MFC_OVERRIDES_NEW
+
+//extern "C++" {
+
+#pragma warning(disable: 4507)  /* Ignore faulty warning */
+
+#ifndef _DEBUG
+
+ /****************************************************************************
+ *
+ * Debug OFF
+ * Debug OFF
+ * Debug OFF
+ *
+ ***************************************************************************/
+
+CoreExport void *__cdecl MAX_new_array(size_t size);
+CoreExport void __cdecl MAX_delete_array(void* mem);
+
+//void * __cdecl operator new[](size_t);
+__forceinline void* _cdecl operator new[](size_t size)
+{
+	return MAX_new_array(size);
+}
+
+
+//FIXME: implement this
+//inline void * __cdecl operator new(size_t s, int, const char *, int)
+//        { return ::operator new(s); }
+
+//FIXME: implement this
+//inline void* __cdecl operator new[](size_t s, int, const char *, int)
+//        { return ::operator new[](s); }
+
+#if     _MSC_VER >= 1200
+
+
+//void __cdecl operator delete[](void *);
+__forceinline void _cdecl operator delete[](void* memblock)
+{
+	MAX_delete_array(memblock);
+}
+
+//FIXME: implement this
+//inline void __cdecl operator delete(void * _P, int, const char *, int)
+//        { ::operator delete(_P); }
+
+//FIXME: implement this
+//inline void __cdecl operator delete[](void * _P, int, const char *, int)
+//        { ::operator delete[](_P); }
+#endif
+#else /* _DEBUG */
+
+ /****************************************************************************
+ *
+ * Debug ON
+ * Debug ON
+ * Debug ON
+ *
+ ***************************************************************************/
+ 
+CoreExport void *__cdecl MAX_new_array(size_t size);
+CoreExport void __cdecl MAX_delete_array(void* mem);
+
+//void * __cdecl operator new[](size_t);
+__forceinline void* _cdecl operator new[](size_t size)
+{
+	return MAX_new_array(size);
+}
+
+//FIXME: implement this
+//void * __cdecl operator new(
+//        size_t,
+//        int,
+//        const char *,
+//        int
+//        );
+
+//FIXME: implement this
+//void * __cdecl operator new[](
+//        size_t,
+//        int,
+//        const char *,
+//        int
+//        );
+
+#if     _MSC_VER >= 1200
+//void __cdecl operator delete[](void *);
+__forceinline void _cdecl operator delete[](void* memblock)
+{
+	MAX_delete_array(memblock);
+}
+
+//FIXME: implement this
+//inline void __cdecl operator delete(void * _P, int, const char *, int)
+//        { ::operator delete(_P); }
+
+//FIXME: implement this
+//inline void __cdecl operator delete[](void * _P, int, const char *, int)
+//        { ::operator delete[](_P); }
+#endif
+
+#ifdef _CRTDBG_MAP_ALLOC
+
+//FIXME: implement this
+//inline void * __cdecl operator new(size_t s)
+//        { return ::operator new(s, _NORMAL_BLOCK, __FILE__, __LINE__); }
+
+//FIXME: implement this
+//inline void* __cdecl operator new[](size_t s)
+//        { return ::operator new[](s, _NORMAL_BLOCK, __FILE__, __LINE__); }
+
+#endif  /* _CRTDBG_MAP_ALLOC */
+
+#endif  /* _DEBUG */
+
+//}
+
+#endif  /* _MFC_OVERRIDES_NEW */
+
+#endif  /* __cplusplus */
+
+
+
+#endif	//_MSC_VER > 1300
+
+#endif  /* MAX_MEM */
+
diff --git a/NifCommon/MAX_MemDirect.h b/NifCommon/MAX_MemDirect.h
new file mode 100644
index 0000000000000000000000000000000000000000..47340a57511e49f02b8d7176acaa2fd7c518ab3b
--- /dev/null
+++ b/NifCommon/MAX_MemDirect.h
@@ -0,0 +1,195 @@
+/**********************************************************************
+ *<
+	FILE: MAX_Mem
+
+	DESCRIPTION:  Access to the memory routines used by Max
+				  (Can be called from plug-ins compiled with different
+				   memory handling)
+
+	CREATED BY: Cleve Ard & Michaelson Britt
+
+	HISTORY: Created, Oct. 2003
+
+ *>	Copyright (c) 2003 Autodesk, Inc., All Rights Reserved.
+ **********************************************************************/
+
+#ifndef __MAX_MEM_H
+#define __MAX_MEM_H
+
+#include <crtdbg.h>
+#include <malloc.h>
+#include <new.h>
+
+
+
+// NOTE: For efficiency, most functions are declared as pointers which
+// route into the corresponding function.  This avoids passing through
+// a "wrapper" layer.
+//
+// New and Delete were implemented with wrappers, so that the original
+// MAX_Mem.h could use function overloads with them, since this is not
+// possible with the preprocessor.
+//   int* x = new;
+// There's no way to define 'new' as a preprocessor macro such that
+// 'new int' is translated to 'MAX_new( sizeof(int) )'
+
+
+CoreExport void *__cdecl MAX_new(size_t size);
+
+CoreExport void *__cdecl MAX_new_array(size_t size);
+
+// Disabled: Placement forms not supported
+//CoreExport void *__cdecl MAX_new_placement(size_t size, void *_P);
+
+CoreExport void __cdecl MAX_delete(void* mem);
+
+CoreExport void __cdecl MAX_delete_array(void* mem);
+
+// Disabled: Placement forms not supported
+//CoreExport void __cdecl MAX_delete_placement(void *memblock, void *_P);
+
+//Allocate block of memory from heap 
+CoreExport void *	(__cdecl *MAX_malloc)(size_t size);
+
+//Allocate storage for array, initializing every byte in allocated block to 0 
+CoreExport void *	(__cdecl *MAX_calloc)(size_t num, size_t size);
+
+//Reallocate block to new size 
+CoreExport void *	(__cdecl *MAX_realloc)(void *memblock, size_t size);
+
+//Expand or shrink block of memory without moving it 
+CoreExport void *	(__cdecl *MAX_expand)(void * memblock, size_t size);
+
+//Free allocated block 
+CoreExport void	(__cdecl *MAX_free)(void * memblock);
+
+//Return size of allocated block 
+CoreExport size_t	(__cdecl *MAX_msize)(void *memblock);
+
+// Set hook function
+// Disabled: because HEAPHOOK does not seem to be enabled in practice
+//CoreExport _HEAPHOOK (__cdecl *MAX_setheaphook)(_HEAPHOOK);
+
+//Add memory to heap 
+CoreExport int	(__cdecl *MAX_heapadd)(void * memblock, size_t size);
+
+//Check heap for consistency 
+CoreExport int	(__cdecl *MAX_heapchk)(void);
+
+//Release unused memory in heap 
+CoreExport int	(__cdecl *MAX_heapmin)(void);
+
+//Fill free heap entries with specified value 
+CoreExport int	(__cdecl *MAX_heapset)(unsigned int fill);
+
+//Return information about each entry in heap 
+CoreExport int	(__cdecl *MAX_heapwalk)(_HEAPINFO *entryinfo);
+
+//Return address of current new handler routine as set by _set_new_handler 
+CoreExport _PNH	(__cdecl *MAX_query_new_handler)( void );
+
+//Enable error-handling mechanism when new operator fails (to allocate memory) and enable compilation of Standard Template Libraries (STL) 
+CoreExport _PNH	(__cdecl *MAX_set_new_handler)( _PNH pNewHandler );
+
+//Return integer indicating new handler mode set by _set_new_mode for malloc 
+CoreExport int	(__cdecl *MAX_query_new_mode)( void );
+
+//Set new handler mode for malloc 
+CoreExport int	(__cdecl *MAX_set_new_mode)( int newhandlermode );
+
+//Get/Set the upper limit for the size of a memory allocation that will be supported by the small-block heap 
+CoreExport size_t	(__cdecl *MAX_get_sbh_threshold)(void);
+CoreExport int	(__cdecl *MAX_set_sbh_threshold)(size_t size);
+
+
+//-----------------------------------------------------------------------------
+// The following debug functions are available only in a Sparks debug build
+
+#ifdef _DEBUG
+#ifndef IS_HYBRID
+
+//Debug version of calloc; only available in the debug versions of the run-time libraries 
+CoreExport void *	(__cdecl *MAX_calloc_dbg)(size_t num, size_t size, int blockType, const char * filename, int lineNumber);
+
+//Debug version of _expand; only available in the debug versions of the run-time libraries 
+CoreExport void *	(__cdecl *MAX_expand_dbg)(void * userData, size_t newSize, int blockType, const char * filename, int lineNumber);
+
+//Debug version of malloc; only available in the debug versions of the run-time libraries 
+CoreExport void *	(__cdecl *MAX_malloc_dbg)(size_t size,int blockType,const char *filename,int lineNumber);
+
+//Debug version of free; only available in the debug versions of the run-time libraries 
+CoreExport void	(__cdecl *MAX_free_dbg)(void * userData, int blockType);
+
+//Debug version of _msize; only available in the debug versions of the run-time libraries 
+CoreExport size_t	(__cdecl *MAX_msize_dbg)(void *userData, int blockType);
+
+//Debug version of realloc; only available in the debug versions of the run-time libraries 
+CoreExport void *	(__cdecl *MAX_realloc_dbg)(void *userData, size_t newSize, int blockType, const char *filename, int lineNumber);
+
+CoreExport extern long& MAX_crtAssertBusy;
+
+//Install a client-defined reporting function by hooking it into the C run-time debug reporting process
+CoreExport _CRT_REPORT_HOOK	(__cdecl *MAX_CrtSetReportHook)(_CRT_REPORT_HOOK reportHook);
+
+//Install a client-defined reporting function by hooking it into the C run-time debug reporting process
+CoreExport int	(__cdecl *MAX_CrtSetReportMode)(int reportType,int reportMode);
+
+//Identify the file or stream to be used as a destination for a specific report type by _CrtDbgReport
+CoreExport _HFILE	(__cdecl *MAX_CrtSetReportFile)(int reportType,_HFILE reportFile);
+
+//Generate a debug report with a user message and send the report to three possible destinations
+CoreExport int	(__cdecl *MAX_CrtDbgReport)(int,const char *,int,const char *,const char *,...);
+
+CoreExport void	__cdecl MAX_CrtDbgBreak(void);
+
+CoreExport extern long& MAX_crtBreakAlloc;      /* Break on this allocation */
+
+//Set a breakpoint on a specified object allocation order number
+CoreExport long	(__cdecl *MAX_CrtSetBreakAlloc)(long lBreakAlloc);
+
+//Install a client-defined allocation function by hooking it into the C run-time debug memory allocation process
+CoreExport _CRT_ALLOC_HOOK	(__cdecl *MAX_CrtSetAllocHook)(_CRT_ALLOC_HOOK allocHook);
+
+CoreExport extern int& MAX_crtDbgFlag;
+
+//Confirm the integrity of the memory blocks allocated on the debug heap
+CoreExport int	(__cdecl *MAX_CrtCheckMemory)(void);
+
+//Retrieve or modify the state of the _crtDbgFlag flag to control the allocation behavior of the debug heap manager
+CoreExport int	(__cdecl *MAX_CrtSetDbgFlag)(int newFlag);
+
+//Call an application-supplied function for all _CLIENT_BLOCK types on the heap
+CoreExport void	(__cdecl *MAX_CrtDoForAllClientObjects)(void (*pfn)(void *, void *),void *context);
+
+//Verify that a specified memory range is valid for reading and writing
+CoreExport int	(__cdecl *MAX_CrtIsValidPointer)(const void *address,unsigned int size,int access);
+
+//Verify that a specified pointer is in the local heap
+CoreExport int	(__cdecl *MAX_CrtIsValidHeapPointer)(const void *userData);
+
+//Verify that a specified memory block is located within the local heap and that it has a valid debug heap block type identifier
+CoreExport int	(__cdecl *MAX_CrtIsMemoryBlock)(const void *userData,unsigned int size,long *requestNumber,char **filename,int *linenumber);
+
+//Install an application-defined function that is called every time a debug dump function is called to dump _CLIENT_BLOCK type memory blocks
+CoreExport _CRT_DUMP_CLIENT	(__cdecl *MAX_CrtSetDumpClient)(_CRT_DUMP_CLIENT dumpClient);
+
+//Obtain the current state of the debug heap and store it in an application-supplied _CrtMemState structure
+CoreExport void	(__cdecl *MAX_CrtMemCheckpoint)(_CrtMemState *state);
+
+//Compare two memory states for significant differences and return the results
+CoreExport int	(__cdecl *MAX_CrtMemDifference)(_CrtMemState *stateDiff,const _CrtMemState *oldState,const _CrtMemState *newState);
+
+//Dump information about objects on the heap since a specified checkpoint was taken or from the start of program execution
+CoreExport void	(__cdecl *MAX_CrtMemDumpAllObjectsSince)(const _CrtMemState *state);
+
+//Dump the debug header information for a specified memory state in a user-readable form
+CoreExport void	(__cdecl *MAX_CrtMemDumpStatistics)(const _CrtMemState *state);
+
+//Dump all of the memory blocks on the debug heap when a significant memory leak has occurred
+CoreExport int	(__cdecl *MAX_CrtDumpMemoryLeaks)(void);
+
+#endif //IS_HYBRID
+#endif //_DEBUG
+
+
+#endif //__MAX_MEM_H
diff --git a/NifCommon/NifCommon_VC80.vcproj b/NifCommon/NifCommon_VC80.vcproj
index 6026cbccd7dadecbce0e0b0f5ea4d707eea28b99..62f60d5fa8bf3df7256f397632ac1b8faa4e8fdd 100644
--- a/NifCommon/NifCommon_VC80.vcproj
+++ b/NifCommon/NifCommon_VC80.vcproj
@@ -516,6 +516,14 @@
 				RelativePath=".\IniSection.h"
 				>
 			</File>
+			<File
+				RelativePath=".\MAX_Mem.h"
+				>
+			</File>
+			<File
+				RelativePath=".\MAX_MemDirect.h"
+				>
+			</File>
 			<File
 				RelativePath=".\NifGui.h"
 				>
diff --git a/NifCommon/niutils.cpp b/NifCommon/niutils.cpp
index 5164417e5244defa59a3229fe7d40edd341c85fe..b46ca0d562248312e56887780d56b560cf81b5ab 100644
--- a/NifCommon/niutils.cpp
+++ b/NifCommon/niutils.cpp
@@ -69,6 +69,110 @@ std::string FormatString(const TCHAR* format,...)
    return text;
 }
 
+
+// routine for parsing white space separated lines.  Handled like command line parameters w.r.t quotes.
+static void parse_line (
+                        const char *start,
+                        char **argv,
+                        char *args,
+                        int *numargs,
+                        int *numchars
+                        )
+{
+   const char NULCHAR    = '\0';
+   const char SPACECHAR  = ' ';
+   const char TABCHAR    = '\t';
+   const char RETURNCHAR = '\r';
+   const char LINEFEEDCHAR = '\n';
+   const char DQUOTECHAR = '\"';
+   const char SLASHCHAR  = '\\';
+   const char *p;
+   int inquote;                    /* 1 = inside quotes */
+   int copychar;                   /* 1 = copy char to *args */
+   unsigned numslash;              /* num of backslashes seen */
+
+   *numchars = 0;
+   *numargs = 0;                   /* the program name at least */
+
+   p = start;
+
+   inquote = 0;
+
+   /* loop on each argument */
+   for(;;) 
+   {
+      if ( *p ) { while (*p == SPACECHAR || *p == TABCHAR || *p == RETURNCHAR || *p == LINEFEEDCHAR) ++p; }
+
+      if (*p == NULCHAR) break; /* end of args */
+
+      /* scan an argument */
+      if (argv)
+         *argv++ = args;     /* store ptr to arg */
+      ++*numargs;
+
+      /* loop through scanning one argument */
+      for (;;) 
+      {
+         copychar = 1;
+         /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
+         2N+1 backslashes + " ==> N backslashes + literal "
+         N backslashes ==> N backslashes */
+         numslash = 0;
+         while (*p == SLASHCHAR) 
+         {
+            /* count number of backslashes for use below */
+            ++p;
+            ++numslash;
+         }
+         if (*p == DQUOTECHAR) 
+         {
+            /* if 2N backslashes before, start/end quote, otherwise copy literally */
+            if (numslash % 2 == 0) {
+               if (inquote) {
+                  if (p[1] == DQUOTECHAR)
+                     p++;    /* Double quote inside quoted string */
+                  else        /* skip first quote char and copy second */
+                     copychar = 0;
+               } else
+                  copychar = 0;       /* don't copy quote */
+
+               inquote = !inquote;
+            }
+            numslash /= 2;          /* divide numslash by two */
+         }
+
+         /* copy slashes */
+         while (numslash--) {
+            if (args)
+               *args++ = SLASHCHAR;
+            ++*numchars;
+         }
+
+         /* if at end of arg, break loop */
+         if (*p == NULCHAR || (!inquote && (*p == SPACECHAR || *p == TABCHAR || *p == RETURNCHAR || *p == LINEFEEDCHAR)))
+            break;
+
+         /* copy character into argument */
+         if (copychar) 
+         {
+            if (args)
+               *args++ = *p;
+            ++*numchars;
+         }
+         ++p;
+      }
+
+      /* null-terminate the argument */
+      if (args)
+         *args++ = NULCHAR;          /* terminate string */
+      ++*numchars;
+   }
+   /* We put one last argument in -- a null ptr */
+   if (argv)
+      *argv++ = NULL;
+   ++*numargs;
+}
+
 // Tokenize a string using strtok and return it as a stringlist
 stringlist TokenizeString(LPCTSTR str, LPCTSTR delims, bool trim)
 {
@@ -80,6 +184,32 @@ stringlist TokenizeString(LPCTSTR str, LPCTSTR delims, bool trim)
    return values;
 }
 
+// Tokenize a string using strtok and return it as a stringlist
+stringlist TokenizeCommandLine(LPCTSTR str, bool trim)
+{
+   stringlist values;
+   int nargs = 0, nchars = 0;
+   parse_line( str, NULL, NULL, &nargs, &nchars);
+   char **largv = (char **)_alloca(nargs * sizeof(TCHAR *) + nchars * sizeof(TCHAR));
+   parse_line( str, largv, ((TCHAR*)largv) + nargs * sizeof(TCHAR*), &nargs, &nchars);
+   for (int i=0; i<nargs; ++i) {
+      LPTSTR p = largv[i];
+      if (p == NULL) continue;
+      values.push_back(string((trim) ? Trim(p) : p));
+   }
+   return values;
+}
+
+string JoinCommandLine(stringlist args)
+{
+   std::stringstream str;
+   for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) {
+      if (itr != args.begin()) str << ' ';
+      str << (*itr);
+   }
+   return str.str();
+}
+
 // Parse and ini file section and return the results as s NameValueCollection.
 NameValueCollection ReadIniSection(LPCTSTR Section, LPCTSTR iniFileName )
 {
@@ -302,6 +432,11 @@ int wildcmpi(const TCHAR *wild, const TCHAR *string) {
    return !*wild;
 }
 
+bool wildmatch(const TCHAR* match, const TCHAR* value)
+{
+   return (wildcmpi(match, value)) ? true : false;
+}
+
 bool wildmatch(const string& match, const std::string& value) 
 {
    return (wildcmpi(match.c_str(), value.c_str())) ? true : false;
@@ -323,6 +458,58 @@ void RenameNode(Interface *gi, LPCTSTR SrcName, LPCTSTR DstName)
    if (node != NULL) node->SetName(const_cast<LPTSTR>(DstName));
 }
 
+Point3 TOEULER(const Matrix3 &m)
+{
+   Point3 rv(0.0f, 0.0f, 0.0f);
+   if ( m.GetRow(2)[0] < 1.0 )
+   {
+      if ( m.GetRow(2)[0] > - 1.0 )
+      {
+         rv[2] = atan2( - m.GetRow(1)[0], m.GetRow(0)[0] );
+         rv[1] = asin( m.GetRow(2)[0] );
+         rv[0] = atan2( - m.GetRow(2)[1], m.GetRow(2)[2] );
+      }
+      else
+      {
+         rv[2] = - atan2( - m.GetRow(1)[2], m.GetRow(1)[1] );
+         rv[1] = - PI / 2;
+         rv[0] = 0.0;
+      }
+   }
+   else
+   {
+      rv[2] = atan2( m.GetRow(1)[2], m.GetRow(1)[1] );
+      rv[1] = PI / 2;
+      rv[0] = 0.0;
+   }
+   return rv;
+}
+
+inline Point3 TODEG(const Point3& p){
+   return Point3(TODEG(p[0]), TODEG(p[1]), TODEG(p[2]));
+}
+
+inline Point3 TORAD(const Point3& p){
+   return Point3(TORAD(p[0]), TORAD(p[1]), TORAD(p[2]));
+}
+
+inline TSTR TOSTRING(const Point3& p) {
+   return FormatText("[%g,%g,%g]", p[0], p[1], p[2]);
+}
+
+inline TSTR TOSTRING(float p) {
+   return FormatText("%g", p);
+}
+
+inline TSTR TOSTRING(const Matrix3& m) {
+   return TOSTRING( TODEG( TOEULER(m) ) );
+}
+
+inline TSTR TOSTRING(const Quat& q) {
+   Matrix3 m; q.MakeMatrix(m);
+   return TOSTRING( m );
+}
+
 void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs, TimeValue t)
 {
    Point3 p = m3.GetTrans();
@@ -337,16 +524,15 @@ void PosRotScaleNode(INode *n, Matrix3& m3, PosRotScale prs, TimeValue t)
 void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs, TimeValue t)
 {
    if (Control *c = n->GetTMController()) {
+      if (prs & prsRot && q.w == FloatNegINF) prs = PosRotScale(prs & ~prsRot);
+      if (prs & prsPos && p.x == FloatNegINF) prs = PosRotScale(prs & ~prsPos);
+      if (prs & prsScale && s == FloatNegINF) prs = PosRotScale(prs & ~prsScale);
 #ifdef USE_BIPED
       // 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) 
          ||(c->ClassID() == FOOTPRINT_CLASS_ID))
       {
-         if (prs & prsRot && q.w == FloatNegINF) prs = PosRotScale(prs & ~prsRot);
-         if (prs & prsPos && p.x == FloatNegINF) prs = PosRotScale(prs & ~prsPos);
-         if (prs & prsScale && s == FloatNegINF) prs = PosRotScale(prs & ~prsScale);
-
          ScaleValue sv(Point3(s,s,s));
          // Get the Biped Export Interface from the controller 
          //IBipedExport *BipIface = (IBipedExport *) c->GetInterface(I_BIPINTERFACE);
@@ -361,6 +547,14 @@ void PosRotScaleNode(INode *n, Point3 p, Quat& q, float s, PosRotScale prs, Time
       }
 #endif
       PosRotScaleNode(c, p, q, s, prs, t);
+
+//#ifdef _DEBUG
+//      static TSTR sEmpty = "<Empty>";
+//      TSTR spos = (prs & prsPos) ? TOSTRING(p) : sEmpty;
+//      TSTR srot = (prs & prsRot) ? TOSTRING(q) : sEmpty;
+//      TSTR sscl = (prs & prsScale) ? TOSTRING(s) : sEmpty;
+//      OutputDebugString(FormatText("Transform(%s, %s, %s, %s)\n", n->GetName(), spos.data(), srot.data(), sscl.data()));
+//#endif
    }
 }
 
@@ -685,4 +879,61 @@ TSTR GetFileVersion(const char *fileName)
       }
    }
    return retval;
-}
\ No newline at end of file
+}
+
+// Calculate bounding sphere using minimum-volume axis-align bounding box.  Its fast but not a very good fit.
+void CalcAxisAlignedSphere(const vector<Vector3>& vertices, Vector3& center, float& radius)
+{
+   //--Calculate center & radius--//
+
+   //Set lows and highs to first vertex
+   Vector3 lows = vertices[ 0 ];
+   Vector3 highs = vertices[ 0 ];
+
+   //Iterate through the vertices, adjusting the stored values
+   //if a vertex with lower or higher values is found
+   for ( unsigned int i = 0; i < vertices.size(); ++i ) {
+      const Vector3 & v = vertices[ i ];
+
+      if ( v.x > highs.x ) highs.x = v.x;
+      else if ( v.x < lows.x ) lows.x = v.x;
+
+      if ( v.y > highs.y ) highs.y = v.y;
+      else if ( v.y < lows.y ) lows.y = v.y;
+
+      if ( v.z > highs.z ) highs.z = v.z;
+      else if ( v.z < lows.z ) lows.z = v.z;
+   }
+
+   //Now we know the extent of the shape, so the center will be the average
+   //of the lows and highs
+   center = (highs + lows) / 2.0f;
+
+   //The radius will be the largest distance from the center
+   Vector3 diff;
+   float dist2(0.0f), maxdist2(0.0f);
+   for ( unsigned int i = 0; i < vertices.size(); ++i ) {
+      const Vector3 & v = vertices[ i ];
+
+      diff = center - v;
+      dist2 = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
+      if ( dist2 > maxdist2 ) maxdist2 = dist2;
+   };
+   radius = sqrt(maxdist2);
+}
+
+// Calculate bounding sphere using average position of the points.  Better fit but slower.
+void CalcCenteredSphere(const vector<Vector3>& vertices, Vector3& center, float& radius)
+{
+   size_t nv = vertices.size();
+   Vector3 sum;
+   for (size_t i=0; i<nv; ++i)
+      sum += vertices[ i ];
+   center = sum / float(nv);
+   radius = 0.0f;
+   for (size_t i=0; i<nv; ++i){
+      Vector3 diff = vertices[ i ] - center;
+      float mag = diff.Magnitude();
+      radius = max(radius, mag);
+   }
+}
diff --git a/NifCommon/niutils.h b/NifCommon/niutils.h
index adc19d0fefb1ae21073a949c58c53a0455c686bf..2af7484621554c09f2ae117791b94162f96a47bf 100644
--- a/NifCommon/niutils.h
+++ b/NifCommon/niutils.h
@@ -140,6 +140,7 @@ inline bool strmatch(const TCHAR* lhs, const TCHAR* rhs) {
    return (0 == _tcsicmp(lhs, rhs));
 }
 
+bool wildmatch(const TCHAR* match, const TCHAR* value);
 bool wildmatch(const string& match, const std::string& value);
 bool wildmatch(const stringlist& matches, const std::string& value);
 
@@ -211,6 +212,9 @@ extern TSTR FormatText(const TCHAR* format,...);
 extern std::string FormatString(const TCHAR* format,...);
 
 extern stringlist TokenizeString(LPCTSTR str, LPCTSTR delims, bool trim=false);
+extern stringlist TokenizeCommandLine(LPCTSTR str, bool trim);
+extern string JoinCommandLine(stringlist args);
+
 extern string GetIndirectValue(LPCSTR path);
 extern NameValueCollection ReadIniSection(LPCTSTR Section, LPCTSTR iniFileName );
 extern string ExpandQualifiers(const string& src, const NameValueCollection& map);
@@ -277,6 +281,14 @@ static inline Niflib::Color3 TOCOLOR3(const Color& c3) {
    return Niflib::Color3(c3.r, c3.g, c3.b);
 }
 
+static inline Niflib::Color3 TOCOLOR3(const Point3& c3) {
+   return Niflib::Color3(c3.x, c3.y, c3.z);
+}
+
+static inline Point3 TOPOINT3(const Niflib::Color3& c3){
+   return Point3(c3.r, c3.g, c3.b);
+}
+
 static inline Point3 TOPOINT3(const Niflib::Vector3& v){
    return Point3(v.x, v.y, v.z);
 }
@@ -322,6 +334,7 @@ static inline Matrix3 TOMATRIX3(const Niflib::Matrix44 &tm, bool invert = false)
    tm.Decompose(pos, rot, scale);
    Matrix3 m(rot.rows[0].data, rot.rows[1].data, rot.rows[2].data, Point3());
    if (invert) m.Invert();
+   m.Scale(Point3(scale, scale, scale));
    m.SetTrans(Point3(pos.x, pos.y, pos.z));
    return m;
 }
@@ -334,6 +347,10 @@ static inline Niflib::Matrix44 TOMATRIX4(const Matrix3 &tm, bool invert = false)
    return m4;
 }
 
+static inline Point3 GetScale(const Matrix3& mtx){
+   return Point3( fabs(mtx.GetRow(0)[0]), fabs(mtx.GetRow(1)[1]), fabs(mtx.GetRow(2)[2]) );
+}
+
 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) {
@@ -370,5 +387,4 @@ inline Niflib::Ref<T> CreateNiObject() {
    return Niflib::StaticCast<T>(Niflib::CreateObject(T::TypeConst().GetTypeName()));
 }
 
-
 #endif // _NIUTILS_H_
\ No newline at end of file
diff --git a/NifCommon/objectParams.h b/NifCommon/objectParams.h
index 9773e3e0b076371a4e3ad44955e921b8c77061c1..9751883af722427649a892deb78203db58e7cfb7 100644
--- a/NifCommon/objectParams.h
+++ b/NifCommon/objectParams.h
@@ -249,6 +249,7 @@ inline bool setMAXScriptController(ReferenceTarget* obj, LPTSTR name, Control* c
 	return rval;
 }
 
+
 // These helpers are used to convert C++ values to
 // their MAXScript value. Maxscript uses different
 // methods to handle the conversion, and these helpers
diff --git a/NifExport/Animation.cpp b/NifExport/Animation.cpp
index f550c0ecb59afbe979054b392ece73290fd66f44..8b7994569fc955a475046d0e4596a1b04f8e254e 100644
--- a/NifExport/Animation.cpp
+++ b/NifExport/Animation.cpp
@@ -62,7 +62,7 @@ bool AnimationExport::doExport()
    seq->SetStartTime(0.0f);
    seq->SetStopTime(0.0f);
    seq->SetFrequency(1.0f);
-   seq->SetCycleType( NiControllerSequence::CYCLE_CLAMP );
+   seq->SetCycleType( CYCLE_CLAMP );
    seq->SetTargetName("Bip01");
    
    return true;
diff --git a/NifExport/Coll.cpp b/NifExport/Coll.cpp
index d52d33288056989d6ab4719fe3f6ff92d17a8e61..230687f5dc0bf9a625efc2346ca5429d9f489723 100755
--- a/NifExport/Coll.cpp
+++ b/NifExport/Coll.cpp
@@ -121,14 +121,14 @@ int Exporter::addVertex(vector<Vector3> &verts, vector<Vector3> &vnorms, const P
 }
 
 void Exporter::addFace(Triangles &tris, vector<Vector3> &verts, vector<Vector3> &vnorms, 
-			 int face, const int vi[3], Mesh *mesh)
+			 int face, const int vi[3], Mesh *mesh, Matrix3& tm)
 {
 	Triangle tri;
 	for (int i=0; i<3; i++)
 	{
-		tri[i] = addVertex(verts, vnorms,
-			               mesh->verts[ mesh->faces[ face ].v[ vi[i] ] ], 
-						   getVertexNormal(mesh, face, mesh->getRVertPtr(mesh->faces[ face ].v[ vi[i] ])));
+      Point3 pt = VectorTransform(mesh->verts[ mesh->faces[ face ].v[ vi[i] ] ], tm);
+      Point3 norm = VectorTransform(getVertexNormal(mesh, face, mesh->getRVertPtr(mesh->faces[ face ].v[ vi[i] ])), tm);
+		tri[i] = addVertex(verts, vnorms, pt, norm);
 	}
 	tris.push_back(tri);
 }
@@ -258,40 +258,22 @@ Exporter::Result Exporter::exportCollision(NiNodeRef &parent, INode *node)
 	NiNodeRef newParent;
 	if (coll)
 	{
-/*		NiNodeRef n = DynamicCast<NiNode>(CreateBlock("NiNode"));
-		parent->AddChild(DynamicCast<NiAVObject>(n));
-
-		Matrix33 rot;
-		Vector3 trans;
-		TimeValue t = 0;
-		nodeTransform(rot, trans, node, t);
-		n->SetLocalRotation(rot);
-		n->SetLocalTranslation(trans);
-		string name = (char*)node->GetName();
-		n->SetName(name);
-
-
-/*		Vector3 trans;
-		QuaternionXYZW q;
-		TimeValue t = 0;
-		nodeTransform(q, trans, node, t, false);
-		body->SetRotation(q);
-		body->SetTranslation(Vector3(trans.x/7, trans.y/7, trans.z/7));
-*/
 		newParent = nodeParent; // always have collision one level up?
 
-		bhkSphereRepShapeRef shape = makeCollisionShape(node);
+      TimeValue t = 0;
+      Matrix3 tm = getTransform(node, t, local);
+      Matrix44 rm4 = TOMATRIX4(tm, false);
+      Vector3 trans; Matrix33 rm; float scale;
+      rm4.Decompose(trans, rm, scale);
+      QuaternionXYZW q = TOQUATXYZW(rm.AsQuaternion());
+
+		bhkSphereRepShapeRef shape = makeCollisionShape(node, tm);
 		bhkRigidBodyRef body = makeCollisionBody(node);
 		body->SetShape(DynamicCast<bhkShape>(shape));
-
-      QuaternionXYZW q;
-      Vector3 trans;
-      TimeValue t = 0;
-      nodeTransform(q, trans, node, t, false);
       body->SetRotation(q);
       body->SetTranslation(trans / 7.0f);
 
-		bhkCollisionObjectRef co = DynamicCast<bhkCollisionObject>(CreateBlock("bhkCollisionObject"));
+		bhkCollisionObjectRef co = new bhkCollisionObject();
 		co->SetBody(DynamicCast<NiObject>(body));
       
 		co->SetParent(newParent);
@@ -347,10 +329,10 @@ bhkRigidBodyRef Exporter::makeCollisionBody(INode *node)
 	// setup body
 	bhkRigidBodyRef body = DynamicCast<bhkRigidBody>(CreateBlock("bhkRigidBodyT"));
 
-	body->SetLayer(lyr);
-	body->SetLayerCopy(lyr);
-	body->SetMotionSystem(msys);
-	body->SetQualityType(qtype);
+	body->SetLayer(OblivionLayer(lyr));
+	body->SetLayerCopy(OblivionLayer(lyr));
+	body->SetMotionSystem(MotionSystem(msys));
+	body->SetQualityType(MotionQuality(qtype));
 	body->SetMass(mass);
 	body->SetLinearDamping(lindamp);
 	body->SetAngularDamping(angdamp);
@@ -366,36 +348,37 @@ bhkRigidBodyRef Exporter::makeCollisionBody(INode *node)
 	return body;
 }
 
-bhkSphereRepShapeRef Exporter::makeCollisionShape(INode *node)
+bhkSphereRepShapeRef Exporter::makeCollisionShape(INode *node, const Matrix3& tm)
 {
 	bhkSphereRepShapeRef shape;
 	
 	TimeValue t = 0;
 	ObjectState os = node->EvalWorldState(t); 
 	if (os.obj->ClassID() == SCUBA_CLASS_ID)
-		shape = makeCapsuleShape(os.obj);
+		shape = makeCapsuleShape(os.obj, tm);
 	else
 	if (os.obj->ClassID() == Class_ID(BOXOBJ_CLASS_ID, 0))
-		shape = makeBoxShape(os.obj);
+		shape = makeBoxShape(os.obj, tm);
 	else
 	if (os.obj->ClassID() == Class_ID(SPHERE_CLASS_ID, 0))
-		shape = makeSphereShape(os.obj);
+		shape = makeSphereShape(os.obj, tm);
 	else
 	if (os.obj->SuperClassID() == GEOMOBJECT_CLASS_ID)
-		shape = makeTriStripsShape(node);
+		shape = makeTriStripsShape(node, tm);
 
 	if (shape)
 	{
 		int mtl;
 		npGetProp(node, NP_HVK_MATERIAL, mtl, NP_DEFAULT_HVK_MATERIAL);
-		shape->SetMaterial(mtl);
+		shape->SetMaterial(HavokMaterial(mtl));
 	}
 
 	return shape;
 }
 
-bhkSphereRepShapeRef Exporter::makeBoxShape(Object *obj)
+bhkSphereRepShapeRef Exporter::makeBoxShape(Object *obj, const Matrix3& tm)
 {
+   Point3 scale = GetScale(tm);
 	float length = 0;
 	float height = 0;
 	float width = 0; 
@@ -404,26 +387,32 @@ bhkSphereRepShapeRef Exporter::makeBoxShape(Object *obj)
 	params->GetValue(obj->GetParamBlockIndex(BOXOBJ_HEIGHT), 0, height, FOREVER);
 	params->GetValue(obj->GetParamBlockIndex(BOXOBJ_WIDTH), 0, width, FOREVER);
 
-	bhkBoxShapeRef box = DynamicCast<bhkBoxShape>(CreateBlock("bhkBoxShape"));
-	box->SetDimensions(Vector3(width, height, length));
+	bhkBoxShapeRef box = new bhkBoxShape();
+	box->SetDimensions(Vector3(width * scale[0], height * scale[1], length * scale[2]));
 
 	return bhkSphereRepShapeRef(DynamicCast<bhkSphereRepShape>(box));
 }
 
-bhkSphereRepShapeRef Exporter::makeSphereShape(Object *obj)
+bhkSphereRepShapeRef Exporter::makeSphereShape(Object *obj, const Matrix3& tm)
 {
+   Point3 scale = GetScale(tm);
+   float s = (scale[0] + scale[1] + scale[2]) / 3.0;
+
 	float radius = 0;
 	IParamArray *params = obj->GetParamBlock();
 	params->GetValue(obj->GetParamBlockIndex(SPHERE_RADIUS), 0, radius, FOREVER);
 
-	bhkSphereShapeRef sphere = DynamicCast<bhkSphereShape>(CreateBlock("bhkSphereShape"));
-	sphere->SetRadius(radius);
+	bhkSphereShapeRef sphere = new bhkSphereShape();
+	sphere->SetRadius(radius * s);
 
 	return bhkSphereRepShapeRef(DynamicCast<bhkSphereRepShape>(sphere));
 }
 
-bhkSphereRepShapeRef Exporter::makeCapsuleShape(Object *obj)
+bhkSphereRepShapeRef Exporter::makeCapsuleShape(Object *obj, const Matrix3& tm)
 {
+   Point3 scale = GetScale(tm);
+   float s = (scale[0] + scale[1] + scale[2]) / 3.0;
+
 	float radius = 0;
 	float height = 0;
 	IParamArray *params = obj->GetParamBlock();
@@ -435,10 +424,12 @@ bhkSphereRepShapeRef Exporter::makeCapsuleShape(Object *obj)
 	return bhkSphereRepShapeRef(DynamicCast<bhkSphereRepShape>(capsule));
 }
 
-bhkSphereRepShapeRef Exporter::makeTriStripsShape(INode *node)
+bhkSphereRepShapeRef Exporter::makeTriStripsShape(INode *node, const Matrix3& tm)
 {
 	TimeValue t = 0;
-	Matrix3 tm = node->GetObjTMAfterWSM(t);
+   Matrix3 sm = ScaleMatrix( GetScale(tm) );
+   
+	//Matrix3 tm = node->GetObjTMAfterWSM(t);
 
 	// Order of the vertices. Get 'em counter clockwise if the objects is
 	// negatively scaled.
@@ -470,7 +461,7 @@ bhkSphereRepShapeRef Exporter::makeTriStripsShape(INode *node)
 	Triangles		tris;
 
 	for (int i=0; i<mesh->getNumFaces(); i++)
-		addFace(tris, verts, vnorms, i, vi, mesh);
+		addFace(tris, verts, vnorms, i, vi, mesh, sm);
 
 	TriStrips strips;
 	strippify(strips, verts, vnorms, tris);
@@ -482,30 +473,7 @@ bhkSphereRepShapeRef Exporter::makeTriStripsShape(INode *node)
 	bhkNiTriStripsShapeRef shape = DynamicCast<bhkNiTriStripsShape>(CreateBlock("bhkNiTriStripsShape"));
 	shape->SetNumStripsData(1);
 	shape->SetStripsData(0, data);
-/*
-	array<float, 2> unknownFloats1;
-	uint i1 = 0x3DCCCCCD;
-	uint i2 = 0x004ABE60;
-	unknownFloats1[0] = *((float*)&i1);
-	unknownFloats1[1] = *((float*)&i2);
-	shape->SetUnknownFloats1(unknownFloats1);
-
-	array<float, 3> unknownFloats2;
-	unknownFloats2[0] = 1;
-	unknownFloats2[1] = 1;
-	unknownFloats2[2] = 1;
-	shape->SetUnknownFloats2(unknownFloats2);
-*/
-/*	array<uint, 5> unknownInts1;
-	unknownInts1[4] = 1;
-	shape->SetUnknownInts1(unknownInts1);
-*/
 
-/* Still not handled
-	vector<uint> unknownInts2;
-	unknownInts2.resize(1);
-	shape->SetUnknownInts2(unknownInts2);
-*/
 	if (tri != os.obj)
 		tri->DeleteMe();
 
diff --git a/NifExport/Config.cpp b/NifExport/Config.cpp
index f9ab7303a6158bd3d9ec593149f5a5ae719c2d76..ea3262bd7c8ecb394e070f66be6ca1f64804ae82 100755
--- a/NifExport/Config.cpp
+++ b/NifExport/Config.cpp
@@ -64,6 +64,10 @@ void Exporter::writeConfig(Interface *i)
       SetIniValue(NifExportSection, "FlattenHierarchy", mFlattenHierarchy, iniName);
       SetIniValue(NifExportSection, "RemoveUnreferencedBones", mRemoveUnreferencedBones, iniName);
       SetIniValue(NifExportSection, "SortNodesToEnd", mSortNodesToEnd, iniName);
+      SetIniValue(NifExportSection, "SkeletonOnly", mSkeletonOnly, iniName);
+      SetIniValue(NifExportSection, "Cameras", mExportCameras, iniName);
+      SetIniValue(NifExportSection, "GenerateBoneCollision", mGenerateBoneCollision, iniName);
+      
    }
 }
 
@@ -115,6 +119,10 @@ void Exporter::readConfig(Interface *i)
       mFlattenHierarchy = GetIniValue(NifExportSection, "FlattenHierarchy", false, iniName);
       mRemoveUnreferencedBones = GetIniValue(NifExportSection, "RemoveUnreferencedBones", false, iniName);
       mSortNodesToEnd = GetIniValue(NifExportSection, "SortNodesToEnd", false, iniName);
+      mSkeletonOnly = GetIniValue(NifExportSection, "SkeletonOnly", false, iniName);
+      mExportCameras = GetIniValue(NifExportSection, "Cameras", false, iniName);
+      mGenerateBoneCollision = GetIniValue(NifExportSection, "GenerateBoneCollision", false, iniName);
+      
   }
 }
 
diff --git a/NifExport/Exporter.cpp b/NifExport/Exporter.cpp
index f081dde2c9487879a38e4db40d3a1a93b46fa28c..a13f1df7e507b17431ca5aaf92e39f9895a9b4de 100755
--- a/NifExport/Exporter.cpp
+++ b/NifExport/Exporter.cpp
@@ -2,6 +2,9 @@
 #include "AppSettings.h"
 #include "niutils.h"
 
+#include "obj/BSXFlags.h"
+#include "obj/BSBound.h"
+
 int Exporter::mVersion=013;
 bool Exporter::mSelectedOnly=false;
 bool Exporter::mTriStrips=true;
@@ -23,39 +26,60 @@ bool Exporter::mSortNodesToEnd=false;
 string Exporter::mGameName = "User";
 string Exporter::mNifVersion = "20.0.0.5";
 int Exporter::mNifUserVersion = 0;
+bool Exporter::mSkeletonOnly=false;
+bool Exporter::mExportCameras=false;
+bool Exporter::mGenerateBoneCollision=false;
 
 Exporter::Exporter(Interface *i, AppSettings *appSettings)
    : mI(i), mAppSettings(appSettings)
 {
 }
 
+
 Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
 {
    //root->SetName("Scene Root");
 
-   if (mExportCollision)
+   int nifVersion = GetVersion(Exporter::mNifVersion);
+   mIsBethesda = (nifVersion == VER_20_0_0_5 || nifVersion == VER_20_0_0_4) && (Exporter::mNifUserVersion == 11);
+
+   CalcBoundingBox(node, mBoundingBox);
+
+   if (mSkeletonOnly && mIsBethesda)
+   {
+      BSBoundRef bsb = CreateNiObject<BSBound>();
+      bsb->SetName("BBX");    
+      bsb->SetCenter( TOVECTOR3(mBoundingBox.Center()) );
+      bsb->SetDimensions( TOVECTOR3(mBoundingBox.Width() / 2.0f) );
+      root->AddExtraData(DynamicCast<NiExtraData>(bsb));
+
+      BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
+      bsx->SetName("BSX");
+      bsx->SetFlags( 0x00000007 );
+      root->AddExtraData(DynamicCast<NiExtraData>(bsx));
+   }
+   else if (mExportCollision && mIsBethesda)
    {
 	   BSXFlagsRef bsx = CreateNiObject<BSXFlags>();
 	   bsx->SetName("BSX");
-	   bsx->SetFlags(0x00000002);
+	   bsx->SetFlags( 0x00000002 );
       root->AddExtraData(DynamicCast<NiExtraData>(bsx));
    }
 
    bool ok = exportUPB(root, node);
-   if (!ok && Exporter::mExportCollision)
-   {
-      NiStringExtraDataRef strings = DynamicCast<NiStringExtraData>(CreateBlock("NiStringExtraData"));	
-      strings->SetName("UPB");
-      strings->SetData("Ellasticity = 0.300000\r\nFriction = 0.300000\r\nUnyielding = 0\r\nProxy_Geometry = <None>\r\nUse_Display_Proxy = 0\r\nDisplay_Children = 1\r\nDisable_Collisions = 0\r\nInactive = 0\r\nDisplay_Proxy = <None>\r\nMass = 0.000000\r\nSimulation_Geometry = 2\r\nCollision_Groups = 589825\r\n");
-      root->AddExtraData(DynamicCast<NiExtraData>(strings));
-   }
+   //if (!ok && Exporter::mExportCollision)
+   //{
+   //   NiStringExtraDataRef strings = DynamicCast<NiStringExtraData>(CreateBlock("NiStringExtraData"));	
+   //   strings->SetName("UPB");
+   //   strings->SetData("Ellasticity = 0.300000\r\nFriction = 0.300000\r\nUnyielding = 0\r\nProxy_Geometry = <None>\r\nUse_Display_Proxy = 0\r\nDisplay_Children = 1\r\nDisable_Collisions = 0\r\nInactive = 0\r\nDisplay_Proxy = <None>\r\nMass = 0.000000\r\nSimulation_Geometry = 2\r\nCollision_Groups = 589825\r\n");
+   //   root->AddExtraData(DynamicCast<NiExtraData>(strings));
+   //}
 
 	mNiRoot = root;
 	
-	Result result;
-	result = exportMeshes(root, node);
-	if (result != Ok)
-		return result;
+	Result result = exportNodes(root, node);
+   if (result != Ok)
+      return result;
 		
 	if (mExportCollision)
 	{
@@ -78,122 +102,75 @@ Exporter::Result Exporter::doExport(NiNodeRef &root, INode *node)
 	return Ok;
 }
 
-#if 0
-
-Exporter::Result Exporter::exportMeshes(NiNodeRef &parent, INode *node)
+// Primary recursive decent routine
+Exporter::Result Exporter::exportNodes(NiNodeRef &parent, INode *node)
 {
-	bool coll = npIsCollision(node);
-	if ((coll && !mExportCollision) ||
-		(node->IsHidden() && !mExportHidden && !coll) ||
-		(mSelectedOnly && !node->Selected()))
-		return Skip;
-
-	TimeValue t = 0;
-	NiNodeRef prevParent = parent;
-	if (!coll && node->IsGroupHead())
-	{
-		NiNodeRef n = DynamicCast<NiNode>(CreateBlock("NiNode"));
-		Matrix33 rot;
-		Vector3 trans;
-		nodeTransform(rot, trans, node, t);
-		n->SetLocalRotation(rot);
-		n->SetLocalTranslation(trans);
-		string name = (char*)node->GetName();
-		n->SetName(name);
-
-		parent->AddChild(DynamicCast<NiAVObject>(n));
-		parent = n;
-	}
-
-	Result result;
-
-	ObjectState os = node->EvalWorldState(t); 
-	if (os.obj) 
-	{
-		// We look at the super class ID to determine the type of the object.
-		switch(os.obj->SuperClassID()) 
-		{
-			case GEOMOBJECT_CLASS_ID: 
-/*				if (os.obj->ClassID() == SCUBA_CLASS_ID)
-				{
-					float radius = 0;
-					float height = 0;
-					IParamArray *params = os.obj->GetParamBlock();
-					params->GetValue(os.obj->GetParamBlockIndex(CAPSULE_RADIUS), 0, radius, FOREVER);
-					params->GetValue(os.obj->GetParamBlockIndex(CAPSULE_HEIGHT), 0, height, FOREVER);
-
-					int foo=1+2;
-				} else
-				if (os.obj->ClassID() == Class_ID(BOXOBJ_CLASS_ID, 0))
-				{
-					float length = 0;
-					float height = 0;
-					float width = 0; 
-
-					IParamArray *params = os.obj->GetParamBlock();
-					params->GetValue(os.obj->GetParamBlockIndex(BOXOBJ_LENGTH), 0, length, FOREVER);
-					params->GetValue(os.obj->GetParamBlockIndex(BOXOBJ_HEIGHT), 0, height, FOREVER);
-					params->GetValue(os.obj->GetParamBlockIndex(BOXOBJ_WIDTH), 0, width, FOREVER);
-
-					int foo=1+2;
-
-				} else
-				if (os.obj->ClassID() == Class_ID(SPHERE_CLASS_ID, 0))
-				{
-					float radius = 0;
-
-					IParamArray *params = os.obj->GetParamBlock();
-					params->GetValue(os.obj->GetParamBlockIndex(SPHERE_RADIUS), 0, radius, FOREVER);
-
-					int foo=1+2;
-
-				} else
-				{
-*/
-					if (!coll)
-					{
-
-						result = exportMesh(parent, node, t);
-						if (result != Ok)
-							return result;
-					} /*else
-					{
-
-						if (!makeCollisionHierarchy(mNiRoot, node, t))
-							return Error;
-					}
-				}
-*/
-				break;
-/*
-			case CAMERA_CLASS_ID:
-				if (GetIncludeObjCamera()) ExportCameraObject(node, indentLevel); 
-				break;
-			case LIGHT_CLASS_ID:
-				if (GetIncludeObjLight()) ExportLightObject(node, indentLevel); 
-				break;
-			case SHAPE_CLASS_ID:
-				if (GetIncludeObjShape()) ExportShapeObject(node, indentLevel); 
-				break;
-			case HELPER_CLASS_ID:
-				if (GetIncludeObjHelper()) ExportHelperObject(node, indentLevel); 
-				break;
-			}
-*/
-		}
-	}
-
-	for (int i=0; i<node->NumberOfChildren(); i++) 
-	{
-		Result result = exportMeshes(parent, node->GetChildNode(i));
-		if (result!=Ok && result!=Skip)
-			return result;
-	}
-
-	if (node->IsGroupHead())
-		parent = prevParent;
+   bool coll = npIsCollision(node);
+   if (coll ||	(node->IsHidden() && !mExportHidden && !coll) || (mSelectedOnly && !node->Selected()))
+      return Skip;
+
+   bool local = !mFlattenHierarchy;
+   NiNodeRef nodeParent = mFlattenHierarchy ? mNiRoot : parent;
+
+   NiNodeRef newParent;
+   TimeValue t = 0;
+   ObjectState os = node->EvalWorldState(t); 
+
+   // Always skip bones and bipeds
+   SClass_ID scid = node->SuperClassID();
+   Class_ID ncid = node->ClassID();
+   TSTR nodeName = node->GetName();
+   TSTR nodeClass; node->GetClassName(nodeClass);
+
+   // For some unusual reason, bones named Helper are converted to Meshes and 
+   //   lose their Bone properties except a new node named Bone seem to show up
+   if (node->IsBoneShowing())
+      newParent = exportBone(nodeParent, node);
+   else if (os.obj && os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
+   {
+      TSTR objClass;
+      os.obj->GetClassName(objClass);
+      SClass_ID oscid = os.obj->SuperClassID();
+      Class_ID oncid = os.obj->ClassID();
+      if (  os.obj 
+         && (  os.obj->ClassID() == BONE_OBJ_CLASSID 
+            || os.obj->ClassID() == Class_ID(BONE_CLASS_ID,0)
+            || os.obj->ClassID() == Class_ID(0x00009125,0) /* Biped Twist Helpers */
+            )
+         ) 
+      {
+         newParent = exportBone(nodeParent, node);
+      } 
+      else if (!mSkeletonOnly)
+      {
+         newParent = (mExportExtraNodes) ? makeNode(nodeParent, node, local) : nodeParent;
+
+         Result result;
+         result = exportMesh(newParent, node, t);
+         if (result != Ok)
+            return result;
+      }
+   }
+   else if (mExportCameras && os.obj && os.obj->SuperClassID()==CAMERA_CLASS_ID)
+   {
+      newParent = makeNode(nodeParent, node, local);
+   }
+   else if (mExportLights && os.obj && os.obj->SuperClassID()==LIGHT_CLASS_ID)
+   {
+      return exportLight(nodeParent, node, (GenLight*)os.obj);
+   }
+   else if (isMeshGroup(node) && local) // only create node if local
+   {
+      newParent = makeNode(parent, node, local);
+   } 
+   else
+      newParent = parent;
 
-	return Ok;
+   for (int i=0; i<node->NumberOfChildren(); i++) 
+   {
+      Result result = exportNodes(newParent, node->GetChildNode(i));
+      if (result!=Ok && result!=Skip)
+         return result;
+   }
+   return Ok;
 }
-
-#endif
diff --git a/NifExport/Exporter.h b/NifExport/Exporter.h
index 36a4bfd64fc522188aa58cf8bab3b8a3559a96e8..44dd7b7d7e28f4ac83dde840596bd85ca46c15e7 100755
--- a/NifExport/Exporter.h
+++ b/NifExport/Exporter.h
@@ -53,6 +53,9 @@ public:
    static string        mGameName;
    static string        mNifVersion;
    static int           mNifUserVersion;
+   static bool				mSkeletonOnly;
+   static bool				mExportCameras;
+   static bool          mGenerateBoneCollision;
 
 	Exporter(Interface *i, AppSettings *appSettings);
 
@@ -99,9 +102,11 @@ public:
    AppSettings          *mAppSettings;
    NodeMap              mNodeMap;
    CallbackList         mPostExportCallbacks;
+   bool                 mIsBethesda;
+   Box3                 mBoundingBox;
 
+   Result					exportNodes(NiNodeRef &root, INode *node);
 	Result					exportCollision(NiNodeRef &root, INode *node);
-	Result					exportMeshes(NiNodeRef &root, INode *node);
 
 	/* utility functions */
 	Mtl						*getMaterial(INode *node, int subMtl);
@@ -109,6 +114,7 @@ public:
 	void					convertMatrix(Matrix33 &dst, const Matrix3 &src);
 	void					nodeTransform(Matrix33 &rot, Vector3 &trans, INode *node, TimeValue t, bool local=true);
 	void					nodeTransform(QuaternionXYZW &rot, Vector3 &trans, INode *node, TimeValue t, bool local=true);
+   Matrix3              getTransform(INode *node, TimeValue t, bool local=true);
 	Point3					getVertexNormal(Mesh* mesh, int faceNo, RVertex* rv);
 	bool					equal(const Vector3 &a, const Point3 &b, float thresh);
    BitmapTex				*getTexture(Mtl *mtl);
@@ -120,6 +126,8 @@ public:
 	bool					isCollisionGroup(INode *maxNode, bool root=true);
 	// returns true if the node contains meshes
 	bool					isMeshGroup(INode *maxNode, bool root=true);
+   void              CalcBoundingBox(INode *node, Box3& box, int all=1);
+   void              CalcBoundingSphere(INode *node, Point3 center, float& radius, int all=1);
 
 	/* tristrips */
 	void					strippify(TriStrips &strips, vector<Vector3> &verts, vector<Vector3> &norms, const Triangles &tris);
@@ -150,18 +158,18 @@ public:
 	/* havok & collision */
 	int						addVertex(vector<Vector3> &verts, vector<Vector3> &vnorms, const Point3 &pt, const Point3 &norm);
 	void					addFace(Triangles &tris, vector<Vector3> &verts, vector<Vector3> &vnorms, 
-								int face, const int vi[3], Mesh *mesh);
+								int face, const int vi[3], Mesh *mesh, Matrix3& tm);
 	bool					makeCollisionHierarchy(NiNodeRef &parent, INode *node, TimeValue t);
 	
 	/* creates a bhkRigidBody */
 	bhkRigidBodyRef			makeCollisionBody(INode *node);
 	/* creates a collision shape from a node */
-	bhkSphereRepShapeRef	makeCollisionShape(INode *node);
+   bhkSphereRepShapeRef	makeCollisionShape(INode *node, const Matrix3& tm = Matrix3::Identity);
 
-	bhkSphereRepShapeRef	makeTriStripsShape(INode *node);
-	bhkSphereRepShapeRef	makeBoxShape(Object *obj);
-	bhkSphereRepShapeRef	makeSphereShape(Object *obj);
-	bhkSphereRepShapeRef	makeCapsuleShape(Object *obj);
+	bhkSphereRepShapeRef	makeTriStripsShape(INode *node, const Matrix3& tm);
+	bhkSphereRepShapeRef	makeBoxShape(Object *obj, const Matrix3& tm);
+	bhkSphereRepShapeRef	makeSphereShape(Object *obj, const Matrix3& tm);
+	bhkSphereRepShapeRef	makeCapsuleShape(Object *obj, const Matrix3& tm);
 
    /* skin export */
    bool makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t);
@@ -174,6 +182,8 @@ public:
    bool exportUPB(NiNodeRef &root, INode *node);
    bool removeUnreferencedBones(NiNodeRef node);
    void sortNodes(NiNodeRef node);
+   NiNodeRef exportBone(NiNodeRef parent, INode *node);
+   Result exportLight(NiNodeRef root, INode *node, GenLight* light);
 };
 
 #endif 
diff --git a/NifExport/KfExport.cpp b/NifExport/KfExport.cpp
index 52c73e237afad85223dfb145b36da6e8e0174db3..c865cc3f50e5868ef38819cf1144d256783be6b0 100644
--- a/NifExport/KfExport.cpp
+++ b/NifExport/KfExport.cpp
@@ -110,17 +110,17 @@ const TCHAR *KfExport::AuthorName()
 
 const TCHAR *KfExport::CopyrightMessage() 
 {	
-   return _T("http://niftools.sourceforge.net");
+   return _T("http://www.niftools.org");
 }
 
 const TCHAR *KfExport::OtherMessage1() 
 {		
-   return _T("http://niftools.sourceforge.net");
+   return _T("http://www.niftools.org");
 }
 
 const TCHAR *KfExport::OtherMessage2() 
 {		
-   return _T("http://niftools.sourceforge.net");
+   return _T("http://www.niftools.org");
 }
 
 unsigned int KfExport::Version()
diff --git a/NifExport/Mesh.cpp b/NifExport/Mesh.cpp
index d0aad18150f064cccefd8c80f469dd5931ca0922..5ed4f0b981edd113885236b58c39e30d23b8a1ed 100755
--- a/NifExport/Mesh.cpp
+++ b/NifExport/Mesh.cpp
@@ -8,112 +8,12 @@
 #include "obj/NiSkinInstance.h"
 #include "obj/NiSkinData.h"
 #include "obj/NiSkinPartition.h"
-/*
-
-void FPUtility::GetAlphaVal(void)
-{
-	if(ip->GetSelNodeCount()<1)return;
-	INode *node = ip->GetSelNode(0);
-	if(!node)return;
- 	ObjectState os = node->EvalWorldState(0);
-	Object *obj = os.obj;
-	BOOL delMesh = false;
-
-	if (obj && obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) { 
-		TriObject * tri = NULL;
-		tri = (TriObject *) obj->ConvertToType(0, Class_ID(TRIOBJ_CLASS_ID, 0));
-		if (obj != tri) 
-			delMesh = true; // we own the copy
-		if (tri) 
-		{
-			Mesh * mesh = &(tri->GetMesh());
-			MeshDelta md(*mesh);
-			BOOL support = mesh->mapSupport(MAP_ALPHA);
-			if(support)
-			{
-				UVVert *alpha = mesh->mapVerts(MAP_ALPHA);
-				for(int i=0;igetNumVerts();i++)
-				{
-					float a = alpha[i].x;
-				}
-			}
-
-		}
-
-		if (delMesh)
-			delete tri;
-	}
-}
-				
-*/
-
-
-Exporter::Result Exporter::exportMeshes(NiNodeRef &parent, INode *node)
-{
-	bool coll = npIsCollision(node);
-	if (coll ||	(node->IsHidden() && !mExportHidden && !coll) || (mSelectedOnly && !node->Selected()))
-		return Skip;
-
-   bool local = !mFlattenHierarchy;
-   NiNodeRef nodeParent = mFlattenHierarchy ? mNiRoot : parent;
-
-	NiNodeRef newParent;
-	TimeValue t = 0;
-	ObjectState os = node->EvalWorldState(t); 
-
-   // Always skip bones and bipeds
-   SClass_ID scid = node->SuperClassID();
-   Class_ID ncid = node->ClassID();
-   TSTR nodeName = node->GetName();
-   TSTR nodeClass; node->GetClassName(nodeClass);
-
-   // For some unusual reason, bones named Helper are converted to Meshes and 
-   //   lose their Bone properties except a new node named Bone seem to show up
-   if (node->IsBoneShowing() || strmatch(nodeName, "Bone"))
-      newParent = makeNode(nodeParent, node, local);
-   else if (os.obj && os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID)
-	{
-      TSTR objClass;
-      os.obj->GetClassName(objClass);
-      SClass_ID oscid = os.obj->SuperClassID();
-      Class_ID oncid = os.obj->ClassID();
-      if (  os.obj 
-         && (  os.obj->ClassID() == BONE_OBJ_CLASSID 
-            || os.obj->ClassID() == Class_ID(BONE_CLASS_ID,0)
-            || os.obj->ClassID() == Class_ID(0x00009125,0) /* Biped Twist Helpers */
-            )
-            ) 
-      {
-         newParent = makeNode(nodeParent, node, local);
-      } 
-      else 
-      {
-         newParent = (mExportExtraNodes) ? makeNode(nodeParent, node, local) : nodeParent;
-
-         Result result;
-         result = exportMesh(newParent, node, t);
-         if (result != Ok)
-            return result;
-      }
-	} 
-   else if (isMeshGroup(node) && local) // only create node if local
-	{
-		newParent = makeNode(parent, node, local);
-
-	} 
-   else
-		newParent = parent;
-
-	for (int i=0; i<node->NumberOfChildren(); i++) 
-	{
-		Result result = exportMeshes(newParent, node->GetChildNode(i));
-		if (result!=Ok && result!=Skip)
-			return result;
-	}
-
-	return Ok;
-}
-
+#include "obj/NiBSBoneLODController.h"
+#include "obj/NiTransformController.h"
+#include "obj/bhkBlendController.h"
+#include "obj/bhkBlendCollisionObject.h"
+#include "obj/bhkSphereShape.h"
+#include "obj/bhkCapsuleShape.h"
 
 Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue t)
 {	
@@ -127,6 +27,7 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 
 	Mesh *mesh = &tri->GetMesh();
 
+
    // Note that calling setVCDisplayData will clear things like normals so we set this up first
    vector<Color4> vertColors;
    if (mVertexColors)
@@ -169,6 +70,8 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
    MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
    if (NULL != specNorms) {
       specNorms->CheckNormals();
+      if (specNorms->GetNumNormals() == 0)
+         mesh->checkNormals(TRUE);
    } else {
       mesh->checkNormals(TRUE);
    }
@@ -206,6 +109,9 @@ Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue
 				break;
 			}
 
+         if (node->IsHidden())
+            shape->SetHidden(true);
+
          shape->SetName(name);
          shape->SetLocalTransform(tm);
          makeSkin(shape, node, grp->second, t);
@@ -263,7 +169,7 @@ int Exporter::addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matr
    Point3 norm;
 
    MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
-   if (NULL != specNorms)
+   if (NULL != specNorms && specNorms->GetNumNormals() != 0)
       norm = specNorms->GetNormal(face, vi);
    else
       norm = getVertexNormal(mesh, face, mesh->getRVertPtr(vidx));
@@ -471,3 +377,200 @@ Exporter::Result SkinInstance::execute()
    shape->GenHardwareSkinInfo();
    return Exporter::Ok;
 }
+
+static void InitializeTimeController(NiTimeControllerRef ctrl, NiNodeRef parent)
+{
+   ctrl->SetFrequency(1.0f);
+   ctrl->SetStartTime(FloatINF);
+   ctrl->SetStopTime(FloatNegINF);
+   ctrl->SetPhase(0.0f);
+   ctrl->SetFlags(0x0C);
+   ctrl->SetTarget( parent );
+   parent->AddController(DynamicCast<NiTimeController>(ctrl));
+}
+
+static void FillBoneController(Exporter* exporter, NiBSBoneLODControllerRef boneCtrl, INode *node)
+{
+   for (int i=0; i<node->NumberOfChildren(); i++) 
+   {
+      INode * child = node->GetChildNode(i);
+      FillBoneController(exporter, boneCtrl, child);
+
+      TSTR upb;
+      child->GetUserPropBuffer(upb);
+      if (!upb.isNull())
+      {
+         // Check for bonelod and add bones to bone controller
+         stringlist tokens = TokenizeString(upb.data(), "\r\n", true);
+         for (stringlist::iterator itr = tokens.begin(); itr != tokens.end(); ++itr) {
+            string& line = (*itr);
+            if (wildmatch("*#", line)) { // ends with #
+               stringlist bonelod = TokenizeString(line.c_str(), "#", true);
+               for (stringlist::iterator token = bonelod.begin(); token != bonelod.end(); ++token) {
+                  if (  wildmatch("??BoneLOD", (*token).c_str())) {
+                     if (++token == bonelod.end()) 
+                        break;
+                     if (strmatch("Bone", (*token).c_str())) {
+                        if (++token == bonelod.end()) 
+                           break;
+                        int group = 0;
+                        std::stringstream str (*token);
+                        str >> group;
+                        boneCtrl->AddNodeToGroup(group, exporter->getNode(child->GetName()));
+                     }
+                  }
+               }
+            }
+         }
+      }
+   }
+}
+
+void InitializeRigidBody(bhkRigidBodyRef body, INode *node)
+{
+   float value;
+   if (node->GetUserPropFloat("Mass", value))
+      body->SetMass(value);
+   if (node->GetUserPropFloat("Ellasticity", value))
+      body->SetRestitution(value);
+   if (node->GetUserPropFloat("Friction", value))
+      body->SetFriction(value);
+   if (node->GetUserPropFloat("Unyielding", value))
+      body->SetFriction(value);
+
+   body->SetLayer(OblivionLayer(OL_BIPED|0x200));
+   body->SetLayerCopy(OblivionLayer(OL_BIPED|0x200));
+
+   Matrix3 tm = node->GetObjTMAfterWSM(0);
+   body->SetRotation( TOQUATXYZW(Quat(tm)) );
+   body->SetTranslation( TOVECTOR3(tm.GetTrans() / 7.0f) );
+}
+
+NiNodeRef Exporter::exportBone(NiNodeRef parent, INode *node)
+{
+   bool local = !mFlattenHierarchy;
+   NiNodeRef newParent = makeNode(parent, node, local);
+
+   // Special Skeleton Only handling routines
+   if (mSkeletonOnly)
+   {
+      InitializeTimeController(new NiTransformController(), newParent);
+
+      bool isBoneRoot = false;
+      if (mIsBethesda)
+      {
+         // Check for Bone Root
+         TSTR upb;
+         node->GetUserPropBuffer(upb);
+         stringlist tokens = TokenizeString(upb.data(), "\r\n", true);
+         for (stringlist::iterator itr = tokens.begin(); itr != tokens.end(); ++itr) {
+            string& line = (*itr);
+            if (wildmatch("*#", line)) { // ends with #
+               stringlist bonelod = TokenizeString(line.c_str(), "#", true);
+               for (stringlist::iterator token = bonelod.begin(); token != bonelod.end(); ++token) {
+                  if (wildmatch("??BoneLOD", (*token).c_str())) {
+                     if (++token == bonelod.end()) 
+                        break;
+                     if (strmatch("BoneRoot", (*token).c_str())) {
+                        isBoneRoot = true;
+                        NiBSBoneLODControllerRef boneCtrl = new NiBSBoneLODController();
+                        InitializeTimeController(boneCtrl, newParent);
+                        FillBoneController(this, boneCtrl, node);
+                        break;
+                     }
+                  }
+               }
+            }
+         }
+         
+         if (!isBoneRoot)
+            InitializeTimeController(new bhkBlendController(), newParent);
+
+         if (mGenerateBoneCollision)
+         {
+            Matrix3 tm = node->GetObjTMAfterWSM(0);
+
+            bhkShapeRef shape;
+            int nc = node->NumberOfChildren();
+            if (nc == 0) {
+               // Nothing
+            } else if (nc == 1) {
+               // Capsule
+               INode *child = node->GetChildNode(0);
+               Matrix3 ctm = Inverse(tm) * child->GetObjTMAfterWSM(0);
+               float len = ctm.GetTrans().Length();
+               float boxLen = mBoundingBox.Width().Length();
+               float ratio = len / boxLen;
+               if ( ratio < 0.05 ) {
+                  // do nothing
+               } else if ( ratio < 0.15 ) {
+                  // Perpendicular Capsule
+                  Point3 center = (ctm.GetTrans() / 2.0f) + tm.GetTrans();
+                  Matrix3 rtm = tm * RotateXMatrix( TORAD(90) );
+                  rtm.SetTranslate(center);
+
+                  Point3 pt1 = VectorTransform( Point3(len, 0.0f, 0.0f), rtm );
+                  Point3 pt2 = VectorTransform( Point3(-len, 0.0f, 0.0f), rtm );
+                  float radius = len / 7.0f / 2.0f ;
+
+                  bhkCapsuleShapeRef capsule = new bhkCapsuleShape();
+                  capsule->SetRadius( radius );
+                  capsule->SetRadius1( radius );
+                  capsule->SetRadius2( radius );
+                  capsule->SetFirstPoint( TOVECTOR3(pt1 / 7.0f) );
+                  capsule->SetSecondPoint( TOVECTOR3(pt2 / 7.0f) );
+                  capsule->SetMaterial(HAV_MAT_SKIN);
+
+                  shape = StaticCast<bhkShape>(capsule);
+               } else {
+                  // Normal Capsule
+                  Point3 center = (ctm.GetTrans() / 2.0f) + tm.GetTrans();
+               }
+            } else {
+               // Sphere
+               float radius = 0.0f;
+               CalcBoundingSphere(node,tm.GetTrans(), radius, 0);
+
+               bhkSphereShapeRef sphere = new bhkSphereShape();
+               sphere->SetRadius(radius / 7.0f);
+               sphere->SetMaterial(HAV_MAT_SKIN);
+               shape = StaticCast<bhkShape>(sphere);
+            }
+
+            if (shape)
+            {
+               bhkBlendCollisionObjectRef blendObj = new bhkBlendCollisionObject();
+               bhkRigidBodyRef body = new bhkRigidBody();
+
+               InitializeRigidBody(body, node);
+               body->SetMotionSystem(MotionSystem(6));
+               body->SetQualityType(MO_QUAL_KEYFRAMED);
+               body->SetShape( StaticCast<bhkShape>(shape) );
+               blendObj->SetBody( StaticCast<NiObject>(body) );
+               newParent->SetCollisionObject( StaticCast<NiCollisionObject>(blendObj) );
+            }
+         }
+      }
+
+
+      if (wildmatch("Bip??", node->GetName()))
+      {
+         NiNodeRef accumNode = new NiNode();
+         accumNode->SetName(FormatString("%s NonAccum", node->GetName()));
+         accumNode->SetLocalTransform(Matrix44::IDENTITY);
+         newParent->AddChild(DynamicCast<NiAVObject>(accumNode));
+
+         // Transfer 
+         if (mIsBethesda) {
+            InitializeTimeController(new bhkBlendController(), accumNode);
+            accumNode->SetCollisionObject(newParent->GetCollisionObject());
+            newParent->SetCollisionObject( NiCollisionObjectRef() );
+         }
+         InitializeTimeController(new NiTransformController(), accumNode);
+         
+         newParent = accumNode;
+      }
+   }
+
+   return newParent;
+}
\ No newline at end of file
diff --git a/NifExport/NifExport.cpp b/NifExport/NifExport.cpp
index 17d12bdddf40f7653e4138257ddb58874b6fba54..c434a040a0617c47aa96e71c101746b1519f789f 100755
--- a/NifExport/NifExport.cpp
+++ b/NifExport/NifExport.cpp
@@ -93,6 +93,9 @@ BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARA
             CheckDlgButton(hWnd, IDC_CHK_HIER, Exporter::mFlattenHierarchy);
             CheckDlgButton(hWnd, IDC_CHK_REM_BONES, Exporter::mRemoveUnreferencedBones);
             CheckDlgButton(hWnd, IDC_CHK_SORTNODES, Exporter::mSortNodesToEnd);
+            CheckDlgButton(hWnd, IDC_CHK_SKEL_ONLY, Exporter::mSkeletonOnly);
+            CheckDlgButton(hWnd, IDC_CHK_CAMERA, Exporter::mExportCameras);
+            CheckDlgButton(hWnd, IDC_CHK_BONE_COLL, Exporter::mGenerateBoneCollision);
 
             string selection = Exporter::mGameName;
             string version = Exporter::mNifVersion;
@@ -149,6 +152,9 @@ BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARA
                   Exporter::mFlattenHierarchy = IsDlgButtonChecked(hWnd, IDC_CHK_HIER);
                   Exporter::mRemoveUnreferencedBones = IsDlgButtonChecked(hWnd, IDC_CHK_REM_BONES);
                   Exporter::mSortNodesToEnd = IsDlgButtonChecked(hWnd, IDC_CHK_SORTNODES);
+                  Exporter::mSkeletonOnly = IsDlgButtonChecked(hWnd, IDC_CHK_SKEL_ONLY);
+                  Exporter::mExportCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA);
+                  Exporter::mGenerateBoneCollision = IsDlgButtonChecked(hWnd, IDC_CHK_BONE_COLL);
 							
 						GetDlgItemText(hWnd, IDC_ED_TEXPREFIX, tmp, MAX_PATH);
 						Exporter::mTexPrefix = tmp;
@@ -183,7 +189,7 @@ BOOL CALLBACK NifExportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARA
 			{
 				if (LOWORD(wParam) == IDC_LBL_LINK)
 				{
-					ShellExecute(hWnd, "open", "http://niftools.sourceforge.net",
+					ShellExecute(hWnd, "open", "http://www.niftools.org",
 						         NULL, NULL, SW_SHOWDEFAULT);
 				}
 			}
@@ -246,17 +252,17 @@ const TCHAR *NifExport::AuthorName()
 
 const TCHAR *NifExport::CopyrightMessage() 
 {	
-	return _T("http://niftools.sourceforge.net");
+	return _T("http://www.niftools.org");
 }
 
 const TCHAR *NifExport::OtherMessage1() 
 {		
-	return _T("http://niftools.sourceforge.net");
+	return _T("http://www.niftools.org");
 }
 
 const TCHAR *NifExport::OtherMessage2() 
 {		
-	return _T("http://niftools.sourceforge.net");
+	return _T("http://www.niftools.org");
 }
 
 unsigned int NifExport::Version()
diff --git a/NifExport/NifExport.rc b/NifExport/NifExport.rc
index 4108ce9142fca89ec69322eb01a90e0cd8a1403b..5c6fc3a057fd45cc1c041fb16a9506a7af96a616 100755
--- a/NifExport/NifExport.rc
+++ b/NifExport/NifExport.rc
@@ -73,43 +73,44 @@ END
 // Dialog
 //
 
-IDD_PANEL DIALOGEX 0, 0, 207, 187
+IDD_PANEL DIALOGEX 0, 0, 205, 203
 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 EXSTYLE WS_EX_TOOLWINDOW
 CAPTION "Export Nif"
 FONT 8, "MS Sans Serif", 0, 0, 0x1
 BEGIN
-    GROUPBOX        "Export:",IDC_STATIC,7,7,81,97
+    GROUPBOX        "Export:",IDC_STATIC,7,7,81,107
     CONTROL         "&Hidden Nodes",IDC_CHK_HIDDEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,17,67,10
     CONTROL         "Co&llision",IDC_CHK_COLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,29,67,10
     CONTROL         "&Vertex Colors",IDC_CHK_VCOLORS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,41,67,10
     CONTROL         "Skin Modifier",IDC_CHK_SKIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,53,67,10
-    CONTROL         "&Animation",IDC_CHK_ANIMATION,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,14,65,67,10
-    CONTROL         "Furniture &Markers",IDC_CHK_FURN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,14,77,67,10
-    CONTROL         "&Lights",IDC_CHK_LIGHTS,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,14,89,67,10
-    GROUPBOX        "Behaviors:",IDC_STATIC,94,6,101,97
+    CONTROL         "&Animation",IDC_CHK_ANIMATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,65,67,10
+    CONTROL         "Furniture &Markers",IDC_CHK_FURN,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,14,101,67,10
+    CONTROL         "&Lights",IDC_CHK_LIGHTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,77,67,10
+    CONTROL         "Cameras",IDC_CHK_CAMERA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,89,67,10
+    GROUPBOX        "Behaviors:",IDC_STATIC,94,7,104,107
     CONTROL         "Generate &Strips",IDC_CHK_STRIPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,17,88,10
-    CONTROL         "&Remap Indices",IDC_CHK_REMAP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,102,89,88,10
     CONTROL         "Extra Nodes on Mesh",IDC_CHK_EXTRA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,28,88,11
     CONTROL         "Add User Prop Buffer",IDC_CHK_UPB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,40,88,11
     CONTROL         "Flatten Hierarchy",IDC_CHK_HIER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,52,88,10
     CONTROL         "Remove Extra Bones",IDC_CHK_REM_BONES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,64,88,10
     CONTROL         "Sort Nodes",IDC_CHK_SORTNODES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,76,88,10
-    CONTROL         "",IDC_SPIN,"SpinnerControl",NOT WS_VISIBLE,193,7,7,10
-    CONTROL         "",IDC_EDIT,"CustEdit",NOT WS_VISIBLE | WS_TABSTOP,194,18,6,10
-    EDITTEXT        IDC_ED_WELDTHRESH,189,30,11,12,ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED
-    LTEXT           "Auto-&Weld",IDC_LBL_WELDTHRESH,192,42,8,8,NOT WS_VISIBLE | WS_DISABLED
-    LTEXT           "Default Texture &Prefix:",IDC_STATIC,7,111,71,8
-    EDITTEXT        IDC_ED_TEXPREFIX,7,122,190,12,ES_AUTOHSCROLL
-    LTEXT           "Game",IDC_STATIC,7,138,66,8
-    COMBOBOX        IDC_CB_GAME,7,149,105,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    LTEXT           "Version",IDC_STATIC,117,138,39,8
-    EDITTEXT        IDC_CB_VERSION,117,149,45,12,ES_AUTOHSCROLL
-    LTEXT           "User",IDC_STATIC,167,138,25,8
-    EDITTEXT        IDC_CB_USER_VERSION,167,149,30,12,ES_AUTOHSCROLL
-    DEFPUSHBUTTON   "&Export",IDOK,5,166,34,14
-    PUSHBUTTON      "&Cancel",IDCANCEL,45,166,33,14
-    LTEXT           "http://niftools.sourceforge.net",IDC_LBL_LINK,105,166,95,14,SS_NOTIFY | SS_CENTERIMAGE
+    CONTROL         "&Remap Indices",IDC_CHK_REMAP,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,102,100,88,10
+    CONTROL         "Skeleton Only",IDC_CHK_SKEL_ONLY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,88,71,10
+    LTEXT           "Default Texture &Prefix:",IDC_STATIC,7,127,71,8
+    EDITTEXT        IDC_ED_TEXPREFIX,7,138,190,12,ES_AUTOHSCROLL
+    LTEXT           "Game",IDC_STATIC,7,154,66,8
+    COMBOBOX        IDC_CB_GAME,7,165,105,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Version",IDC_STATIC,117,154,39,8
+    EDITTEXT        IDC_CB_VERSION,117,165,45,12,ES_AUTOHSCROLL
+    LTEXT           "User",IDC_STATIC,167,154,25,8
+    EDITTEXT        IDC_CB_USER_VERSION,167,165,30,12,ES_AUTOHSCROLL
+    DEFPUSHBUTTON   "&Export",IDOK,5,182,34,14
+    PUSHBUTTON      "&Cancel",IDCANCEL,45,182,33,14
+    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,103,182,95,14,SS_NOTIFY | SS_CENTERIMAGE
+    EDITTEXT        IDC_ED_WELDTHRESH,185,118,11,12,ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED
+    LTEXT           "Auto-&Weld",IDC_LBL_WELDTHRESH,175,118,8,8,NOT WS_VISIBLE | WS_DISABLED
+    CONTROL         "Gen. Bone Collision",IDC_CHK_BONE_COLL,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,100,118,73,10
 END
 
 
@@ -124,9 +125,9 @@ BEGIN
     IDD_PANEL, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 200
+        RIGHTMARGIN, 198
         TOPMARGIN, 7
-        BOTTOMMARGIN, 180
+        BOTTOMMARGIN, 196
     END
 END
 #endif    // APSTUDIO_INVOKED
diff --git a/NifExport/Util.cpp b/NifExport/Util.cpp
index b310bc015a17b53053b298b36814ec532b75af0f..4c161845b722d49fae0b71c8d0ba9aefc559a309 100755
--- a/NifExport/Util.cpp
+++ b/NifExport/Util.cpp
@@ -1,4 +1,9 @@
 #include "pch.h"
+#include <obj/NiLight.h>
+#include <obj/NiAmbientLight.h>
+#include <obj/NiPointLight.h>
+#include <obj/NiDirectionalLight.h>
+#include <obj/NiSpotLight.h>
 
 bool Exporter::TMNegParity(const Matrix3 &m)
 {
@@ -56,15 +61,21 @@ void Exporter::convertMatrix(Matrix33 &dst, const Matrix3 &src)
 			r2.x, r2.y, r2.z);
 }
 
+Matrix3 Exporter::getTransform(INode *node, TimeValue t, bool local)
+{
+   Matrix3 tm = node->GetObjTMAfterWSM(t);
+   if (local)
+   {
+      Matrix3 pm = node->GetParentTM(t);
+      pm.Invert();
+      tm *= pm;
+   }
+   return tm;
+}
+
 void Exporter::nodeTransform(Matrix33 &rot, Vector3 &trans, INode *node, TimeValue t, bool local)
 {
-	Matrix3 tm = node->GetObjTMAfterWSM(t);
-	if (local)
-	{
-		Matrix3 pm = node->GetParentTM(t);
-		pm.Invert();
-		tm *= pm;
-	}
+	Matrix3 tm = getTransform(node, t, local);
 	convertMatrix(rot, tm);
 	trans.Set(tm.GetTrans().x, tm.GetTrans().y, tm.GetTrans().z);
 }
@@ -255,4 +266,149 @@ void Exporter::sortNodes(NiNodeRef node)
    vector<NiNodeRef> children = DynamicCast<NiNode>(node->GetChildren());
    for (vector<NiNodeRef>::iterator itr = children.begin(); itr != children.end(); ++itr)
       sortNodes(*itr);
-}
\ No newline at end of file
+}
+
+
+Exporter::Result Exporter::exportLight(NiNodeRef parent, INode *node, GenLight* light)
+{
+   TimeValue t = 0;
+   NiLightRef niLight;
+   switch (light->Type())
+   {
+   case OMNI_LIGHT:
+      {
+         if (light->GetAmbientOnly())
+         {
+            niLight = new NiAmbientLight();
+         }
+         else
+         {
+            NiPointLightRef pointLight = new NiPointLight();
+            float atten = light->GetAtten(t, ATTEN_START);
+            switch (light->GetDecayType())
+            {
+            case 0: pointLight->SetConstantAttenuation(1.0f); break;
+            case 1: pointLight->SetLinearAttenuation( atten / 4.0f ); break;
+            case 2: pointLight->SetQuadraticAttenuation( sqrt(atten / 4.0f) ); break;
+            }
+            niLight = StaticCast<NiLight>(pointLight);
+         }
+     }
+      break;
+   case TSPOT_LIGHT:
+   case FSPOT_LIGHT:
+      niLight = new NiSpotLight();
+      break;
+   case DIR_LIGHT:
+   case TDIR_LIGHT:
+      niLight = new NiDirectionalLight();
+      break;
+   }
+   if (niLight == NULL)
+      return Skip;
+
+   niLight->SetName(node->GetName());
+
+   Matrix3 tm = getTransform(node, t, !mFlattenHierarchy);
+   niLight->SetLocalTransform( TOMATRIX4(tm, false) );
+
+   niLight->SetDimmer( light->GetIntensity(0) );
+   Color3 rgbcolor = TOCOLOR3( light->GetRGBColor(0) );
+   if (light->GetAmbientOnly())
+   {
+      niLight->SetDiffuseColor(Color3(0,0,0));
+      niLight->SetSpecularColor(Color3(0,0,0));
+      niLight->SetAmbientColor(rgbcolor);
+   }
+   else
+   {
+      niLight->SetDiffuseColor(rgbcolor);
+      niLight->SetSpecularColor(rgbcolor);
+      niLight->SetAmbientColor(Color3(0,0,0));
+   }
+   parent->AddChild( DynamicCast<NiAVObject>(niLight) );
+   return Ok;
+}
+
+void Exporter::CalcBoundingBox(INode *node, Box3& box, int all)
+{
+   if (NULL == node) 
+      return;
+
+   Matrix3 tm = node->GetObjTMAfterWSM(0);
+   if (node->IsBoneShowing()) {
+      box.IncludePoints(&tm.GetTrans(), 1, NULL);
+   } else {
+      if (Object *o = node->GetObjectRef()) {
+         if (o->SuperClassID()==GEOMOBJECT_CLASS_ID) {
+            if (  o->ClassID() == BONE_OBJ_CLASSID 
+               || o->ClassID() == Class_ID(BONE_CLASS_ID,0)
+               || o->ClassID() == Class_ID(0x00009125,0) /* Biped Twist Helpers */
+               )
+            {
+               box.IncludePoints(&tm.GetTrans(), 1, NULL);
+            }
+            else
+            {
+               Box3 local;
+               o->GetLocalBoundBox(0, node, mI->GetActiveViewport(), local);
+               box.IncludePoints(&local.Min(), 1, NULL);
+               box.IncludePoints(&local.Max(), 1, NULL);
+            }
+         }
+         else if (mExportCameras && o->SuperClassID()==CAMERA_CLASS_ID)
+         {
+            box.IncludePoints(&tm.GetTrans(), 1, NULL);
+         }
+      }
+   }
+   if (all < 0)
+      return;
+
+   all = (all>0 ? all : -1);
+   for (int i=0; i<node->NumberOfChildren(); i++) {
+      CalcBoundingBox(node->GetChildNode(i), box, all );
+   }
+}
+
+void Exporter::CalcBoundingSphere(INode *node, Point3 center, float& radius, int all)
+{
+   if (NULL == node) 
+      return;
+
+   Matrix3 tm = node->GetObjTMAfterWSM(0);
+   Point3 pt = (tm.GetTrans() - center);
+   float len = pt.Length();
+
+   if (node->IsBoneShowing()) {
+      radius = max(len, radius);
+   } else {
+      if (Object *o = node->GetObjectRef()) {
+         if (o->SuperClassID()==GEOMOBJECT_CLASS_ID) {
+            if (  o->ClassID() == BONE_OBJ_CLASSID 
+               || o->ClassID() == Class_ID(BONE_CLASS_ID,0)
+               || o->ClassID() == Class_ID(0x00009125,0) /* Biped Twist Helpers */
+               )
+            {
+               radius = max(len, radius);
+            }
+            else
+            {
+               radius = max(len, radius);
+            }
+         }
+         else if (mExportCameras && o->SuperClassID()==CAMERA_CLASS_ID)
+         {
+            radius = max(len, radius);
+         }
+      }
+   }
+   if (all < 0)
+      return;
+
+   all = (all>0 ? all : -1);
+   for (int i=0; i<node->NumberOfChildren(); i++) {
+      CalcBoundingSphere(node->GetChildNode(i), center, radius, all );
+   }
+}
+
diff --git a/NifExport/resource.h b/NifExport/resource.h
index 26b12888647b45a91583ef8ae799de1149d1f1fe..2aeda3876b1813200b7f14bf54146135089fb35a 100755
--- a/NifExport/resource.h
+++ b/NifExport/resource.h
@@ -31,8 +31,11 @@
 #define IDC_CHK_UPB                     1020
 #define IDC_CHK_HIER                    1021
 #define IDC_CHK_REM_BONES               1022
-#define IDC_CHECK7                      1023
 #define IDC_CHK_SORTNODES               1023
+#define IDC_CHK_SKEL_ONLY               1024
+#define IDC_CHK_CAMERA                  1025
+#define IDC_CHK_SKEL_ONLY2              1026
+#define IDC_CHK_BONE_COLL               1026
 #define IDC_COLOR                       1456
 #define IDC_EDIT                        1490
 #define IDC_SPIN                        1496
@@ -43,7 +46,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        102
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1024
+#define _APS_NEXT_CONTROL_VALUE         1025
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
diff --git a/NifImport/ImportAnimation.cpp b/NifImport/ImportAnimation.cpp
index 590949ba7d6602cb037a529cb659b06b62258387..72ee9f4f15dfe28230dc9ea07a190fde8caa44ad 100644
--- a/NifImport/ImportAnimation.cpp
+++ b/NifImport/ImportAnimation.cpp
@@ -11,10 +11,13 @@ HISTORY:
 *>	Copyright (c) 2006, All Rights Reserved.
 **********************************************************************/
 #include "stdafx.h"
+#include <IFrameTagManager.h>
+#include <notetrck.h>
 #include "MaxNifImport.h"
 #include "NIFImporter.h"
 #include "KFMImporter.h"
 #include "KFImporter.h"
+#include "AnimKey.h"
 #include <obj/NiInterpolator.h>
 #include <obj/NiTransformInterpolator.h>
 #include <obj/NiTransformData.h>
@@ -35,7 +38,12 @@ enum {
    IPOS_W_REF	=	3,
 };
 
-#include "AnimKey.h"
+void* operator new(size_t size, NoteKey* stub )
+{ return MAX_new(size); }
+
+void operator delete(void* memblock, NoteKey* stub )
+{ return MAX_delete(memblock); }
+
 
 struct AnimationImport
 {
@@ -135,6 +143,138 @@ void NifImporter::ClearAnimation(INode *node)
       }
    }
 }
+void NifImporter::ClearAnimation()
+{
+   if (clearAnimation)
+   {
+      if (IFrameTagManager *tagMgr = (IFrameTagManager*)GetCOREInterface(FRAMETAGMANAGER_INTERFACE)) {
+
+         int n = tagMgr->GetTagCount();
+         for (int i=n-1; i>=0; --i){
+            tagMgr->DeleteTag( tagMgr->GetTagID(i) );
+         }
+      }
+      ClearAnimation(gi->GetRootNode());
+   }
+}
+
+FPValue GetScriptedProperty( FPValue& thing, TCHAR* propName ) {
+   init_thread_locals();
+   push_alloc_frame();
+   two_value_locals( thingValue, propNameValue );
+   save_current_frames();
+   trace_back_active = FALSE;
+
+   FPValue retVal( TYPE_INT, 0 );
+   BOOL isUndefined = ((thing.type==TYPE_VALUE) && (thing.v==&undefined));
+   if( (thing.i!=0) && (!isUndefined) ) try { //Safe handling for NULL
+      vl.thingValue = InterfaceFunction::FPValue_to_val( thing );
+      vl.propNameValue = Name::intern( propName );
+      vl.thingValue = vl.thingValue->get_property( &vl.propNameValue, 1 );
+      vl.thingValue->to_fpvalue( retVal );
+   }
+   catch ( ... ) {
+      clear_error_source_data();
+      restore_current_frames();
+      MAXScript_signals = 0;
+      if (progress_bar_up)
+         MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE;
+   }
+   pop_value_locals();
+   pop_alloc_frame();
+   return retVal;
+}
+
+Value* GetFunction( Value* thing, TCHAR* funcName ) {
+   init_thread_locals();
+   push_alloc_frame();
+   one_value_local( funcNameValue );
+   save_current_frames();
+   trace_back_active = FALSE;
+
+   Value* retval = NULL;
+   if( (thing!=0) ) try { //Safe handling for NULL
+      vl.funcNameValue = Name::intern( funcName );
+      retval = thing->get_property( &vl.funcNameValue, 1 );
+   }
+   catch ( ... ) {
+      clear_error_source_data();
+      restore_current_frames();
+      MAXScript_signals = 0;
+      if (progress_bar_up)
+         MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE;
+   }
+   pop_value_locals();
+   pop_alloc_frame();
+   return retval;
+}
+
+static FPValue myAddNewNoteKey(Value* noteTrack, int frame)
+{
+   // Magic initialization stuff for maxscript.
+   static bool script_initialized = false;
+   if (!script_initialized) {
+      init_MAXScript();
+      script_initialized = TRUE;
+   }
+   init_thread_locals();
+   push_alloc_frame();
+   five_value_locals(name, fn, track, frame, result);
+   save_current_frames();
+   trace_back_active = FALSE;
+
+   FPValue retVal( TYPE_INT, 0 );
+   try	{
+      // Create the name of the maxscript function we want.
+      // and look it up in the global names
+      vl.name = Name::intern(_T("addNewNoteKey"));
+      vl.fn = globals->get(vl.name);
+
+      // For some reason we get a global thunk back, so lets
+      // check the cell which should point to the function.
+      // Just in case if it points to another global thunk
+      // try it again.
+      while (vl.fn != NULL && is_globalthunk(vl.fn))
+         vl.fn = static_cast<GlobalThunk*>(vl.fn)->cell;
+      while (vl.fn != NULL && is_constglobalthunk(vl.fn))
+         vl.fn = static_cast<ConstGlobalThunk*>(vl.fn)->cell;
+
+      // Now we should have a MAXScriptFunction, which we can
+      // call to do the actual conversion. If we didn't
+      // get a MAXScriptFunction, we can't convert.
+      if (vl.fn != NULL && vl.fn->_is_function()) {
+         Value* args[4];
+
+         // Ok. convertToArchMat takes one parameter, the material
+         // and an optional keyword paramter, replace, which tells
+         // convertToArchMat whether to replace all reference to
+         // the old material by the new one.
+         args[0] = vl.track = noteTrack;	// The original material
+         args[1] = Integer::intern(frame);
+         args[2] = &keyarg_marker;						// Separates keyword params from mandatory
+
+         // Call the funtion and save the result.
+         vl.result = vl.fn->apply(args, 2);
+
+         // If the result isn't NULL, try to convert it to a material.
+         // If the convesion fails, an exception will be thrown.
+         if (vl.result != NULL)
+            vl.result->to_fpvalue(retVal);
+      }
+   } catch (...) {
+      clear_error_source_data();
+      restore_current_frames();
+      MAXScript_signals = 0;
+      if (progress_bar_up)
+         MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE;
+   }
+
+   // Magic Max Script stuff to clear the frame and locals.
+   pop_value_locals();
+   pop_alloc_frame();
+
+   return retVal;
+}
 
 bool KFMImporter::ImportAnimation()
 {
@@ -151,8 +291,89 @@ bool KFMImporter::ImportAnimation()
       float maxTime = 0.0f;
 
       NiControllerSequenceRef cntr = (*itr);
+      float start = cntr->GetStartTime();
+      float stop = cntr->GetStopTime();
+      float total = (stop - start);
+
       vector<ControllerLink> links = cntr->GetControllerData();
 
+      NiTextKeyExtraDataRef textKeyData = cntr->GetTextKeyExtraData();
+      vector<StringKey> textKeys = textKeyData->GetKeys();
+      if (!textKeys.empty()) {
+
+         if (addNoteTracks) {
+            string target = cntr->GetTargetName();
+            if ( INode *n = gi->GetINodeByName(target.c_str()) ) {
+
+               TSTR script;
+               script += 
+               "fn getActorManager obj = (\n"
+               "   local nt = undefined\n"
+               "   n = numNoteTracks obj\n"
+               "   for i = 1 to n do (\n"
+               "      local nt = getNoteTrack obj i\n"
+               "      if (nt.name == \"ActorManager\") then ( return nt )\n"
+               "   )\n"
+               "   nt = notetrack \"ActorManager\"\n"
+               "   addNoteTrack obj nt\n"
+               "   return nt\n"
+               ")\n"
+               "fn addNoteKey nt frame tag = (\n"
+               "   local Key = addNewNoteKey nt.keys frame\n"
+               "   Key.value = tag\n"
+               ")\n";
+
+               script += FormatText("nt = getActorManager $'%s'\n", target.c_str());
+               
+               for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) {
+                  TimeValue t = TimeToFrame(time + (*itr).time) + 1;
+
+                  if (wildmatch("start*", (*itr).data)){
+                     stringlist args = TokenizeCommandLine((*itr).data.c_str(), true);
+                     if (args.empty()) continue;
+                     bool hasName = false;
+                     bool hasLoop = false;
+                     CycleType ct = cntr->GetCycleType();
+                     for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) {
+                        if (strmatch("-name", *itr)) {
+                           if (++itr == args.end()) break;                       
+                           hasName = true;
+                        } else if (strmatch("-loop", *itr)) {
+                           hasLoop = true;
+                        }
+                     }
+                     if (!hasName) {
+                        string name = cntr->GetName();
+                        if (name.empty())
+                           name = FormatString("EMPTY_SEQUENCE_AT_%df", int(t * FramesPerSecond / TicksPerFrame) );
+                        args.push_back("-name");
+                        args.push_back(name);
+                     }
+                     if (!hasLoop && ct == CYCLE_LOOP) {
+                        args.push_back("-loop");
+                     }
+                     
+                     string line = JoinCommandLine(args);
+                     script += FormatText("addNoteKey nt (%d/ticksPerFrame) \"%s\"\n", t, line.c_str());
+                  } else {
+                     script += FormatText("addNoteKey nt (%d/ticksPerFrame) \"%s\"\n", t, (*itr).data.c_str());
+                  }
+
+                  //NoteKey *key = new NoteKey(TimeToFrame(time + (*itr).time), (*itr).data.c_str(), 0);
+                  //nt->keys.Append(1, &key);
+               }
+               ExecuteMAXScriptScript(script, TRUE, NULL);
+            }
+         }
+
+         if (addTimeTags) {
+            if (IFrameTagManager *tagMgr = (IFrameTagManager*)GetCOREInterface(FRAMETAGMANAGER_INTERFACE)) {
+               for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) {
+                  tagMgr->CreateNewTag(const_cast<TCHAR*>((*itr).data.c_str()), TimeToFrame(time + (*itr).time), 0, FALSE);
+               }
+            }
+         }
+      }
       for (vector<ControllerLink>::iterator lnk=links.begin(); lnk != links.end(); ++lnk)
       {
          string name = (*lnk).targetName;
@@ -175,10 +396,6 @@ bool KFMImporter::ImportAnimation()
 
          INode *n = gi->GetINodeByName(name.c_str());
 
-         float start = cntr->GetStartTime();
-         float stop = cntr->GetStopTime();
-         float total = (stop - start);
-
          NiKeyframeDataRef data;
          Point3 p; Quat q; float s;
          if (ai.GetTransformData(*lnk, name, data, p, q, s)) {
diff --git a/NifImport/ImportLights.cpp b/NifImport/ImportLights.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a48feb36f440ee9ee66f247d99c056ec2e1da35d
--- /dev/null
+++ b/NifImport/ImportLights.cpp
@@ -0,0 +1,116 @@
+/**********************************************************************
+*<
+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/NiLight.h>
+#include <obj/NiAmbientLight.h>
+#include <obj/NiPointLight.h>
+#include <obj/NiDirectionalLight.h>
+#include <obj/NiSpotLight.h>
+#include <float.h>
+#include <dummy.h>
+
+using namespace Niflib;
+
+// Targeted Lights
+// Normal Lights
+static GenLight *CreateLight(Interface *gi, const NiLightRef& light, int lightType)
+{
+   if (GenLight *ob = gi->CreateLightObject(lightType)) {
+      float dimmer = light->GetDimmer();
+      Point3 ambient = TOPOINT3(light->GetAmbientColor());
+      Point3 diffuse = TOPOINT3(light->GetDiffuseColor());
+      Point3 specular = TOPOINT3(light->GetSpecularColor());
+      Point3 black(0,0,0);
+
+      ob->Enable(1);
+      ob->SetUseLight(1);
+      ob->SetIntensity(0, dimmer);
+      if (diffuse != black) {
+         ob->SetAffectDiffuse(TRUE);
+         ob->SetRGBColor(0, diffuse);
+      } else {
+         ob->SetAffectDiffuse(FALSE);
+      }
+      if (specular != black) {
+         ob->SetAffectSpecular(TRUE);
+         ob->SetRGBColor(0, specular);
+      } else {
+         ob->SetAffectSpecular(FALSE);
+      }
+      return ob;
+   }
+   return NULL;
+}
+
+bool NifImporter::ImportLights(NiNodeRef node)
+{
+   bool ok = false;
+   vector<NiAVObjectRef> children = node->GetChildren();
+   ok |= ImportLights(DynamicCast<NiLight>(children));
+   vector<NiNodeRef> childNodes = DynamicCast<NiNode>(children);
+   for (vector<NiNodeRef>::iterator itr=childNodes.begin(), end=childNodes.end(); itr != end; ++itr)
+      ok |= ImportLights(*itr);
+   return ok;
+}
+
+bool NifImporter::ImportLights(vector<NiLightRef> lights)
+{
+   bool ok = false;
+   for (vector<NiLightRef>::iterator itr=lights.begin(), end=lights.end(); itr != end; ++itr)
+   {
+      GenLight *ob = NULL;
+      NiLightRef& light = (*itr);
+
+      if (light->IsSameType(NiPointLight::TypeConst())){
+         ob = CreateLight(gi, light, OMNI_LIGHT);
+         NiPointLightRef ptLight = light;
+         float c = ptLight->GetConstantAttenuation();
+         float l = ptLight->GetLinearAttenuation();
+         float q = ptLight->GetQuadraticAttenuation();
+         if (c != 0.0f){
+            ob->SetDecayType(0);
+         } else if (l != 0.0f) {
+            ob->SetDecayType(1);
+            ob->SetUseAtten(1);
+            ob->SetAtten(0, ATTEN_START, 4.0f/l);
+         } else if (q != 0.0f) {
+            ob->SetDecayType(2);
+            ob->SetAtten(0, ATTEN_START, 4.0f/(l*l));
+         }
+      } else if (light->IsSameType(NiDirectionalLight::TypeConst())){
+         ob = CreateLight(gi, light, DIR_LIGHT);
+      } else if (light->IsSameType(NiAmbientLight::TypeConst())){
+         ob = CreateLight(gi, light, OMNI_LIGHT);
+         ob->SetAmbientOnly(TRUE);
+      } else if (light->IsSameType(NiSpotLight::TypeConst())){
+         ob = CreateLight(gi, light, FSPOT_LIGHT);
+      }
+      if (INode *n = gi->CreateObjectNode(ob)) {
+         string name = light->GetName();
+         if (!name.empty()) {
+            n->SetName(const_cast<TCHAR*>(name.c_str()));
+         }
+         Matrix44 m4 = light->GetWorldTransform();
+         Vector3 pos; Matrix33 rot; float scale;
+         m4.Decompose(pos, rot, scale);
+         Point3 p = TOPOINT3(pos);
+         Quat q = TOQUAT(rot.AsQuaternion());
+
+         PosRotScaleNode(n, p, q, scale, prsDefault);
+         n->Hide(light->GetHidden() ? TRUE : FALSE);
+      }
+      ok = true;
+   }
+   return ok;
+}
diff --git a/NifImport/ImportMeshAndSkin.cpp b/NifImport/ImportMeshAndSkin.cpp
index ff29936136fed6914d548d696a9f16cffeaac706..b759cb73dec1cb22f38ac8266057ac937d77e7bb 100644
--- a/NifImport/ImportMeshAndSkin.cpp
+++ b/NifImport/ImportMeshAndSkin.cpp
@@ -74,8 +74,9 @@ bool NifImporter::ImportMesh(ImpNode *node, TriObject *o, NiTriBasedGeomRef triG
    // uv texture info
    {
       int nUVSet = triGeomData->GetUVSetCount();
-      int n = 0;
-      for (int j=0; j<nUVSet; j++){
+      int n = 0, j = 0;
+      //for (int j=0; j<nUVSet; j++){
+      if (nUVSet > 0) {
          vector<TexCoord> texCoords = triGeomData->GetUVSet(j);
          n = texCoords.size();
          mesh.setNumTVerts(n, FALSE);
@@ -414,28 +415,33 @@ bool NifImporter::ImportSkin(ImpNode *node, NiTriBasedGeomRef triGeom)
       int numWeightsPerVertex = 4;
       IParamBlock2 *params = skinMod->GetParamBlockByID(2/*advanced*/);
       params->SetValue(0x7/*bone_Limit*/, 0, numWeightsPerVertex);
+
+      // Can get some truly bizarre animations without this in MAX with Civ4 Leaderheads
+      BOOL ignore = TRUE;
+      params->SetValue(0xE/*ignoreBoneScale*/, 0, ignore);
+
       //RefTargetHandle advanced = skinMod->GetReference(3);
       //setMAXScriptValue(advanced, "bone_Limit", 0, numWeightsPerVertex);
 
+      Matrix3 geom = TOMATRIX3(triGeom->GetLocalTransform());
       Matrix3 m3 = TOMATRIX3(data->GetOverallTransform());
       Matrix3 im3 = Inverse(m3);
-      iskinImport->SetSkinTm(tnode, m3, m3); // ???
+      Matrix3 nm3 = im3 * geom;
+      iskinImport->SetSkinTm(tnode, nm3, nm3); // ???
       // Create Bone List
       Tab<INode*> bones;
       for (size_t i=0; i<nifBones.size(); ++i){
          NiNodeRef bone = nifBones[i];
-         Matrix3 b3 = TOMATRIX3(data->GetBoneTransform(i));
-         Matrix3 ib3 = Inverse(b3);
 
          string name = bone->GetName();
          if (INode *boneRef = gi->GetINodeByName(name.c_str())) {
             bones.Append(1, &boneRef);
             iskinImport->AddBoneEx(boneRef, TRUE);
 
-            // Set Bone Transform
-            Matrix3 tm = ib3;
-            if (applyOverallTransformToSkinAndBones)
-               ib3 *= im3;
+            //// Set Bone Transform
+            Matrix3 b3 = TOMATRIX3(data->GetBoneTransform(i));
+            Matrix3 ib3 = Inverse(b3);
+            ib3 *= geom;
             iskinImport->SetBoneTm(boneRef, ib3, ib3);
          }
       }
diff --git a/NifImport/ImportSkeleton.cpp b/NifImport/ImportSkeleton.cpp
index e6c7df6c0e1d32cc03ca93885087f8d3862befef..0649cb40d39c33b5064beca07cf3f800906f4018 100644
--- a/NifImport/ImportSkeleton.cpp
+++ b/NifImport/ImportSkeleton.cpp
@@ -638,9 +638,11 @@ void NifImporter::ImportBones(NiNodeRef node, bool recurse)
                      || (convertBillboardsToDummyNodes && node->IsDerivedType(NiBillboardNode::TypeConst()))
                       );
          if (wildmatch("Camera*", name)) {
-            if (bone = CreateCamera(name)) {
-               PosRotScaleNode(bone, p, q, scale, prs);
-               bone->Hide(node->GetHidden() ? TRUE : FALSE);
+            if (enableCameras) {
+               if (bone = CreateCamera(name)) {
+                  PosRotScaleNode(bone, p, q, scale, prs);
+                  bone->Hide(node->GetHidden() ? TRUE : FALSE);
+               }
             }
          }else if (isDummy && createNubsForBones)
             bone = CreateHelper(name, p);
@@ -690,10 +692,11 @@ bool NifImporter::ImportUPB(INode *node, Niflib::NiNodeRef block)
       list<NiStringExtraDataRef> strings = DynamicCast<NiStringExtraData>(block->GetExtraData());
       for (list<NiStringExtraDataRef>::iterator itr = strings.begin(); itr != strings.end(); ++itr){
          if (strmatch((*itr)->GetName(), "UserPropBuffer") || strmatch((*itr)->GetName(), "UPB")) {
-            char buffer[1048], *line = buffer;
+            char buffer[1048];
             istringstream istr((*itr)->GetData(), ios_base::out);
             while (!istr.eof()) {
-               line[0] = 0;
+               char *line = buffer;
+               buffer[0] = 0;
                istr.getline(buffer, _countof(buffer)-1);
                if (LPTSTR equals = _tcschr(line, TEXT('='))){
                   *equals++ = 0;
@@ -702,6 +705,18 @@ bool NifImporter::ImportUPB(INode *node, Niflib::NiNodeRef block)
                      node->SetUserPropString(TSTR(line), TSTR(equals));
                      ok |= true;
                   }
+               } else {
+                  Trim(line);
+                  int len = strlen(line);
+                  // Handle bethesda special values?
+                  if (len > 0 && line[len-1] == '#'){
+                     TSTR buf, value;
+                     node->GetUserPropBuffer(buf);
+                     value.append(line).append("\r\n").append(buf);
+                     if (wildmatch("BSBoneLOD#*", value))
+                        value[0] = 'N', value[1] = 'i'; // Use NIBoneLOD to be compatible with Civ4 code
+                     node->SetUserPropBuffer(value);
+                  }
                }
             }
          }
diff --git a/NifImport/KFImporter.cpp b/NifImport/KFImporter.cpp
index 44d73da9ae922e226848f2ef5abde8ba0c377383..43180a3545c7f778ca8857e2e40f8fd6ff4a3d6f 100644
--- a/NifImport/KFImporter.cpp
+++ b/NifImport/KFImporter.cpp
@@ -31,10 +31,14 @@ void KFImporter::ReadBlocks()
 
 bool KFImporter::DoImport()
 {
-   if (clearAnimation)
+   if (!suppressPrompts)
    {
-      ClearAnimation(gi->GetRootNode());
+      if (!ShowDialog())
+         return true;
+      ApplyAppSettings();
+      SaveIniSettings();
    }
 
+   ClearAnimation();
    return ImportAnimation();
 }
diff --git a/NifImport/KFMImporter.cpp b/NifImport/KFMImporter.cpp
index 3b63d88e914430bb29f6d2377ef2141bfa5cd2ef..dd02b8259f9878b63ac1b6f236394053d1776ace 100644
--- a/NifImport/KFMImporter.cpp
+++ b/NifImport/KFMImporter.cpp
@@ -4,6 +4,8 @@
 #include "gen/ControllerLink.h"
 using namespace Niflib;
 
+extern LPCTSTR AnimImportSection;
+
 KFMImporter::KFMImporter(const TCHAR *Name,ImpInterface *I,Interface *GI, BOOL SuppressPrompts)
    : BaseClass()
 {
@@ -29,7 +31,6 @@ void KFMImporter::ReadBlocks()
          TCHAR buffer[MAX_PATH];
          GetFullPathName(name.c_str(), MAX_PATH, buffer, NULL);
          PathRemoveFileSpec(buffer);
-#ifdef USE_UNSUPPORTED_CODE
          string nif_filename = path + '\\' + kfm.nif_filename;
          if (_taccess(nif_filename.c_str(), 0) != -1)
             root = ReadNifTree(nif_filename);
@@ -55,7 +56,6 @@ void KFMImporter::ReadBlocks()
                }
             }
          }
-#endif
       }
    }
    catch (std::exception&)
@@ -68,6 +68,15 @@ void KFMImporter::ReadBlocks()
 
 bool KFMImporter::DoImport()
 {
+   bool ok = true;
+   if (!suppressPrompts)
+   {
+      if (!ShowDialog())
+         return true;
+      ApplyAppSettings();
+      SaveIniSettings();
+   }
+
    // might check if blocks exist and if not go ahead and import nif.
    if (root)
    {
@@ -82,11 +91,14 @@ bool KFMImporter::DoImport()
       }
    }
 
-   if (clearAnimation)
-   {
-      ClearAnimation(gi->GetRootNode());
-   }
-
+   ClearAnimation();
    return ImportAnimation();
    //return BaseClass::DoImport();
 }
+
+void KFMImporter::SaveIniSettings()
+{
+   SetIniValue(AnimImportSection, "ClearAnimation", clearAnimation);
+   SetIniValue(AnimImportSection, "AddNoteTracks", addNoteTracks);
+   SetIniValue(AnimImportSection, "AddTimeTags", addTimeTags);
+}
\ No newline at end of file
diff --git a/NifImport/KFMImporter.h b/NifImport/KFMImporter.h
index f07aa80349cf2dd169c193d8c4fc112030349be5..b673c463f0ce788c3c407a2b51073d8cf0b4aa3b 100644
--- a/NifImport/KFMImporter.h
+++ b/NifImport/KFMImporter.h
@@ -33,7 +33,9 @@ public:
 
    // Implemented in ImportAnimation.cpp
    virtual bool ImportAnimation();
+   virtual void SaveIniSettings();
 
+   bool ShowDialog();
 
    std::vector<Niflib::NiControllerSequenceRef> kf;
 };
diff --git a/NifImport/KfDialog.cpp b/NifImport/KfDialog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f127a74998f50f7d45c05171d2b15b4626eca975
--- /dev/null
+++ b/NifImport/KfDialog.cpp
@@ -0,0 +1,89 @@
+/**********************************************************************
+*<
+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"
+#include "KFMImporter.h"
+#include "resource.h"
+#include "shellapi.h"
+
+using namespace Niflib;
+
+static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
+   static KFMImporter *imp = NULL;
+   static DWORD dlgRes = IDCANCEL; 
+
+   switch(message) {
+      case WM_INITDIALOG:
+         {
+            dlgRes = IDCANCEL;
+
+            // Append file version to dialog
+            TSTR version = GetFileVersion(NULL);
+            if (!version.isNull()) {
+               char buffer[256];
+               GetWindowText(hWnd, buffer, _countof(buffer));
+               _tcscat(buffer, TEXT(" "));
+               _tcscat(buffer, version);
+               SetWindowText(hWnd, buffer);
+            }
+
+            imp = (KFMImporter *)lParam;
+            CenterWindow(hWnd,GetParent(hWnd));
+
+            CheckDlgButton(hWnd, IDC_CHK_CLEARANIM, imp->clearAnimation);
+            CheckDlgButton(hWnd, IDC_CHK_KEYNOTES, imp->addNoteTracks);
+            CheckDlgButton(hWnd, IDC_CHK_TIMETAGS, imp->addTimeTags);
+         }
+         return TRUE;
+
+      case WM_CLOSE:
+         {
+            EndDialog(hWnd, dlgRes);
+         }
+         return TRUE;
+
+      case WM_COMMAND : 
+         {
+            if (HIWORD(wParam) == BN_CLICKED)
+            {
+               switch (LOWORD(wParam))
+               {
+               case IDOK:
+                  imp->clearAnimation = IsDlgButtonChecked(hWnd, IDC_CHK_CLEARANIM) ? true : false;
+                  imp->addNoteTracks = IsDlgButtonChecked(hWnd, IDC_CHK_KEYNOTES) ? true : false;
+                  imp->addTimeTags = IsDlgButtonChecked(hWnd, IDC_CHK_TIMETAGS) ? true : false;
+                  EndDialog(hWnd, dlgRes=IDOK);
+                  return TRUE;
+
+               case IDCANCEL:
+                  EndDialog(hWnd, dlgRes=IDCANCEL);
+                  return TRUE;
+               }
+            }
+            else if (HIWORD(wParam) == STN_CLICKED)
+            {
+               if (LOWORD(wParam) == IDC_LBL_LINK)
+               {
+                  ShellExecute(hWnd, "open", "http://www.niftools.org", NULL, NULL, SW_SHOWDEFAULT);
+               }
+            }
+         }
+         break;
+   }
+   return FALSE;
+}
+
+bool KFMImporter::ShowDialog()
+{
+   return (IDOK == DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_KF_PANEL), GetActiveWindow(), MaxNifImportOptionsDlgProc, (LPARAM)this));
+}
\ No newline at end of file
diff --git a/NifImport/MaxNifImport.cpp b/NifImport/MaxNifImport.cpp
index 9af93ff400107ef8195ed814b3fea1e1beb4d565..dcb540767ea78219ba64fafb500572834903f57c 100644
--- a/NifImport/MaxNifImport.cpp
+++ b/NifImport/MaxNifImport.cpp
@@ -92,11 +92,7 @@ MaxNifImport::~MaxNifImport()
 
 int MaxNifImport::ExtCount()
 {
-#ifdef USE_UNSUPPORTED_CODE
 	return 3;
-#else
-   return 2;
-#endif
 }
 
 const TCHAR *MaxNifImport::Ext(int n)
diff --git a/NifImport/MaxNifImport.h b/NifImport/MaxNifImport.h
index 782ff5be4cefc0d922ce6494e5c947ccbaf1ee38..378671d247205beb6d8df45eab3c0b09909fc2c9 100644
--- a/NifImport/MaxNifImport.h
+++ b/NifImport/MaxNifImport.h
@@ -54,6 +54,7 @@
 #include "obj\NiSkinData.h"
 #include "obj\NiSkinInstance.h"
 #include "obj\NiSkinPartition.h"
+#include "obj\NiLight.h"
 
 #include "niutils.h"
 #include "AppSettings.h"
diff --git a/NifImport/MaxNifImport.rc b/NifImport/MaxNifImport.rc
index 0930a5bce1e0b8f434f8d9e0f935fc0a9fdbed6d..158f7c58ee899eb2a3b38a5d2ab1d138e54a3762 100644
--- a/NifImport/MaxNifImport.rc
+++ b/NifImport/MaxNifImport.rc
@@ -26,19 +26,19 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 // Dialog
 //
 
-IDD_PANEL DIALOGEX 0, 0, 206, 171
+IDD_NIF_PANEL DIALOGEX 0, 0, 218, 193
 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 EXSTYLE WS_EX_TOOLWINDOW
 CAPTION "Import Nif"
 FONT 8, "MS Sans Serif", 0, 0, 0x1
 BEGIN
-    GROUPBOX        "Import:",IDC_STATIC,7,6,81,92
+    GROUPBOX        "Import:",IDC_STATIC,7,6,81,105
     CONTROL         "&Skeleton",IDC_CHK_BONES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,16,67,10
     CONTROL         "S&kin Modifier",IDC_CHK_SKIN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,27,67,10
     CONTROL         "Vertex &Colors",IDC_CHK_VCOLORS,"Button",BS_AUTO3STATE | WS_TABSTOP,14,38,67,10
-    CONTROL         "Co&llision",IDC_CHK_COLL,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,62,67,10
+    CONTROL         "Co&llision",IDC_CHK_COLL,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,86,67,10
     CONTROL         "&Animation",IDC_CHK_ANIMATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,50,67,10
-    CONTROL         "Furniture &Markers",IDC_CHK_FURN,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,73,67,10
+    CONTROL         "Furniture &Markers",IDC_CHK_FURN,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,14,97,67,10
     GROUPBOX        "Behaviors:",IDC_STATIC,94,6,101,92
     CONTROL         "Flip U&V",IDC_CHK_FLIP_UV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,16,88,10
     CONTROL         "&Render Textures in View",IDC_CHK_SHOW_TEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,27,88,10
@@ -46,15 +46,32 @@ BEGIN
     CONTROL         "Remove &Illegal Faces",IDC_CHK_ILLEGAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,50,88,11
     CONTROL         "Remove &Unused Bones",IDC_CHK_REM_BONES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,62,88,10
     CONTROL         "Use &Biped",IDC_CHK_BIPED,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,102,84,87,10
-    LTEXT           "Skeleton:",IDC_STC_SKELETON,7,121,31,8
-    EDITTEXT        IDC_ED_SKELETON,7,133,171,12,ES_AUTOHSCROLL
-    PUSHBUTTON      "...",IDC_BTN_BROWSE,180,133,19,13
-    LTEXT           "Game:",IDC_STATIC,7,107,31,8
-    COMBOBOX        IDC_CB_GAME,47,105,131,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
-    DEFPUSHBUTTON   "&Import",IDOK,5,150,34,14
-    PUSHBUTTON      "&Cancel",IDCANCEL,45,150,33,14
-    LTEXT           "http://niftools.sourceforge.net",IDC_LBL_LINK,93,150,95,14,SS_NOTIFY | SS_CENTERIMAGE
+    LTEXT           "Skeleton:",IDC_STC_SKELETON,7,143,31,8
+    EDITTEXT        IDC_ED_SKELETON,7,155,171,12,ES_AUTOHSCROLL
+    PUSHBUTTON      "...",IDC_BTN_BROWSE,192,155,19,13
+    LTEXT           "Game:",IDC_STATIC,7,129,31,8
+    COMBOBOX        IDC_CB_GAME,47,127,131,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    DEFPUSHBUTTON   "&Import",IDOK,5,172,34,14
+    PUSHBUTTON      "&Cancel",IDCANCEL,45,172,33,14
+    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,93,172,95,14,SS_NOTIFY | SS_CENTERIMAGE
     CONTROL         "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,73,88,10
+    CONTROL         "&Lights",IDC_CHK_LIGHTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,62,67,10
+    CONTROL         "Cameras",IDC_CHK_CAMERA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,74,67,10
+END
+
+IDD_KF_PANEL DIALOGEX 0, 0, 118, 99
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Import KF"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    GROUPBOX        "Import:",-1,7,6,104,51
+    DEFPUSHBUTTON   "&Import",IDOK,5,78,34,14
+    PUSHBUTTON      "&Cancel",IDCANCEL,45,78,33,14
+    LTEXT           "http://www.niftools.org",IDC_LBL_LINK,7,61,95,14,SS_NOTIFY | SS_CENTERIMAGE
+    CONTROL         "Clear Animation",IDC_CHK_CLEARANIM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,18,72,10
+    CONTROL         "Add Key Notes",IDC_CHK_KEYNOTES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,31,72,10
+    CONTROL         "Add Time Tags",IDC_CHK_TIMETAGS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,44,72,10
 END
 
 
@@ -66,12 +83,20 @@ END
 #ifdef APSTUDIO_INVOKED
 GUIDELINES DESIGNINFO 
 BEGIN
-    IDD_PANEL, DIALOG
+    IDD_NIF_PANEL, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 211
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 186
+    END
+
+    IDD_KF_PANEL, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 199
+        RIGHTMARGIN, 111
         TOPMARGIN, 7
-        BOTTOMMARGIN, 164
+        BOTTOMMARGIN, 92
     END
 END
 #endif    // APSTUDIO_INVOKED
diff --git a/NifImport/MaxNifImport_VC80.vcproj b/NifImport/MaxNifImport_VC80.vcproj
index 245bcbb797a644a6ca549347aeed5ab49ebc701a..93ff0e2604c2dfbf7bb02493d56e7076d3e45144 100644
--- a/NifImport/MaxNifImport_VC80.vcproj
+++ b/NifImport/MaxNifImport_VC80.vcproj
@@ -691,10 +691,6 @@
 			Name="Header Files"
 			Filter="h;hpp;hxx;hm;inl"
 			>
-			<File
-				RelativePath=".\KFImporter.h"
-				>
-			</File>
 			<File
 				RelativePath=".\resource.h"
 				>
@@ -728,6 +724,10 @@
 				RelativePath=".\ImportCollision.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\ImportLights.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\ImportMeshAndSkin.cpp"
 				>
@@ -740,10 +740,18 @@
 				RelativePath=".\ImportSkeleton.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\KfDialog.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\KFImporter.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\KFImporter.h"
+				>
+			</File>
 			<File
 				RelativePath=".\KFMImporter.cpp"
 				>
diff --git a/NifImport/NIFImport.cpp b/NifImport/NIFImport.cpp
index 872417ed3bf0d38ce7f518fd87b4d462c979f901..001a36d737206f02b48ad30289abfc42ea93af89 100644
--- a/NifImport/NIFImport.cpp
+++ b/NifImport/NIFImport.cpp
@@ -142,6 +142,8 @@ void NifImporter::LoadIniSettings()
    flipUVTextures = GetIniValue(NifImportSection, "FlipUVTextures", true);
    enableSkinSupport = GetIniValue(NifImportSection, "EnableSkinSupport", true);
    enableCollision = GetIniValue(NifImportSection, "EnableCollision", true);
+   enableLights = GetIniValue(NifImportSection, "Lights", false);
+   enableCameras = GetIniValue(NifImportSection, "Cameras", false);
    vertexColorMode = GetIniValue<int>(NifImportSection, "VertexColorMode", 1);
    useCiv4Shader = GetIniValue(NifImportSection, "UseCiv4Shader", true);
    mergeNonAccum = GetIniValue(NifImportSection, "MergeNonAccum", true);
@@ -170,6 +172,8 @@ void NifImporter::LoadIniSettings()
    requireMultipleKeys = GetIniValue(AnimImportSection, "RequireMultipleKeys", true);
    applyOverallTransformToSkinAndBones = GetIniValue(AnimImportSection, "ApplyOverallTransformToSkinAndBones", true);
    clearAnimation = GetIniValue(AnimImportSection, "ClearAnimation", true);
+   addNoteTracks = GetIniValue(AnimImportSection, "AddNoteTracks", true);
+   addTimeTags = GetIniValue(AnimImportSection, "AddTimeTags", true);
 
    // Collision
    bhkScaleFactor = GetIniValue<float>(CollisionSection, "bhkScaleFactor", 7.0f);
@@ -196,6 +200,9 @@ void NifImporter::SaveIniSettings()
    SetIniValue(NifImportSection, "EnableSkinSupport", enableSkinSupport);
    SetIniValue(NifImportSection, "VertexColorMode", vertexColorMode);
    SetIniValue(NifImportSection, "EnableCollision", enableCollision);
+   SetIniValue(NifImportSection, "Lights", enableLights);
+   SetIniValue(NifImportSection, "Cameras", enableCameras);
+   
    //SetIniValue(NifImportSection, "EnableFurniture", enableAnimations);
 
    SetIniValue(NifImportSection, "FlipUVTextures", flipUVTextures);
@@ -208,7 +215,7 @@ void NifImporter::SaveIniSettings()
    SetIniValue(BipedImportSection, "RemoveUnusedImportedBones", removeUnusedImportedBones);  
 
    SetIniValue(AnimImportSection, "EnableAnimations", enableAnimations);
-   SetIniValue(BipedImportSection, "ClearAnimation", clearAnimation);
+   SetIniValue(AnimImportSection, "ClearAnimation", clearAnimation);
 }
 
 INode *NifImporter::GetNode(Niflib::NiNodeRef node)
@@ -233,36 +240,6 @@ bool NifImporter::DoImport()
    vector<string> importedBones;
    if (!isBiped && importSkeleton && importBones)
    {
-      //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())
@@ -296,6 +273,11 @@ bool NifImporter::DoImport()
                ImportBones(rootNode);
          }
 
+
+         if (enableLights){
+            ok = ImportLights(rootNode);
+         }
+
          ok = ImportMeshes(rootNode);
 
          if (importSkeleton && removeUnusedImportedBones){
@@ -324,12 +306,7 @@ bool NifImporter::DoImport()
       }
    }
 
-   if (clearAnimation)
-   {
-      ClearAnimation(gi->GetRootNode());
-   }
-
-   // Kick of animation import
+   ClearAnimation();
    ImportAnimation();
    return true;
 }
\ No newline at end of file
diff --git a/NifImport/NIFImporter.h b/NifImport/NIFImporter.h
index 5f864741a3dce7cdbe49c5440f6d076124463cad..087328b405962b56044304fa2905b90f7e6ffe0d 100644
--- a/NifImport/NIFImporter.h
+++ b/NifImport/NIFImporter.h
@@ -34,6 +34,8 @@ public:
    int vertexColorMode;
    bool useCiv4Shader;
    bool mergeNonAccum;
+   bool enableLights;
+   bool enableCameras;
 
    // Biped/Bones related settings
    bool importBones;
@@ -65,6 +67,8 @@ public:
    bool requireMultipleKeys;
    bool applyOverallTransformToSkinAndBones;
    bool clearAnimation;
+   bool addNoteTracks;
+   bool addTimeTags;
 
    // Collision settings
    float bhkScaleFactor;
@@ -79,8 +83,8 @@ public:
    void BuildNodes();
 
    // Ini File related routines
-   void LoadIniSettings();
-   void SaveIniSettings();
+   virtual void LoadIniSettings();
+   virtual void SaveIniSettings();
 
    void ApplyAppSettings();
 
@@ -110,6 +114,9 @@ public:
    INode *CreateHelper(const string& name, Point3 startPos);
    INode *CreateCamera(const string& name);
 
+   bool ImportLights(Niflib::NiNodeRef node);
+   bool ImportLights(vector<Niflib::NiLightRef> lights);
+
    // Primary Collision entry point.  Tests for bhk objects
    bool ImportCollision(Niflib::NiNodeRef node);
 
@@ -121,6 +128,7 @@ public:
 
    // Animation Helpers
    bool ImportAnimation();
+   void ClearAnimation();
    void ClearAnimation(INode *node);
 
 
diff --git a/NifImport/NifDialog.cpp b/NifImport/NifDialog.cpp
index 2a9629edb88f6d0dc6be471e00662f70227af8d4..2bf1152e9a8236f6b3e98b3fbbbe754e181f666c 100644
--- a/NifImport/NifDialog.cpp
+++ b/NifImport/NifDialog.cpp
@@ -17,7 +17,7 @@ HISTORY:
 
 using namespace Niflib;
 
-BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
+static BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) {
    static NifImporter *imp = NULL;
    static DWORD dlgRes = IDCANCEL; 
 
@@ -44,6 +44,9 @@ BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LP
             CheckDlgButton(hWnd, IDC_CHK_VCOLORS, imp->vertexColorMode);
             CheckDlgButton(hWnd, IDC_CHK_COLL, imp->enableCollision);
             CheckDlgButton(hWnd, IDC_CHK_ANIMATION, imp->enableAnimations);
+            CheckDlgButton(hWnd, IDC_CHK_LIGHTS, imp->enableLights);
+            CheckDlgButton(hWnd, IDC_CHK_CAMERA, imp->enableCameras);
+            
             //CheckDlgButton(hWnd, IDC_CHK_FURN, imp->);
 
             CheckDlgButton(hWnd, IDC_CHK_FLIP_UV, imp->flipUVTextures);
@@ -92,6 +95,8 @@ BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LP
                   imp->enableSkinSupport = IsDlgButtonChecked(hWnd, IDC_CHK_SKIN) ? true : false;
                   imp->vertexColorMode = (int)IsDlgButtonChecked(hWnd, IDC_CHK_VCOLORS);
                   imp->enableCollision = IsDlgButtonChecked(hWnd, IDC_CHK_COLL) ? true : false;
+                  imp->enableCameras = IsDlgButtonChecked(hWnd, IDC_CHK_CAMERA) ? true : false;
+                  imp->enableLights = IsDlgButtonChecked(hWnd, IDC_CHK_LIGHTS) ? true : false;
                   imp->enableAnimations = IsDlgButtonChecked(hWnd, IDC_CHK_ANIMATION) ? true : false;
                   //IsDlgButtonChecked(hWnd, IDC_CHK_FURN, imp->);
 
@@ -155,8 +160,7 @@ BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LP
             {
                if (LOWORD(wParam) == IDC_LBL_LINK)
                {
-                  ShellExecute(hWnd, "open", "http://niftools.sourceforge.net",
-                     NULL, NULL, SW_SHOWDEFAULT);
+                  ShellExecute(hWnd, "open", "http://www.niftools.org", NULL, NULL, SW_SHOWDEFAULT);
                }
             }
             else if (HIWORD(wParam) == CBN_SELCHANGE)
@@ -186,5 +190,5 @@ BOOL CALLBACK MaxNifImportOptionsDlgProc(HWND hWnd,UINT message,WPARAM wParam,LP
 
 bool NifImporter::ShowDialog()
 {
-   return (IDOK == DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PANEL), GetActiveWindow(), MaxNifImportOptionsDlgProc, (LPARAM)this));
+   return (IDOK == DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_NIF_PANEL), GetActiveWindow(), MaxNifImportOptionsDlgProc, (LPARAM)this));
 }
\ No newline at end of file
diff --git a/NifImport/resource.h b/NifImport/resource.h
index 0d6f568252e29f75e72b832b2c55bdc261c1176f..a608a761e71fb321548495c9ddbdc68b35d7be4e 100644
--- a/NifImport/resource.h
+++ b/NifImport/resource.h
@@ -8,6 +8,8 @@
 #define IDS_PARAMS                      4
 #define IDS_SPIN                        5
 #define IDD_PANEL                       101
+#define IDD_NIF_PANEL                   101
+#define IDD_KF_PANEL                    102
 #define IDC_CLOSEBUTTON                 1000
 #define IDC_DOSTUFF                     1000
 #define IDC_EDITHEIGHT                  1001
@@ -18,6 +20,7 @@
 #define IDC_CHK_FURN                    1005
 #define IDC_ED_SKELETON                 1006
 #define IDC_CHK_BIPED                   1007
+#define IDC_CHK_LIGHTS                  1008
 #define IDC_CHK_COLL                    1010
 #define IDC_LBL_LINK                    1011
 #define IDC_CHK_VCOLORS                 1012
@@ -31,6 +34,9 @@
 #define IDC_STC_SKELETON                1021
 #define IDC_CHK_REM_BONES               1022
 #define IDC_CHK_CLEARANIM               1023
+#define IDC_CHK_KEYNOTES                1024
+#define IDC_CHK_CAMERA                  1025
+#define IDC_CHK_TIMETAGS                1026
 
 // Next default values for new objects
 // 
diff --git a/NifImport/stdafx.h b/NifImport/stdafx.h
index 5b53f29d025cbac02581dc56aa11e6a98e06cb93..ccd7e8bd0453e3be8baab8b47ec2bad5bed6f4b6 100644
--- a/NifImport/stdafx.h
+++ b/NifImport/stdafx.h
@@ -22,3 +22,4 @@
 #include <shlwapi.h>
 
 #include "MaxNifImport.h"
+#include "MAX_MemDirect.h"
\ No newline at end of file