diff --git a/add_mesh_spindle.py b/add_mesh_spindle.py
new file mode 100644
index 0000000000000000000000000000000000000000..2fef9c618238d8051cf169895b5e1f103ba90970
--- /dev/null
+++ b/add_mesh_spindle.py
@@ -0,0 +1,168 @@
+# add_mesh_spindle.py Copyright (C) 2008-2009, FourMadMen.com
+#
+# add spindle to the blender 2.50 add->mesh menu
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+
+bl_addon_info = {
+    'name': 'Add Mesh: Spindle',
+    'author': 'fourmadmen',
+    'version': '2.0',
+    'blender': (2, 5, 3),
+    'location': 'View3D > Add > Mesh ',
+    'description': 'Adds a mesh Spindle to the Add Mesh menu',
+    'url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/Add_Spindle',
+    'category': 'Add Mesh'}
+
+"""
+Name: 'Spindle'
+Blender: 250
+Group: 'AddMesh'
+Tip: 'Add Spindle Object...'
+__author__ = ["Four Mad Men", "FourMadMen.com"]
+__version__ = '0.10'
+__url__ = [""]
+email__=["bwiki {at} fourmadmen {dot} com"]
+
+Usage:
+
+* Launch from Add Mesh menu
+
+* Modify parameters as desired or keep defaults
+
+"""
+
+
+import bpy
+import mathutils
+from math import pi
+
+
+def add_spindle( spindle_segments, spindle_radius, spindle_height, spindle_cap_height):
+
+    Vector = mathutils.Vector
+    RotationMatrix = mathutils.RotationMatrix
+
+    verts = []
+    faces = []
+
+    tot_verts = spindle_segments * 2 + 2
+
+    half_height = spindle_height * .5
+
+    verts.extend( Vector(0, 0, half_height + spindle_cap_height) )
+    verts.extend( Vector(0, 0, -half_height - spindle_cap_height) )
+
+    i = 2
+    for index in range(spindle_segments):
+        mtx = RotationMatrix( 2 * pi * float(index)/spindle_segments, 3, 'Z' )
+
+        verts.extend( Vector(spindle_radius, 0, half_height) * mtx )
+        it1 = i
+        i+=1
+
+        verts.extend( Vector(spindle_radius, 0, -half_height) * mtx )
+        ib1 = i
+        i+=1
+
+        if i>4:
+            faces.extend( (it2, it1, 0, it2) )
+            faces.extend( (it1, it2, ib2, ib1) )
+            faces.extend( (ib1, ib2, 1, ib1) )
+
+        it2 = it1
+        ib2 = ib1
+
+    faces.extend( (tot_verts-2, 2, 0, tot_verts-2) )
+    faces.extend( (3, 2, tot_verts-2, tot_verts-1) )
+    faces.extend( (3, tot_verts-1, 1, 3) )
+
+    return verts, faces
+
+from bpy.props import FloatProperty
+from bpy.props import IntProperty
+
+class AddSpindle(bpy.types.Operator):
+    '''Add a spindle mesh.'''
+    bl_idname = "mesh.spindle_add"
+    bl_label = "Add Spindle"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    spindle_segments = IntProperty(name="Segments",
+        description="Number of segments of the spindle",
+        default=32, min=3, max=256)
+    spindle_radius = FloatProperty(name="Radius",
+        description="Radius of the spindle",
+        default=1.00, min=0.01, max=100.00)
+    spindle_height = FloatProperty(name="Height",
+        description="Height of the spindle",
+        default=1.00, min=0.01, max=100.00)
+    spindle_cap_height = FloatProperty(name="Cap Height",
+        description="Cap height of the spindle",
+        default=0.50, min=0.01, max=100.00)
+
+    def execute(self, context):
+
+        verts_loc, faces = add_spindle(self.properties.spindle_segments, self.properties.spindle_radius, self.properties.spindle_height, self.properties.spindle_cap_height)
+
+        mesh = bpy.data.meshes.new("Spindle")
+
+        mesh.add_geometry(int(len(verts_loc) / 3), 0, int(len(faces) / 4))
+        mesh.verts.foreach_set("co", verts_loc)
+        mesh.faces.foreach_set("verts_raw", faces)
+        mesh.faces.foreach_set("smooth", [False] * len(mesh.faces))
+
+        scene = context.scene
+
+        # ugh
+        for ob in scene.objects:
+            ob.selected = False
+
+        mesh.update()
+        ob_new = bpy.data.objects.new("Spindle", mesh)
+        ob_new.data = mesh
+        scene.objects.link(ob_new)
+        scene.objects.active = ob_new
+        ob_new.selected = True
+
+        ob_new.location = tuple(context.scene.cursor_location)
+
+        return {'FINISHED'}
+
+
+# Register the operator
+# Add to a menu, reuse an icon used elsewhere that happens to have fitting name
+# unfortunately, the icon shown is the one I expected from looking at the
+# blenderbuttons file from the release/datafiles directory
+
+menu_func = (lambda self, context: self.layout.operator(AddSpindle.bl_idname, text="Add Spindle", icon='PLUGIN'))
+
+def register():
+    bpy.types.register(AddSpindle)
+    bpy.types.INFO_MT_mesh_add.append(menu_func)
+
+def unregister():
+    bpy.types.unregister(AddSpindle)
+    bpy.types.INFO_MT_mesh_add.remove(menu_func)
+
+# Remove "Spindle" menu from the "Add Mesh" menu.
+#space_info.INFO_MT_mesh_add.remove(menu_func)
+
+if __name__ == "__main__":
+    register()
diff --git a/add_mesh_wedge.py b/add_mesh_wedge.py
new file mode 100644
index 0000000000000000000000000000000000000000..eec7005100a9a8afb59820131daa248518b04e9f
--- /dev/null
+++ b/add_mesh_wedge.py
@@ -0,0 +1,151 @@
+# add_mesh_wedge.py Copyright (C) 2008-2009, FourMadMen.com
+#
+# add wedge to the blender 2.50 add->mesh menu
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+
+bl_addon_info = {
+    'name': 'Add Mesh: Wedge',
+    'author': 'fourmadmen',
+    'version': '1.1',
+    'blender': (2, 5, 3),
+    'location': 'View3D > Add > Mesh ',
+    'description': 'Adds a mesh Wedge to the Add Mesh menu',
+    'url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/Add_Wedge',
+    'category': 'Add Mesh'}
+
+
+"""
+Name: 'Wedge'
+Blender: 250
+Group: 'AddMesh'
+Tip: 'Add Wedge Object...'
+__author__ = ["Four Mad Men", "FourMadMen.com"]
+__version__ = '0.10'
+__url__ = [""]
+email__=["bwiki {at} fourmadmen {dot} com"]
+
+Usage:
+
+* Launch from Add Mesh menu
+
+* Modify parameters as desired or keep defaults
+
+"""
+
+
+import bpy
+import mathutils
+
+
+def add_wedge( wedge_width, wedge_height, wedge_depth):
+
+    Vector = mathutils.Vector
+
+    verts = []
+    faces = []
+
+    half_width = wedge_width * .5
+    half_height = wedge_height * .5
+    half_depth = wedge_depth * .5
+  
+    verts.extend( Vector(-half_width, -half_height, half_depth) )
+    verts.extend( Vector(-half_width, -half_height, -half_depth) )
+
+    verts.extend( Vector(half_width, -half_height, half_depth) )
+    verts.extend( Vector(half_width, -half_height, -half_depth) )
+
+    verts.extend( Vector(-half_width, half_height, half_depth) )
+    verts.extend( Vector(-half_width, half_height, -half_depth) )
+
+    faces.extend( [0, 2, 4, 0] )
+    faces.extend( [1, 3, 5, 1] )
+    faces.extend( [0, 1, 3, 2] )
+    faces.extend( [0, 4, 5, 1] )
+    faces.extend( [2, 3, 5, 4] )
+
+    return verts, faces
+
+from bpy.props import FloatProperty
+
+class AddWedge(bpy.types.Operator):
+    '''Add a wedge mesh.'''
+    bl_idname = "mesh.wedge_add"
+    bl_label = "Add Wedge"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    wedge_width = FloatProperty(name="Width",
+        description="Width of Wedge",
+        default=2.00, min=0.01, max=100.00)
+    wedge_height = FloatProperty(name="Height",
+        description="Height of Wedge",
+        default=2.00, min=0.01, max=100.00)
+    wedge_depth = FloatProperty(name="Depth",
+        description="Depth of Wedge",
+        default=2.00, min=0.01, max=100.00)
+
+    def execute(self, context):
+
+        verts_loc, faces = add_wedge(self.properties.wedge_width, self.properties.wedge_height, self.properties.wedge_depth)
+
+        mesh = bpy.data.meshes.new("Wedge")
+
+        mesh.add_geometry(int(len(verts_loc) / 3), 0, int(len(faces) / 4))
+        mesh.verts.foreach_set("co", verts_loc)
+        mesh.faces.foreach_set("verts_raw", faces)
+        mesh.faces.foreach_set("smooth", [False] * len(mesh.faces))
+
+        scene = context.scene
+
+        # ugh
+        for ob in scene.objects:
+            ob.selected = False
+
+        mesh.update()
+        ob_new = bpy.data.objects.new("Wedge", mesh)
+        ob_new.data = mesh
+        scene.objects.link(ob_new)
+        scene.objects.active = ob_new
+        ob_new.selected = True
+
+        ob_new.location = tuple(context.scene.cursor_location)
+
+        return {'FINISHED'}
+
+
+# Register the operator
+# Add to a menu, reuse an icon used elsewhere that happens to have fitting name
+# unfortunately, the icon shown is the one I expected from looking at the
+# blenderbuttons file from the release/datafiles directory
+
+menu_func = (lambda self, context: self.layout.operator(AddWedge.bl_idname, text="Add Wedge", icon='PLUGIN'))
+
+def register():
+    bpy.types.register(AddWedge)
+    bpy.types.INFO_MT_mesh_add.append(menu_func)
+
+def unregister():
+    bpy.types.unregister(AddWedge)
+    bpy.types.INFO_MT_mesh_add.remove(menu_func)
+
+# Remove "Wedge" menu from the "Add Mesh" menu.
+#space_info.INFO_MT_mesh_add.remove(menu_func)
+
+if __name__ == "__main__":
+    register()
diff --git a/export_unreal_psk_psa.py b/export_unreal_psk_psa.py
new file mode 100644
index 0000000000000000000000000000000000000000..348800806c356da577830385d3009476ff45061a
--- /dev/null
+++ b/export_unreal_psk_psa.py
@@ -0,0 +1,1588 @@
+ #  ***** GPL LICENSE BLOCK *****
+ #
+ #  This program is free software: you can redistribute it and/or modify
+ #  it under the terms of the GNU General Public License as published by
+ #  the Free Software Foundation, either version 3 of the License, or
+ #  (at your option) any later version.
+ #
+ #  This program is distributed in the hope that it will be useful,
+ #  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ #  GNU General Public License for more details.
+ #
+ #  You should have received a copy of the GNU General Public License
+ #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ #  All rights reserved.
+ #  ***** GPL LICENSE BLOCK *****
+ 
+bl_addon_info = {
+    'name': 'Export: Unreal Skeletal Mesh/Animation (.psk & .psa)',
+    'author': 'Darknet',
+    'version': '2.0',
+    'blender': (2, 5, 3),
+    'location': 'File > Export ',
+    'description': 'Export Unreal Engine (.psk & .psa)',
+    'url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/File_I-O/Unreal_psk_psa',
+    'category': 'Import/Export'}
+
+
+"""
+Name: 'Unreal Skeletal Mesh/Animation (.psk and .psa) Export'
+Blender: 250
+Group: 'Export'
+Tooltip: 'Unreal Skeletal Mesh and Animation Export (*.psk, *.psa)'
+"""
+
+__author__ = "Darknet/Optimus_P-Fat/Active_Trash/Sinsoft"
+__url__ = ['http://sinsoft.com', 'www.sinsoft.com', 'sinsoft.com']
+__version__ = "0.1.1"
+
+__bpydoc__ = """\
+
+-- Unreal Skeletal Mesh and Animation Export (.psk  and .psa) export script v0.0.1 --<br> 
+
+- NOTES:
+- This script Exports To Unreal's PSK and PSA file formats for Skeletal Meshes and Animations. <br>
+- This script DOES NOT support vertex animation! These require completely different file formats. <br>
+
+- v0.0.1
+- Initial version
+
+- v0.0.2
+- This version adds support for more than one material index!
+
+[ - Edit by: Darknet
+- v0.0.3 - v0.0.12
+- This will work on UT3 and it is a stable version that work with vehicle for testing. 
+- Main Bone fix no dummy needed to be there.
+- Just bone issues position, rotation, and offset for psk.
+- The armature bone position, rotation, and the offset of the bone is fix. It was to deal with skeleton mesh export for psk.
+- Animation is fix for position, offset, rotation bone support one rotation direction when armature build. 
+- It will convert your mesh into triangular when exporting to psk file.
+- Did not work with psa export yet.
+
+- v0.0.13
+- The animatoin will support different bone rotations when export the animation.
+
+- v0.0.14
+- Fixed Action set keys frames when there is no pose keys and it will ignore it.
+
+- v0.0.15
+- Fixed multiple objects when exporting to psk. Select one mesh to export to psk.
+- ]
+
+- v0.1.1
+- Blender 2.50 svn (Support)
+
+Credit to:
+- export_cal3d.py (Position of the Bones Format)
+- blender2md5.py (Animation Translation Format)
+- export_obj.py (Blender 2.5/Pyhton 3.x Format)
+
+- freenode #blendercoder -> user -> ideasman42
+
+-Give Credit to those who work on this script.
+"""
+
+import os
+import time
+import datetime
+import bpy
+import mathutils
+import operator
+
+from struct import pack, calcsize
+
+MENUPANELBOOL = True
+
+# REFERENCE MATERIAL JUST IN CASE:
+# 
+# U = x / sqrt(x^2 + y^2 + z^2)
+# V = y / sqrt(x^2 + y^2 + z^2)
+#
+# Triangles specifed counter clockwise for front face
+#
+#defines for sizeofs
+SIZE_FQUAT = 16
+SIZE_FVECTOR = 12
+SIZE_VJOINTPOS = 44
+SIZE_ANIMINFOBINARY = 168
+SIZE_VCHUNKHEADER = 32
+SIZE_VMATERIAL = 88
+SIZE_VBONE = 120
+SIZE_FNAMEDBONEBINARY = 120
+SIZE_VRAWBONEINFLUENCE = 12
+SIZE_VQUATANIMKEY = 32
+SIZE_VVERTEX = 16
+SIZE_VPOINT = 12
+SIZE_VTRIANGLE = 12
+
+########################################################################
+# Generic Object->Integer mapping
+# the object must be usable as a dictionary key
+class ObjMap:
+	def __init__(self):
+		self.dict = {}
+		self.next = 0
+	def get(self, obj):
+		if obj in self.dict:
+			return self.dict[obj]
+		else:
+			id = self.next
+			self.next = self.next + 1
+			self.dict[obj] = id
+			return id
+		
+	def items(self):
+		getval = operator.itemgetter(0)
+		getkey = operator.itemgetter(1)
+		return map(getval, sorted(self.dict.items(), key=getkey))
+
+########################################################################
+# RG - UNREAL DATA STRUCTS - CONVERTED FROM C STRUCTS GIVEN ON UDN SITE 
+# provided here: http://udn.epicgames.com/Two/BinaryFormatSpecifications.html
+# updated UDK (Unreal Engine 3): http://udn.epicgames.com/Three/BinaryFormatSpecifications.html
+class FQuat:
+	def __init__(self): 
+		self.X = 0.0
+		self.Y = 0.0
+		self.Z = 0.0
+		self.W = 1.0
+		
+	def dump(self):
+		data = pack('ffff', self.X, self.Y, self.Z, self.W)
+		return data
+		
+	def __cmp__(self, other):
+		return cmp(self.X, other.X) \
+			or cmp(self.Y, other.Y) \
+			or cmp(self.Z, other.Z) \
+			or cmp(self.W, other.W)
+		
+	def __hash__(self):
+		return hash(self.X) ^ hash(self.Y) ^ hash(self.Z) ^ hash(self.W)
+		
+	def __str__(self):
+		return "[%f,%f,%f,%f](FQuat)" % (self.X, self.Y, self.Z, self.W)
+		
+class FVector(object):
+	def __init__(self, X=0.0, Y=0.0, Z=0.0):
+		self.X = X
+		self.Y = Y
+		self.Z = Z
+		
+	def dump(self):
+		data = pack('fff', self.X, self.Y, self.Z)
+		return data
+		
+	def __cmp__(self, other):
+		return cmp(self.X, other.X) \
+			or cmp(self.Y, other.Y) \
+			or cmp(self.Z, other.Z)
+		
+	def _key(self):
+		return (type(self).__name__, self.X, self.Y, self.Z)
+		
+	def __hash__(self):
+		return hash(self._key())
+		
+	def __eq__(self, other):
+		if not hasattr(other, '_key'):
+			return False
+		return self._key() == other._key() 
+		
+	def dot(self, other):
+		return self.X * other.X + self.Y * other.Y + self.Z * other.Z
+	
+	def cross(self, other):
+		return FVector(self.Y * other.Z - self.Z * other.Y,
+				self.Z * other.X - self.X * other.Z,
+				self.X * other.Y - self.Y * other.X)
+				
+	def sub(self, other):
+		return FVector(self.X - other.X,
+			self.Y - other.Y,
+			self.Z - other.Z)
+
+class VJointPos:
+	def __init__(self):
+		self.Orientation = FQuat()
+		self.Position = FVector()
+		self.Length = 0.0
+		self.XSize = 0.0
+		self.YSize = 0.0
+		self.ZSize = 0.0
+		
+	def dump(self):
+		data = self.Orientation.dump() + self.Position.dump() + pack('4f', self.Length, self.XSize, self.YSize, self.ZSize)
+		return data
+			
+class AnimInfoBinary:
+	def __init__(self):
+		self.Name = "" # length=64
+		self.Group = ""	# length=64
+		self.TotalBones = 0
+		self.RootInclude = 0
+		self.KeyCompressionStyle = 0
+		self.KeyQuotum = 0
+		self.KeyPrediction = 0.0
+		self.TrackTime = 0.0
+		self.AnimRate = 0.0
+		self.StartBone = 0
+		self.FirstRawFrame = 0
+		self.NumRawFrames = 0
+		
+	def dump(self):
+		data = pack('64s64siiiifffiii', self.Name, self.Group, self.TotalBones, self.RootInclude, self.KeyCompressionStyle, self.KeyQuotum, self.KeyPrediction, self.TrackTime, self.AnimRate, self.StartBone, self.FirstRawFrame, self.NumRawFrames)
+		return data
+
+class VChunkHeader:
+	def __init__(self, name, type_size):
+		self.ChunkID = name # length=20
+		self.TypeFlag = 1999801 # special value
+		self.DataSize = type_size
+		self.DataCount = 0
+		
+	def dump(self):
+		data = pack('20siii', self.ChunkID, self.TypeFlag, self.DataSize, self.DataCount)
+		return data
+		
+class VMaterial:
+	def __init__(self):
+		self.MaterialName = "" # length=64
+		self.TextureIndex = 0
+		self.PolyFlags = 0 # DWORD
+		self.AuxMaterial = 0
+		self.AuxFlags = 0 # DWORD
+		self.LodBias = 0
+		self.LodStyle = 0
+		
+	def dump(self):
+		data = pack('64siLiLii', self.MaterialName, self.TextureIndex, self.PolyFlags, self.AuxMaterial, self.AuxFlags, self.LodBias, self.LodStyle)
+		return data
+
+class VBone:
+	def __init__(self):
+		self.Name = "" # length = 64
+		self.Flags = 0 # DWORD
+		self.NumChildren = 0
+		self.ParentIndex = 0
+		self.BonePos = VJointPos()
+		
+	def dump(self):
+		data = pack('64sLii', self.Name, self.Flags, self.NumChildren, self.ParentIndex) + self.BonePos.dump()
+		return data
+
+#same as above - whatever - this is how Epic does it...		
+class FNamedBoneBinary:
+	def __init__(self):
+		self.Name = "" # length = 64
+		self.Flags = 0 # DWORD
+		self.NumChildren = 0
+		self.ParentIndex = 0
+		self.BonePos = VJointPos()
+		
+		self.IsRealBone = 0  # this is set to 1 when the bone is actually a bone in the mesh and not a dummy
+		
+	def dump(self):
+		data = pack('64sLii', self.Name, self.Flags, self.NumChildren, self.ParentIndex) + self.BonePos.dump()
+		return data
+	
+class VRawBoneInfluence:
+	def __init__(self):
+		self.Weight = 0.0
+		self.PointIndex = 0
+		self.BoneIndex = 0
+		
+	def dump(self):
+		data = pack('fii', self.Weight, self.PointIndex, self.BoneIndex)
+		return data
+		
+class VQuatAnimKey:
+	def __init__(self):
+		self.Position = FVector()
+		self.Orientation = FQuat()
+		self.Time = 0.0
+		
+	def dump(self):
+		data = self.Position.dump() + self.Orientation.dump() + pack('f', self.Time)
+		return data
+		
+class VVertex(object):
+	def __init__(self):
+		self.PointIndex = 0 # WORD
+		self.U = 0.0
+		self.V = 0.0
+		self.MatIndex = 0 #BYTE
+		self.Reserved = 0 #BYTE
+		
+	def dump(self):
+		data = pack('HHffBBH', self.PointIndex, 0, self.U, self.V, self.MatIndex, self.Reserved, 0)
+		return data
+		
+	def __cmp__(self, other):
+		return cmp(self.PointIndex, other.PointIndex) \
+			or cmp(self.U, other.U) \
+			or cmp(self.V, other.V) \
+			or cmp(self.MatIndex, other.MatIndex) \
+			or cmp(self.Reserved, other.Reserved)
+	
+	def _key(self):
+		return (type(self).__name__,self.PointIndex, self.U, self.V,self.MatIndex,self.Reserved)
+		
+	def __hash__(self):
+		return hash(self._key())
+		
+	def __eq__(self, other):
+		if not hasattr(other, '_key'):
+			return False
+		return self._key() == other._key()
+		
+class VPoint(object):
+	def __init__(self):
+		self.Point = FVector()
+		
+	def dump(self):
+		return self.Point.dump()
+		
+	def __cmp__(self, other):
+		return cmp(self.Point, other.Point)
+	
+	def _key(self):
+		return (type(self).__name__, self.Point)
+	
+	def __hash__(self):
+		return hash(self._key())
+		
+	def __eq__(self, other):
+		if not hasattr(other, '_key'):
+			return False
+		return self._key() == other._key() 
+		
+class VTriangle:
+	def __init__(self):
+		self.WedgeIndex0 = 0 # WORD
+		self.WedgeIndex1 = 0 # WORD
+		self.WedgeIndex2 = 0 # WORD
+		self.MatIndex = 0 # BYTE
+		self.AuxMatIndex = 0 # BYTE
+		self.SmoothingGroups = 0 # DWORD
+		
+	def dump(self):
+		data = pack('HHHBBL', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2, self.MatIndex, self.AuxMatIndex, self.SmoothingGroups)
+		return data
+
+# END UNREAL DATA STRUCTS
+########################################################################
+
+########################################################################
+#RG - helper class to handle the normal way the UT files are stored 
+#as sections consisting of a header and then a list of data structures
+class FileSection:
+	def __init__(self, name, type_size):
+		self.Header = VChunkHeader(name, type_size)
+		self.Data = [] # list of datatypes
+		
+	def dump(self):
+		data = self.Header.dump()
+		for i in range(len(self.Data)):
+			data = data + self.Data[i].dump()
+		return data
+		
+	def UpdateHeader(self):
+		self.Header.DataCount = len(self.Data)
+		
+class PSKFile:
+	def __init__(self):
+		self.GeneralHeader = VChunkHeader("ACTRHEAD", 0)
+		self.Points = FileSection("PNTS0000", SIZE_VPOINT)		#VPoint
+		self.Wedges = FileSection("VTXW0000", SIZE_VVERTEX)		#VVertex
+		self.Faces = FileSection("FACE0000", SIZE_VTRIANGLE)		#VTriangle
+		self.Materials = FileSection("MATT0000", SIZE_VMATERIAL)	#VMaterial
+		self.Bones = FileSection("REFSKELT", SIZE_VBONE)		#VBone
+		self.Influences = FileSection("RAWWEIGHTS", SIZE_VRAWBONEINFLUENCE)	#VRawBoneInfluence
+		
+		#RG - this mapping is not dumped, but is used internally to store the new point indices 
+		# for vertex groups calculated during the mesh dump, so they can be used again
+		# to dump bone influences during the armature dump
+		#
+		# the key in this dictionary is the VertexGroup/Bone Name, and the value
+		# is a list of tuples containing the new point index and the weight, in that order
+		#
+		# Layout:
+		# { groupname : [ (index, weight), ... ], ... }
+		#
+		# example: 
+		# { 'MyVertexGroup' : [ (0, 1.0), (5, 1.0), (3, 0.5) ] , 'OtherGroup' : [(2, 1.0)] }
+		
+		self.VertexGroups = {} 
+		
+	def AddPoint(self, p):
+		#print ('AddPoint')
+		self.Points.Data.append(p)
+		
+	def AddWedge(self, w):
+		#print ('AddWedge')
+		self.Wedges.Data.append(w)
+	
+	def AddFace(self, f):
+		#print ('AddFace')
+		self.Faces.Data.append(f)
+		
+	def AddMaterial(self, m):
+		#print ('AddMaterial')
+		self.Materials.Data.append(m)
+		
+	def AddBone(self, b):
+		#print ('AddBone [%s]: Position: (x=%f, y=%f, z=%f) Rotation=(%f,%f,%f,%f)'  % (b.Name, b.BonePos.Position.X, b.BonePos.Position.Y, b.BonePos.Position.Z, b.BonePos.Orientation.X,b.BonePos.Orientation.Y,b.BonePos.Orientation.Z,b.BonePos.Orientation.W))
+		self.Bones.Data.append(b)
+		
+	def AddInfluence(self, i):
+		#print ('AddInfluence')
+		self.Influences.Data.append(i)
+		
+	def UpdateHeaders(self):
+		self.Points.UpdateHeader()
+		self.Wedges.UpdateHeader()
+		self.Faces.UpdateHeader()
+		self.Materials.UpdateHeader()
+		self.Bones.UpdateHeader()
+		self.Influences.UpdateHeader()
+		
+	def dump(self):
+		self.UpdateHeaders()
+		data = self.GeneralHeader.dump() + self.Points.dump() + self.Wedges.dump() + self.Faces.dump() + self.Materials.dump() + self.Bones.dump() + self.Influences.dump()
+		return data
+		
+	def GetMatByIndex(self, mat_index):
+		if mat_index >= 0 and len(self.Materials.Data) > mat_index:
+			return self.Materials.Data[mat_index]
+		else:
+			m = VMaterial()
+			m.MaterialName = "Mat%i" % mat_index
+			self.AddMaterial(m)
+			return m
+		
+	def PrintOut(self):
+		print ("--- PSK FILE EXPORTED ---")
+		print ('point count: %i' % len(self.Points.Data))
+		print ('wedge count: %i' % len(self.Wedges.Data))
+		print ('face count: %i' % len(self.Faces.Data))
+		print ('material count: %i' % len(self.Materials.Data))
+		print ('bone count: %i' % len(self.Bones.Data))
+		print ('inlfuence count: %i' % len(self.Influences.Data))
+		print ('-------------------------')
+
+# PSA FILE NOTES FROM UDN:
+#
+#	The raw key array holds all the keys for all the bones in all the specified sequences, 
+#	organized as follows:
+#	For each AnimInfoBinary's sequence there are [Number of bones] times [Number of frames keys] 
+#	in the VQuatAnimKeys, laid out as tracks of [numframes] keys for each bone in the order of 
+#	the bones as defined in the array of FnamedBoneBinary in the PSA. 
+#
+#	Once the data from the PSK (now digested into native skeletal mesh) and PSA (digested into 
+#	a native animation object containing one or more sequences) are associated together at runtime, 
+#	bones are linked up by name. Any bone in a skeleton (from the PSK) that finds no partner in 
+#	the animation sequence (from the PSA) will assume its reference pose stance ( as defined in 
+#	the offsets & rotations that are in the VBones making up the reference skeleton from the PSK)
+
+class PSAFile:
+	def __init__(self):
+		self.GeneralHeader = VChunkHeader("ANIMHEAD", 0)
+		self.Bones = FileSection("BONENAMES", SIZE_FNAMEDBONEBINARY)	#FNamedBoneBinary
+		self.Animations = FileSection("ANIMINFO", SIZE_ANIMINFOBINARY)	#AnimInfoBinary
+		self.RawKeys = FileSection("ANIMKEYS", SIZE_VQUATANIMKEY)	#VQuatAnimKey
+		
+		# this will take the format of key=Bone Name, value = (BoneIndex, Bone Object)
+		# THIS IS NOT DUMPED
+		self.BoneLookup = {} 
+		
+	def dump(self):
+		data = self.Generalheader.dump() + self.Bones.dump() + self.Animations.dump() + self.RawKeys.dump()
+		return data
+	
+	def AddBone(self, b):
+		#LOUD
+		#print "AddBone: " + b.Name
+		self.Bones.Data.append(b)
+		
+	def AddAnimation(self, a):
+		#LOUD
+		#print "AddAnimation: %s, TotalBones: %i, AnimRate: %f, NumRawFrames: %i, TrackTime: %f" % (a.Name, a.TotalBones, a.AnimRate, a.NumRawFrames, a.TrackTime)
+		self.Animations.Data.append(a)
+		
+	def AddRawKey(self, k):
+		#LOUD
+		#print "AddRawKey [%i]: Time: %f, Quat: x=%f, y=%f, z=%f, w=%f, Position: x=%f, y=%f, z=%f" % (len(self.RawKeys.Data), k.Time, k.Orientation.X, k.Orientation.Y, k.Orientation.Z, k.Orientation.W, k.Position.X, k.Position.Y, k.Position.Z)
+		self.RawKeys.Data.append(k)
+		
+	def UpdateHeaders(self):
+		self.Bones.UpdateHeader()
+		self.Animations.UpdateHeader()
+		self.RawKeys.UpdateHeader()
+		
+	def GetBoneByIndex(self, bone_index):
+		if bone_index >= 0 and len(self.Bones.Data) > bone_index:
+			return self.Bones.Data[bone_index]
+	
+	def IsEmpty(self):
+		return (len(self.Bones.Data) == 0 or len(self.Animations.Data) == 0)
+	
+	def StoreBone(self, b):
+		self.BoneLookup[b.Name] = [-1, b]
+					
+	def UseBone(self, bone_name):
+		if bone_name in self.BoneLookup:
+			bone_data = self.BoneLookup[bone_name]
+			
+			if bone_data[0] == -1:
+				bone_data[0] = len(self.Bones.Data)
+				self.AddBone(bone_data[1])
+				#self.Bones.Data.append(bone_data[1])
+			
+			return bone_data[0]
+			
+	def GetBoneByName(self, bone_name):
+		if bone_name in self.BoneLookup:
+			bone_data = self.BoneLookup[bone_name]
+			return bone_data[1]
+		
+	def GetBoneIndex(self, bone_name):
+		if bone_name in self.BoneLookup:
+			bone_data = self.BoneLookup[bone_name]
+			return bone_data[0]
+		
+	def dump(self):
+		self.UpdateHeaders()
+		data = self.GeneralHeader.dump() + self.Bones.dump() + self.Animations.dump() + self.RawKeys.dump()
+		return data
+		
+	def PrintOut(self):
+		print ('--- PSA FILE EXPORTED ---')
+		print ('bone count: %i' % len(self.Bones.Data))
+		print ('animation count: %i' % len(self.Animations.Data))
+		print ('rawkey count: %i' % len(self.RawKeys.Data))
+		print ('-------------------------')
+		
+####################################	
+# helpers to create bone structs
+def make_vbone(name, parent_index, child_count, orientation_quat, position_vect):
+	bone = VBone()
+	bone.Name = name
+	bone.ParentIndex = parent_index
+	bone.NumChildren = child_count
+	bone.BonePos.Orientation = orientation_quat
+	bone.BonePos.Position.X = position_vect.x
+	bone.BonePos.Position.Y = position_vect.y
+	bone.BonePos.Position.Z = position_vect.z
+	
+	#these values seem to be ignored?
+	#bone.BonePos.Length = tail.length
+	#bone.BonePos.XSize = tail.x
+	#bone.BonePos.YSize = tail.y
+	#bone.BonePos.ZSize = tail.z
+
+	return bone
+
+def make_namedbonebinary(name, parent_index, child_count, orientation_quat, position_vect, is_real):
+	bone = FNamedBoneBinary()
+	bone.Name = name
+	bone.ParentIndex = parent_index
+	bone.NumChildren = child_count
+	bone.BonePos.Orientation = orientation_quat
+	bone.BonePos.Position.X = position_vect.x
+	bone.BonePos.Position.Y = position_vect.y
+	bone.BonePos.Position.Z = position_vect.z
+	bone.IsRealBone = is_real
+	return bone	
+	
+##################################################
+#RG - check to make sure face isnt a line
+#The face has to be triangle not a line
+def is_1d_face(blender_face,mesh):
+	#ID Vertex of id point
+	v0 = blender_face.verts[0]
+	v1 = blender_face.verts[1]
+	v2 = blender_face.verts[2]
+	
+	return (mesh.verts[v0].co == mesh.verts[v1].co or \
+	mesh.verts[v1].co == mesh.verts[v2].co or \
+	mesh.verts[v2].co == mesh.verts[v0].co)
+	return False
+
+##################################################
+# http://en.wikibooks.org/wiki/Blender_3D:_Blending_Into_Python/Cookbook#Triangulate_NMesh
+
+#blender 2.50 format using the Operators/command convert the mesh to tri mesh
+def triangulateNMesh(object):
+	bneedtri = False
+	scene = bpy.context.scene
+	bpy.ops.object.mode_set(mode='OBJECT')
+	for i in scene.objects: i.selected = False #deselect all objects
+	object.selected = True
+	scene.objects.active = object #set the mesh object to current
+	bpy.ops.object.mode_set(mode='OBJECT')
+	print("Checking mesh if needs to convert quad to Tri...")
+	for face in object.data.faces:
+		if (len(face.verts) > 3):
+			bneedtri = True
+			break
+	
+	bpy.ops.object.mode_set(mode='OBJECT')
+	if bneedtri == True:
+		print("Converting quad to tri mesh...")
+		me_da = object.data.copy() #copy data
+		me_ob = object.copy() #copy object
+		#note two copy two types else it will use the current data or mesh
+		me_ob.data = me_da
+		bpy.context.scene.objects.link(me_ob)#link the object to the scene #current object location
+		for i in scene.objects: i.selected = False #deselect all objects
+		me_ob.selected = True
+		scene.objects.active = me_ob #set the mesh object to current
+		bpy.ops.object.mode_set(mode='EDIT') #Operators
+		bpy.ops.mesh.select_all(action='SELECT')#select all the face/vertex/edge
+		bpy.ops.mesh.quads_convert_to_tris() #Operators
+		bpy.context.scene.update()
+		bpy.ops.object.mode_set(mode='OBJECT') # set it in object
+		bpy.context.scene.unrealtriangulatebool = True
+		print("Triangulate Mesh Done!")
+	else:
+		print("No need to convert tri mesh.")
+		me_ob = object
+	return me_ob
+
+#Blender Bone Index
+class BBone:
+	def __init__(self):
+		self.bone = ""
+		self.index = 0
+bonedata = []
+BBCount = 0	
+#deal with mesh bones groups vertex point
+def BoneIndex(bone):
+	global BBCount, bonedata
+	#print("//==============")
+	#print(bone.name , "ID:",BBCount)
+	BB = BBone()
+	BB.bone = bone.name
+	BB.index = BBCount
+	bonedata.append(BB)
+	BBCount += 1
+	for current_child_bone in bone.children:
+		BoneIndex(current_child_bone)
+
+def BoneIndexArmature(blender_armature):
+	global BBCount
+	#print("\n Buildng bone before mesh \n")
+	#objectbone = blender_armature.pose #Armature bone
+	#print(blender_armature)
+	objectbone = blender_armature[0].pose 
+	#print(dir(ArmatureData))
+	
+	for bone in objectbone.bones:
+		if(bone.parent == None):
+			BoneIndex(bone)
+			#BBCount += 1
+			break
+	
+# Actual object parsing functions
+def parse_meshes(blender_meshes, psk_file):
+	#this is use to call the bone name and the index array for group index matches
+	global bonedata
+	#print("BONE DATA",len(bonedata))
+	print ("----- parsing meshes -----")
+	print("Number of Object Meshes:",len(blender_meshes))
+	for current_obj in blender_meshes: #number of mesh that should be one mesh here
+	
+		current_obj = triangulateNMesh(current_obj)
+		print("Mesh Name:",current_obj.name)
+		current_mesh = current_obj.data
+		
+		#if len(current_obj.materials) > 0:
+		#	object_mat = current_obj.materials[0]
+		object_material_index = current_obj.active_material_index
+	
+		points = ObjMap()
+		wedges = ObjMap()
+		
+		discarded_face_count = 0
+		print (" -- Dumping Mesh Faces -- LEN:", len(current_mesh.faces))
+		for current_face in current_mesh.faces:
+			#print ' -- Dumping UVs -- '
+			#print current_face.uv_textures
+			
+			if len(current_face.verts) != 3:
+				raise RuntimeError("Non-triangular face (%i)" % len(current_face.v))
+			
+			#No Triangulate Yet
+			#			if len(current_face.verts) != 3:
+			#				raise RuntimeError("Non-triangular face (%i)" % len(current_face.verts))
+			#				#TODO: add two fake faces made of triangles?
+			
+			#RG - apparently blender sometimes has problems when you do quad to triangle 
+			#	conversion, and ends up creating faces that have only TWO points -
+			# 	one of the points is simply in the vertex list for the face twice. 
+			#	This is bad, since we can't get a real face normal for a LINE, we need 
+			#	a plane for this. So, before we add the face to the list of real faces, 
+			#	ensure that the face is actually a plane, and not a line. If it is not 
+			#	planar, just discard it and notify the user in the console after we're
+			#	done dumping the rest of the faces
+			
+			if not is_1d_face(current_face,current_mesh):
+				#print("faces")
+				wedge_list = []
+				vect_list = []
+				
+				#get or create the current material
+				m = psk_file.GetMatByIndex(object_material_index)
+
+				face_index = current_face.index
+				has_UV = False
+				faceUV = None
+				
+				if len(current_mesh.uv_textures) > 0:
+					has_UV = True	
+					faceUV = current_mesh.active_uv_texture.data[face_index]#UVs for current face
+					#size(data) is number of texture faces. Each face has UVs
+					#print("DATA face uv: ",len(faceUV.uv), " >> ",(faceUV.uv[0][0]))
+				
+				for i in range(3):
+					vert_index = current_face.verts[i]
+					vert = current_mesh.verts[vert_index]
+					uv = []
+					#assumes 3 UVs Per face (for now).
+					if (has_UV):
+						if len(faceUV.uv) != 3:
+							print ("WARNING: Current face is missing UV coordinates - writing 0,0...")
+							print ("WARNING: Face has more than 3 UVs - writing 0,0...")
+							uv = [0.0, 0.0]
+						else:
+							#uv.append(faceUV.uv[i][0])
+							#uv.append(faceUV.uv[i][1])
+							uv = [faceUV.uv[i][0],faceUV.uv[i][1]] #OR bottom works better # 24 for cube
+							#uv = list(faceUV.uv[i]) #30 just cube	
+					else:
+						print ("No UVs?")
+						uv = [0.0, 0.0]
+					#print("UV >",uv)
+					#uv = [0.0, 0.0] #over ride uv that is not fixed
+					#print(uv)
+					#flip V coordinate because UEd requires it and DOESN'T flip it on its own like it
+					#does with the mesh Y coordinates.
+					#this is otherwise known as MAGIC-2
+					uv[1] = 1.0 - uv[1]
+					
+					#deal with the min and max value
+					#if value is over the set limit it will null the uv texture
+					if (uv[0] > 1):
+						uv[0] = 1
+					if (uv[0] < 0):
+						uv[0] = 0
+					if (uv[1] > 1):
+						uv[1] = 1
+					if (uv[1] < 0):
+						uv[1] = 0
+
+					
+					# RE - Append untransformed vector (for normal calc below)
+					# TODO: convert to Blender.mathutils
+					vect_list.append(FVector(vert.co.x, vert.co.y, vert.co.z))
+					
+					# Transform position for export
+					#vpos = vert.co * object_material_index
+					vpos = vert.co * current_obj.matrix
+					# Create the point
+					p = VPoint()
+					p.Point.X = vpos.x
+					p.Point.Y = vpos.y
+					p.Point.Z = vpos.z
+					
+					# Create the wedge
+					w = VVertex()
+					w.MatIndex = object_material_index
+					w.PointIndex = points.get(p) # get index from map
+					#Set UV TEXTURE
+					w.U = uv[0]
+					w.V = uv[1]
+					index_wedge = wedges.get(w)
+					wedge_list.append(index_wedge)
+					
+					#print results
+					#print 'result PointIndex=%i, U=%f, V=%f, wedge_index=%i' % (
+					#	w.PointIndex,
+					#	w.U,
+					#	w.V,
+					#	wedge_index)
+				
+				# Determine face vertex order
+				# get normal from blender
+				no = current_face.normal
+				
+				# TODO: convert to Blender.mathutils
+				# convert to FVector
+				norm = FVector(no[0], no[1], no[2])
+				
+				# Calculate the normal of the face in blender order
+				tnorm = vect_list[1].sub(vect_list[0]).cross(vect_list[2].sub(vect_list[1]))
+				
+				# RE - dot the normal from blender order against the blender normal
+				# this gives the product of the two vectors' lengths along the blender normal axis
+				# all that matters is the sign
+				dot = norm.dot(tnorm)
+
+				# print results
+				#print 'face norm: (%f,%f,%f), tnorm=(%f,%f,%f), dot=%f' % (
+				#	norm.X, norm.Y, norm.Z,
+				#	tnorm.X, tnorm.Y, tnorm.Z,
+				#	dot)
+
+				tri = VTriangle()
+				# RE - magic: if the dot product above > 0, order the vertices 2, 1, 0
+				#        if the dot product above < 0, order the vertices 0, 1, 2
+				#        if the dot product is 0, then blender's normal is coplanar with the face
+				#        and we cannot deduce which side of the face is the outside of the mesh
+				if (dot > 0):
+					(tri.WedgeIndex2, tri.WedgeIndex1, tri.WedgeIndex0) = wedge_list
+				elif (dot < 0):
+					(tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list
+				else:
+					dindex0 = current_face.verts[0];
+					dindex1 = current_face.verts[1];
+					dindex2 = current_face.verts[2];
+					raise RuntimeError("normal vector coplanar with face! points:", current_mesh.verts[dindex0].co, current_mesh.verts[dindex1].co, current_mesh.verts[dindex2].co)
+				
+				tri.MatIndex = object_material_index
+				#print(tri)
+				psk_file.AddFace(tri)
+				
+			else:
+				discarded_face_count = discarded_face_count + 1
+				
+		print (" -- Dumping Mesh Points -- LEN:",len(points.dict))
+		for point in points.items():
+			psk_file.AddPoint(point)
+		print (" -- Dumping Mesh Wedge -- LEN:",len(wedges.dict))
+		for wedge in wedges.items():
+			psk_file.AddWedge(wedge)
+			
+		#RG - if we happend upon any non-planar faces above that we've discarded, 
+		#	just let the user know we discarded them here in case they want 
+		#	to investigate
+	
+		if discarded_face_count > 0: 
+			print ("INFO: Discarded %i non-planar faces." % (discarded_face_count))
+		
+		#RG - walk through the vertex groups and find the indexes into the PSK points array 
+		#for them, then store that index and the weight as a tuple in a new list of 
+		#verts for the group that we can look up later by bone name, since Blender matches
+		#verts to bones for influences by having the VertexGroup named the same thing as
+		#the bone
+
+		#vertex group.
+		for bonegroup in bonedata:
+			#print("bone gourp build:",bonegroup.bone)
+			vert_list = []
+			for current_vert in current_mesh.verts:
+				#print("INDEX V:",current_vert.index)
+				vert_index = current_vert.index
+				for vgroup in current_vert.groups:#vertex groupd id
+					vert_weight = vgroup.weight
+					if(bonegroup.index == vgroup.group):
+						p = VPoint()
+						vpos = current_vert.co * current_obj.matrix
+						p.Point.X = vpos.x
+						p.Point.Y = vpos.y 
+						p.Point.Z = vpos.z
+						#print(current_vert.co)
+						point_index = points.get(p) #point index
+						v_item = (point_index, vert_weight)
+						vert_list.append(v_item)
+			#bone name, [point id and wieght]
+			#print("Add Vertex Group:",bonegroup.bone, " No. Points:",len(vert_list))
+			psk_file.VertexGroups[bonegroup.bone] = vert_list
+		
+		#unrealtriangulatebool #this will remove the mesh from the scene
+		if (bpy.context.scene.unrealtriangulatebool == True):
+			print("Remove tmp Mesh [ " ,current_obj.name, " ] from scene >"  ,(bpy.context.scene.unrealtriangulatebool ))
+			bpy.ops.object.mode_set(mode='OBJECT') # set it in object
+			bpy.context.scene.objects.unlink(current_obj)
+			
+def make_fquat(bquat):
+	quat = FQuat()
+	
+	#flip handedness for UT = set x,y,z to negative (rotate in other direction)
+	quat.X = -bquat.x
+	quat.Y = -bquat.y
+	quat.Z = -bquat.z
+
+	quat.W = bquat.w
+	return quat
+	
+def make_fquat_default(bquat):
+	quat = FQuat()
+	
+	quat.X = bquat.x
+	quat.Y = bquat.y
+	quat.Z = bquat.z
+	
+	quat.W = bquat.w
+	return quat
+
+# =================================================================================================
+# TODO: remove this 1am hack
+nbone = 0
+def parse_bone(blender_bone, psk_file, psa_file, parent_id, is_root_bone, parent_matrix, parent_root):
+	global nbone 	# look it's evil!
+	#print '-------------------- Dumping Bone ---------------------- '
+
+	#If bone does not have parent that mean it the root bone
+	if blender_bone.parent == None:
+		parent_root = blender_bone
+	
+	
+	child_count = len(blender_bone.children)
+	#child of parent
+	child_parent = blender_bone.parent
+	
+	if child_parent != None:
+		print ("--Bone Name:",blender_bone.name ," parent:" , blender_bone.parent.name, "ID:", nbone)
+	else:
+		print ("--Bone Name:",blender_bone.name ," parent: None" , "ID:", nbone)
+	
+	
+	
+	
+	if child_parent != None:
+		quat_root = blender_bone.matrix
+		quat = make_fquat(quat_root.to_quat())
+		
+		quat_parent = child_parent.matrix.to_quat().inverse()
+		parent_head = child_parent.head * quat_parent
+		parent_tail = child_parent.tail * quat_parent
+		
+		set_position = (parent_tail - parent_head) + blender_bone.head
+	else:
+		# ROOT BONE
+		#This for root 
+		set_position = blender_bone.head * parent_matrix #ARMATURE OBJECT Locction
+		rot_mat = blender_bone.matrix * parent_matrix.rotation_part() #ARMATURE OBJECT Rotation
+		#print(dir(rot_mat))
+		
+		quat = make_fquat_default(rot_mat.to_quat())
+		
+	print ("[[======= FINAL POSITION:", set_position)
+	final_parent_id = parent_id
+	
+	#RG/RE -
+	#if we are not seperated by a small distance, create a dummy bone for the displacement
+	#this is only needed for root bones, since UT assumes a connected skeleton, and from here
+	#down the chain we just use "tail" as an endpoint
+	#if(head.length > 0.001 and is_root_bone == 1):
+	if(0):	
+		pb = make_vbone("dummy_" + blender_bone.name, parent_id, 1, FQuat(), tail)
+		psk_file.AddBone(pb)
+		pbb = make_namedbonebinary("dummy_" + blender_bone.name, parent_id, 1, FQuat(), tail, 0)
+		psa_file.StoreBone(pbb)
+		final_parent_id = nbone
+		nbone = nbone + 1
+		#tail = tail-head
+		
+	my_id = nbone
+	
+	pb = make_vbone(blender_bone.name, final_parent_id, child_count, quat, set_position)
+	psk_file.AddBone(pb)
+	pbb = make_namedbonebinary(blender_bone.name, final_parent_id, child_count, quat, set_position, 1)
+	psa_file.StoreBone(pbb)
+
+	nbone = nbone + 1
+	
+	#RG - dump influences for this bone - use the data we collected in the mesh dump phase
+	# to map our bones to vertex groups
+	#print("///////////////////////")
+	#print("set influence")
+	if blender_bone.name in psk_file.VertexGroups:
+		vertex_list = psk_file.VertexGroups[blender_bone.name]
+		#print("vertex list:", len(vertex_list), " of >" ,blender_bone.name )
+		for vertex_data in vertex_list:
+			#print("set influence vettex")
+			point_index = vertex_data[0]
+			vertex_weight = vertex_data[1]
+			influence = VRawBoneInfluence()
+			influence.Weight = vertex_weight
+			#influence.BoneIndex = my_id
+			influence.BoneIndex = my_id
+			influence.PointIndex = point_index
+			#print(influence)
+			#print ('Adding Bone Influence for [%s] = Point Index=%i, Weight=%f' % (blender_bone.name, point_index, vertex_weight))
+			#print("adding influence")
+			psk_file.AddInfluence(influence)
+	
+	#blender_bone.matrix_local
+	#recursively dump child bones
+	mainparent = parent_matrix
+	#if len(blender_bone.children) > 0:
+	for current_child_bone in blender_bone.children:
+		parse_bone(current_child_bone, psk_file, psa_file, my_id, 0, mainparent, parent_root)
+
+def parse_armature(blender_armature, psk_file, psa_file):
+	print ("----- parsing armature -----")
+	print ('blender_armature length: %i' % (len(blender_armature)))
+	
+	#magic 0 sized root bone for UT - this is where all armature dummy bones will attach
+	#dont increment nbone here because we initialize it to 1 (hackity hackity hack)
+
+	#count top level bones first. NOT EFFICIENT.
+	child_count = 0
+	for current_obj in blender_armature: 
+		current_armature = current_obj.data
+		bones = [x for x in current_armature.bones if not x.parent == None]
+		child_count += len(bones)
+
+	for current_obj in blender_armature:
+		print ("Current Armature Name: " + current_obj.name)
+		current_armature = current_obj.data
+		#armature_id = make_armature_bone(current_obj, psk_file, psa_file)
+		
+		#we dont want children here - only the top level bones of the armature itself
+		#we will recursively dump the child bones as we dump these bones
+		"""
+		bones = [x for x in current_armature.bones if not x.parent == None]
+		#will ingore this part of the ocde
+		"""
+		for current_bone in current_armature.bones: #list the bone. #note this will list all the bones.
+			if(current_bone.parent == None):
+				parse_bone(current_bone, psk_file, psa_file, 0, 0, current_obj.matrix, None)
+				break
+
+# get blender objects by type		
+def get_blender_objects(objects, intype):
+	return [x for x in objects if x.type == intype]
+			
+#strips current extension (if any) from filename and replaces it with extension passed in
+def make_filename_ext(filename, extension):
+	new_filename = ''
+	extension_index = filename.find('.')
+	
+	if extension_index == -1:
+		new_filename = filename + extension
+	else:
+		new_filename = filename[0:extension_index] + extension
+		
+	return new_filename
+
+# returns the quaternion Grassman product a*b
+# this is the same as the rotation a(b(x)) 
+# (ie. the same as B*A if A and B are matrices representing 
+# the rotations described by quaternions a and b)
+def grassman(a, b):	
+	return mathutils.Quaternion(
+		a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z,
+		a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y,
+		a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x,
+		a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w)
+		
+def parse_animation(blender_scene, blender_armatures, psa_file):
+	#to do list:
+	#need to list the action sets
+	#need to check if there animation
+	#need to check if animation is has one frame then exit it
+	print ('\n----- parsing animation -----')
+	#print(dir(blender_scene.render))
+	render_data = blender_scene.render
+	bHaveAction = True
+	
+	anim_rate = render_data.fps
+	print("==== Blender Settings ====")
+	print ('Scene: %s Start Frame: %i, End Frame: %i' % (blender_scene.name, blender_scene.start_frame, blender_scene.end_frame))
+	print ('Frames Per Sec: %i' % anim_rate)
+	print ("Default FPS: 24" )
+	
+	cur_frame_index = 0
+	
+	#print(dir(bpy.data.actions))
+	#print(dir(bpy.context.scene.set))
+	
+	#list of armature objects
+	for arm in blender_armatures:
+		#check if there animation data from armature or something
+		if not arm.animation_data:
+			print("======================================")
+			print("Check Animation Data: None")
+			print("Armature has no animation, skipping...")
+			print("======================================")
+			break
+			
+		if not arm.animation_data.action:
+			print("======================================")
+			print("Check Action: None")
+			print("Armature has no animation, skipping...")
+			print("======================================")
+			break
+		act = arm.animation_data.action
+		#print(dir(act))
+		action_name = act.name
+		
+		if not len(act.fcurves):
+			print("//===========================================================")
+			print("// None bone pose set keys for this action set... skipping...")
+			print("//===========================================================")
+			bHaveAction = False
+			
+		#this deal with action export control
+		if bHaveAction == True:
+			print("")
+			print("==== Action Set ====")
+			print("Action Name:",action_name)
+			#look for min and max frame that current set keys
+			framemin, framemax = act.get_frame_range()
+			#print("max frame:",framemax)
+			start_frame = framemin
+			end_frame = framemax
+			scene_frames = range(start_frame, end_frame+1)
+			frame_count = len(scene_frames)
+			#===================================================
+			anim = AnimInfoBinary()
+			anim.Name = action_name
+			anim.Group = "" #what is group?
+			anim.NumRawFrames = frame_count
+			anim.AnimRate = anim_rate
+			anim.FirstRawFrame = cur_frame_index
+			#===================================================
+			count_previous_keys = len(psa_file.RawKeys.Data)
+			print("Frame Key Set Count:",frame_count, "Total Frame:",frame_count)
+			#print("init action bones...")
+			unique_bone_indexes = {}
+			# bone lookup table
+			bones_lookup =  {}
+		
+			#build bone node for animation keys needed to be set
+			for bone in arm.data.bones:
+				bones_lookup[bone.name] = bone
+			#print("bone name:",bone.name)
+			frame_count = len(scene_frames)
+			#print ('Frame Count: %i' % frame_count)
+			pose_data = arm.pose
+		
+			#these must be ordered in the order the bones will show up in the PSA file!
+			ordered_bones = {}
+			ordered_bones = sorted([(psa_file.UseBone(x.name), x) for x in pose_data.bones], key=operator.itemgetter(0))
+			
+			#############################
+			# ORDERED FRAME, BONE
+			#for frame in scene_frames:
+			
+			for i in range(frame_count):
+				frame = scene_frames[i]
+				#LOUD
+				#print ("==== outputting frame %i ===" % frame)
+				
+				if frame_count > i+1:
+					next_frame = scene_frames[i+1]
+					#print "This Frame: %i, Next Frame: %i" % (frame, next_frame)
+				else:
+					next_frame = -1
+					#print "This Frame: %i, Next Frame: NONE" % frame
+				
+				#frame start from 1 as number one from blender
+				blender_scene.set_frame(frame)
+				
+				cur_frame_index = cur_frame_index + 1
+				for bone_data in ordered_bones:
+					bone_index = bone_data[0]
+					pose_bone = bone_data[1]
+					#print("[=====POSE NAME:",pose_bone.name)
+					
+					#print("LENG >>.",len(bones_lookup))
+					blender_bone = bones_lookup[pose_bone.name]
+					
+					#just need the total unique bones used, later for this AnimInfoBinary
+					unique_bone_indexes[bone_index] = bone_index
+					#LOUD
+					#print ("-------------------", pose_bone.name)
+					head = pose_bone.head
+					
+					posebonemat = mathutils.Matrix(pose_bone.matrix)
+					parent_pose = pose_bone.parent
+					if parent_pose != None:
+						parentposemat = mathutils.Matrix(parent_pose.matrix)
+						#blender 2.4X it been flip around with new 2.50 (mat1 * mat2) should now be (mat2 * mat1)
+						posebonemat = parentposemat.invert() * posebonemat
+					head = posebonemat.translation_part()
+					quat = posebonemat.to_quat().normalize()
+					vkey = VQuatAnimKey()
+					vkey.Position.X = head.x
+					vkey.Position.Y = head.y
+					vkey.Position.Z = head.z
+					
+					if parent_pose != None:
+						quat = make_fquat(quat)
+					else:
+						quat = make_fquat_default(quat)
+					
+					vkey.Orientation = quat
+					#print("Head:",head)
+					#print("Orientation",quat)
+					
+					#time from now till next frame = diff / framesPerSec
+					if next_frame >= 0:
+						diff = next_frame - frame
+					else:
+						diff = 1.0
+					
+					#print ("Diff = ", diff)
+					vkey.Time = float(diff)/float(anim_rate)
+					
+					psa_file.AddRawKey(vkey)
+					
+			#done looping frames
+			#done looping armatures
+			#continue adding animInfoBinary counts here
+		
+			anim.TotalBones = len(unique_bone_indexes)
+			print("Bones Count:",anim.TotalBones)
+			anim.TrackTime = float(frame_count) / anim.AnimRate
+			print("Time Track Frame:",anim.TrackTime)
+			psa_file.AddAnimation(anim)
+			print("==== Finish Action Build(s) ====")
+		
+exportmessage = "Export Finish"		
+		
+def fs_callback(filename, context, user_setting):
+	#this deal with repeat export and the reset settings
+	global bonedata, BBCount, nbone, exportmessage
+	bonedata = []#clear array
+	BBCount = 0
+	nbone = 0
+	
+	start_time = time.clock()
+	
+	print ("========EXPORTING TO UNREAL SKELETAL MESH FORMATS========\r\n")
+	print("Blender Version:", bpy.app.version_string)
+	
+	psk = PSKFile()
+	psa = PSAFile()
+	
+	#sanity check - this should already have the extension, but just in case, we'll give it one if it doesn't
+	psk_filename = make_filename_ext(filename, '.psk')
+	
+	#make the psa filename
+	psa_filename = make_filename_ext(filename, '.psa')
+	
+	print ('PSK File: ' +  psk_filename)
+	print ('PSA File: ' +  psa_filename)
+	
+	barmature = True
+	bmesh = True
+	blender_meshes = []
+	blender_armature = []
+	selectmesh = []
+	selectarmature = []
+	
+	current_scene = context.scene
+	cur_frame = current_scene.current_frame #store current frame before we start walking them during animation parse
+	objects = current_scene.objects
+	
+	print("Checking object count...")
+	for next_obj in objects:
+		if next_obj.type == 'MESH':
+			blender_meshes.append(next_obj)
+			if (next_obj.selected):
+				#print("mesh object select")
+				selectmesh.append(next_obj)
+		if next_obj.type == 'ARMATURE':
+			blender_armature.append(next_obj)
+			if (next_obj.selected):
+				#print("armature object select")
+				selectarmature.append(next_obj)
+	
+	print("Mesh Count:",len(blender_meshes)," Armature Count:",len(blender_armature))
+	print("====================================")
+	print("Checking Mesh Condtion(s):")
+	if len(blender_meshes) == 1:
+		print(" - One Mesh Scene")
+	elif (len(blender_meshes) > 1) and (len(selectmesh) == 1):
+		print(" - One Mesh [Select]")
+	else:
+		print(" - Too Many Meshes!")
+		print(" - Select One Mesh Object!")
+		bmesh = False
+	print("====================================")
+	print("Checking Armature Condtion(s):")
+	if len(blender_armature) == 1:
+		print(" - One Armature Scene")
+	elif (len(blender_armature) > 1) and (len(selectarmature) == 1):
+		print(" - One Armature [Select]")
+	else:
+		print(" - Too Armature Meshes!")
+		print(" - Select One Armature Object Only!")
+		barmature = False
+	
+	if 	(bmesh == False) or (barmature == False):
+		exportmessage = "Export Fail! Check Log."
+		print("=================================")
+		print("= Export Fail!                  =")
+		print("=================================")
+	else:
+		exportmessage = "Export Finish!"
+		#need to build a temp bone index for mesh group vertex
+		BoneIndexArmature(blender_armature)
+
+		try:
+			#######################
+			# STEP 1: MESH DUMP
+			# we build the vertexes, wedges, and faces in here, as well as a vertexgroup lookup table
+			# for the armature parse
+			print("//===============================")
+			print("// STEP 1")
+			print("//===============================")
+			parse_meshes(blender_meshes, psk)
+		except:
+			context.scene.set_frame(cur_frame) #set frame back to original frame
+			print ("Exception during Mesh Parse")
+			raise
+		
+		try:
+			#######################
+			# STEP 2: ARMATURE DUMP
+			# IMPORTANT: do this AFTER parsing meshes - we need to use the vertex group data from 
+			# the mesh parse in here to generate bone influences
+			print("//===============================")
+			print("// STEP 2")
+			print("//===============================")
+			parse_armature(blender_armature, psk, psa) 
+			
+		except:
+			context.scene.set_frame(cur_frame) #set frame back to original frame
+			print ("Exception during Armature Parse")
+			raise
+
+		try:
+			#######################
+			# STEP 3: ANIMATION DUMP
+			# IMPORTANT: do AFTER parsing bones - we need to do bone lookups in here during animation frames
+			print("//===============================")
+			print("// STEP 3")
+			print("//===============================")
+			parse_animation(current_scene, blender_armature, psa) 
+			
+		except:
+			context.scene.set_frame(cur_frame) #set frame back to original frame
+			print ("Exception during Animation Parse")
+			raise
+
+		# reset current frame
+		
+		context.scene.set_frame(cur_frame) #set frame back to original frame
+		
+		##########################
+		# FILE WRITE
+		print("//===========================================")
+		print("// bExportPsk:",bpy.context.scene.unrealexportpsk," bExportPsa:",bpy.context.scene.unrealexportpsa)
+		print("//===========================================")
+		if bpy.context.scene.unrealexportpsk == True:
+			print("Writing Skeleton Mesh Data...")
+			#RG - dump psk file
+			psk.PrintOut()
+			file = open(psk_filename, "wb") 
+			file.write(psk.dump())
+			file.close() 
+			print ("Successfully Exported File: " + psk_filename)
+		if bpy.context.scene.unrealexportpsa == True:
+			print("Writing Animaiton Data...")
+			#RG - dump psa file
+			if not psa.IsEmpty():
+				psa.PrintOut()
+				file = open(psa_filename, "wb") 
+				file.write(psa.dump())
+				file.close() 
+				print ("Successfully Exported File: " + psa_filename)
+			else:
+				print ("No Animations (.psa file) to Export")
+
+		print ('PSK/PSA Export Script finished in %.2f seconds' % (time.clock() - start_time))
+		
+		#MSG BOX EXPORT COMPLETE
+		#...
+
+		#DONE
+		print ("PSK/PSA Export Complete")
+
+def write_data(path, context, user_setting):
+	print("//============================")
+	print("// running psk/psa export...")
+	print("//============================")
+	fs_callback(path, context, user_setting)
+	pass
+
+from bpy.props import *
+
+exporttypedata = []
+
+# [index,text field,0] #or something like that
+exporttypedata.append(("0","PSK","Export PSK"))
+exporttypedata.append(("1","PSA","Export PSA"))
+exporttypedata.append(("2","ALL","Export ALL"))
+
+IntProperty= bpy.types.Scene.IntProperty
+
+IntProperty(attr="unrealfpsrate", name="fps rate",
+    description="Set the frame per second (fps) for unreal.",
+    default=24,min=1,max=100)
+	
+bpy.types.Scene.EnumProperty( attr="unrealexport_settings",
+    name="Export:",
+    description="Select a export settings (psk/psa/all)...",
+    items = exporttypedata, default = '0')
+		
+bpy.types.Scene.BoolProperty( attr="unrealtriangulatebool",
+    name="Triangulate Mesh",
+    description="Convert Quad to Tri Mesh Boolean...",
+    default=False)
+	
+bpy.types.Scene.BoolProperty( attr="unrealexportpsk",
+    name="bool export psa",
+    description="bool for exporting this psk format",
+    default=False)
+	
+bpy.types.Scene.BoolProperty( attr="unrealexportpsa",
+    name="bool export psa",
+    description="bool for exporting this psa format",
+    default=False)
+
+class ExportUDKAnimData(bpy.types.Operator):
+	global exportmessage
+	'''Export Skeleton Mesh / Animation Data file(s)'''
+	bl_idname = "export.udk_anim_data" # this is important since its how bpy.ops.export.udk_anim_data is constructed
+	bl_label = "Export PSK/PSA"
+	__doc__ = "One mesh and one armature else select one mesh or armature to be exported."
+
+	# List of operator properties, the attributes will be assigned
+	# to the class instance from the operator settings before calling.
+
+	# TODO, add props
+	path = StringProperty(name="File Path", description="File path used for exporting the PSA file", maxlen= 1024, default= "")
+	use_setting = BoolProperty(name="No Options Yet", description="No Options Yet", default= True)
+	filename = StringProperty(name="filename", description="", maxlen= 1024, default= "")
+	directory = StringProperty(name="directory", description="", maxlen= 1024, default= "")
+	pskexportbool = BoolProperty(name="Export PSK", description="Export Skeletal Mesh", default= True)
+	psaexportbool = BoolProperty(name="Export PSA", description="Export Action Set (Animation Data)", default= True)
+	#fpssettomg = IntProperty(name="FPS", attr="fpsunrealexport",description="", default=24, min=1, max=120, soft_min=1, soft_max=120, step=1, options={'ANIMATABLE'})
+
+	def poll(self, context):
+		return context.active_object != None
+
+	def execute(self, context):
+		#check if  skeleton mesh is needed to be exported
+		if (self.properties.pskexportbool):
+			bpy.context.scene.unrealexportpsk = True
+		else:
+			bpy.context.scene.unrealexportpsk = False
+		#check if  animation data is needed to be exported
+		if (self.properties.psaexportbool):
+			bpy.context.scene.unrealexportpsa = True
+		else:
+			bpy.context.scene.unrealexportpsa = False
+			
+		#print(">>>>>",dir(self))
+		#self.properties.pskexportbool
+		#self.layout.prop(context,context.scene.render,"fps")
+		write_data(self.properties.path, context, self.properties.use_setting)
+		
+		self.report({'WARNING', 'INFO'}, exportmessage)
+		return {'FINISHED'}
+		
+	def invoke(self, context, event):
+		wm = context.manager
+		wm.add_fileselect(self)
+		return {'RUNNING_MODAL'}	
+		
+def menu_func(self, context):
+	bpy.context.scene.unrealexportpsk = True
+	bpy.context.scene.unrealexportpsa = True
+	default_path = bpy.data.filename.replace(".blend", ".psk")
+	self.layout.operator ("export.udk_anim_data", text="Skeleton Mesh / Animation Data (.psk/.psa)").path = default_path
+
+class VIEW3D_PT_unrealtools_objectmode(bpy.types.Panel):
+	bl_space_type = "VIEW_3D"
+	bl_region_type = "TOOLS"
+	bl_label = "Unreal Tools"
+	
+	def poll(self, context):
+		return context.active_object
+    
+	def draw(self, context):
+		layout = self.layout
+		#layout.label(text="Unreal Tools")
+		rd = context.scene
+		#drop box
+		layout.prop(rd, "unrealexport_settings",expand=True)
+		#layout.prop(rd, "unrealexport_settings")
+		#button
+		layout.operator("object.UnrealExport")
+		#FPS #it use the real data from your scene
+		layout.prop(rd.render, "fps")
+		
+		#row = layout.row()
+		#row.label(text="Action Set(s)(not build)")
+		#for action in  bpy.data.actions:
+			#print(dir( action))
+			#print(action.get_frame_range())
+			#row = layout.row()
+			#row.prop(action, "name")
+			
+			#print(dir(action.groups[0]))
+			#for g in action.groups:#those are bones
+				#print("group...")
+				#print(dir(g))
+				#print("////////////")
+				#print((g.name))
+				#print("////////////")
+			
+			#row.label(text="Active:" + action.select)
+		btrimesh = False
+		
+class OBJECT_OT_UnrealExport(bpy.types.Operator):
+	global exportmessage
+	bl_idname = "OBJECT_OT_UnrealExport"
+	bl_label = "Unreal Export"
+	__doc__ = "Select export setting for .psk/.psa or both."
+	
+	def invoke(self, context, event):
+		path = StringProperty(name="File Path", description="File path used for exporting the PSA file", maxlen= 1024, default= "")
+		print("Init Export Script:")
+		if(int(bpy.context.scene.unrealexport_settings) == 0):
+			bpy.context.scene.unrealexportpsk = True
+			bpy.context.scene.unrealexportpsa = False
+			print("Exporting PSK...")
+		if(int(bpy.context.scene.unrealexport_settings) == 1):
+			bpy.context.scene.unrealexportpsk = False
+			bpy.context.scene.unrealexportpsa = True
+			print("Exporting PSA...")
+		if(int(bpy.context.scene.unrealexport_settings) == 2):
+			bpy.context.scene.unrealexportpsk = True
+			bpy.context.scene.unrealexportpsa = True
+			print("Exporting ALL...")
+		default_path = bpy.data.filename.replace(".blend", ".psk")
+		fs_callback(default_path, bpy.context, False)
+		self.report({'WARNING', 'INFO'}, exportmessage)
+		return{'FINISHED'}	
+	
+def register():
+	global MENUPANELBOOL
+	if MENUPANELBOOL:
+		bpy.types.register(OBJECT_OT_UnrealExport)
+		bpy.types.register(VIEW3D_PT_unrealtools_objectmode)
+	bpy.types.register(ExportUDKAnimData)
+	bpy.types.INFO_MT_file_export.append(menu_func)
+    
+def unregister():
+	global MENUPANELBOOL
+	if MENUPANELBOOL:
+		bpy.types.unregister(OBJECT_OT_UnrealExport)
+		bpy.types.unregister(VIEW3D_PT_unrealtools_objectmode)
+	bpy.types.unregister(ExportUDKAnimData)
+	bpy.types.INFO_MT_file_export.remove(menu_func)
+
+if __name__ == "__main__":
+    register()
\ No newline at end of file
diff --git a/import_scene_unreal_psk.py b/import_scene_unreal_psk.py
new file mode 100644
index 0000000000000000000000000000000000000000..f56f08d513e41a987b035485dafab102011958fa
--- /dev/null
+++ b/import_scene_unreal_psk.py
@@ -0,0 +1,598 @@
+#!BPY
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA	02111-1307	USA
+
+bl_addon_info = {
+    'name': 'Import: Unreal Skeleton Mesh(.psk)',
+    'author': 'Darknet',
+    'version': '2.0',
+    'blender': (2, 5, 3),
+    'location': 'File > Import ',
+    'description': 'Import Unreal Engine (.psk)',
+    'url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/File_I-O/Unreal_psk_psa',
+    'category': 'Import/Export'}
+
+"""
+Name: 'Skeleton Mesh Import(.psk)'
+Blender: 250
+Group: 'Import'
+Tip: 'Import mesh data from Unreal Tournament PSK file.'
+
+Updated by: Darknet
+
+# Unreal Tournament PSK file to Blender mesh converter V1.0
+# Author: D.M. Sturgeon (camg188 at the elYsium forum)
+# Imports a *psk file to a new mesh
+
+#-No UV Texutre
+#-No Weight
+#-No Armature Bones
+#-No Material ID
+#-Export Text Log From Current Location File (Bool )
+"""
+
+import bpy
+import mathutils
+import os
+import sys
+import string
+import math
+import re
+from string import *
+from struct import *
+from math import *
+
+#from bpy.props import *
+
+import mathutils
+
+vector = mathutils.Vector
+
+#output log in to txt file
+DEBUGLOG = False
+
+scale = 1.0
+bonesize = 1.0
+md5_bones=[]
+
+def unpack_list(list_of_tuples):
+    l = []
+    for t in list_of_tuples:
+        l.extend(t)
+    return l
+"""
+class md5_bone:
+	bone_index=0
+	name=""
+	bindpos=[]
+	bindmat = mathutils.Quaternion()
+	parent=""
+	parent_index=0
+	blenderbone=None
+	roll=0
+
+	def __init__(self):
+		self.bone_index=0
+		self.name=""
+		self.bindpos=[0.0]*3
+		self.bindmat=[None]*3  #is this how you initilize a 2d-array
+		for i in range(3): self.bindmat[i] = [0.0]*3
+		self.parent=""
+		self.parent_index=0
+		self.blenderbone=None
+
+	def dump(self):
+		print ("bone index: ", self.bone_index)
+		print ("name: ", self.name)
+		print ("bind position: ", self.bindpos)
+		print ("bind translation matrix: ", self.bindmat)
+		print ("parent: ", self.parent)
+		print ("parent index: ", self.parent_index)
+		print ("blenderbone: ", self.blenderbone)
+"""
+class md5_bone:
+	bone_index=0
+	name=""
+	bindpos=[]
+	bindmat=[]
+	scale = []
+	parent=""
+	parent_index=0
+	blenderbone=None
+	roll=0
+
+	def __init__(self):
+		self.bone_index=0
+		self.name=""
+		self.bindpos=[0.0]*3
+		self.scale=[0.0]*3
+		self.bindmat=[None]*3  #is this how you initilize a 2d-array
+		for i in range(3): self.bindmat[i] = [0.0]*3
+		self.parent=""
+		self.parent_index=0
+		self.blenderbone=None
+
+	def dump(self):
+		print ("bone index: ", self.bone_index)
+		print ("name: ", self.name)
+		print ("bind position: ", self.bindpos)
+		print ("bind translation matrix: ", self.bindmat)
+		print ("parent: ", self.parent)
+		print ("parent index: ", self.parent_index)
+		print ("blenderbone: ", self.blenderbone)
+		
+#http://www.blender.org/forum/viewtopic.php?t=13340&sid=8b17d5de07b17960021bbd72cac0495f			
+def fixRollZ(b):
+	v = (b.tail-b.head)/b.length
+	b.roll -= math.degrees(math.atan2(v[0]*v[2]*(1 - v[1]),v[0]*v[0] + v[1]*v[2]*v[2])) 
+def fixRoll(b):
+	v = (b.tail-b.head)/b.length
+	if v[2]*v[2] > .5:
+		#align X-axis
+		b.roll += math.degrees(math.atan2(v[0]*v[2]*(1 - v[1]),v[2]*v[2] + v[1]*v[0]*v[0]))
+	else:
+		#align Z-axis
+		b.roll -= math.degrees(math.atan2(v[0]*v[2]*(1 - v[1]),v[0]*v[0] + v[1]*v[2]*v[2])) 
+		
+def pskimport(infile):
+	global DEBUGLOG
+	print ("--------------------------------------------------")
+	print ("---------SCRIPT EXECUTING PYTHON IMPORTER---------")
+	print ("--------------------------------------------------")
+	print ("Importing file: ", infile)
+	
+	md5_bones=[]
+	pskfile = open(infile,'rb')
+	if (DEBUGLOG):
+		logpath = infile.replace(".psk", ".txt")
+		print("logpath:",logpath)
+		logf = open(logpath,'w')
+		
+	def printlog(strdata):
+		if (DEBUGLOG):
+			logf.write(strdata)
+			
+	objName = infile.split('\\')[-1].split('.')[0]
+	
+	me_ob = bpy.data.meshes.new(objName)
+	print("objName:",objName)
+	printlog(("New Mesh = " + me_ob.name + "\n"))
+	#read general header
+	indata = unpack('20s3i',pskfile.read(32))
+	#not using the general header at this time
+	#================================================================================================== 
+	# vertex point
+	#================================================================================================== 
+	#read the PNTS0000 header
+	indata = unpack('20s3i',pskfile.read(32))
+	recCount = indata[3]
+	printlog(( "Nbr of PNTS0000 records: " + str(recCount) + "\n"))
+	counter = 0
+	verts = []
+	while counter < recCount:
+		counter = counter + 1
+		indata = unpack('3f',pskfile.read(12))
+		#print(indata[0],indata[1],indata[2])
+		verts.extend([(indata[0],indata[1],indata[2])])
+		#Tmsh.verts.append(NMesh.Vert(indata[0],indata[1],indata[2]))
+		
+	#================================================================================================== 
+	# UV
+	#================================================================================================== 
+	#read the VTXW0000 header
+	indata = unpack('20s3i',pskfile.read(32))
+	recCount = indata[3]
+	printlog( "Nbr of VTXW0000 records: " + str(recCount)+ "\n")
+	counter = 0
+	UVCoords = []
+	#UVCoords record format = [index to PNTS, U coord, v coord]
+	while counter < recCount:
+		counter = counter + 1
+		indata = unpack('hhffhh',pskfile.read(16))
+		UVCoords.append([indata[0],indata[2],indata[3]])
+		#print([indata[0],indata[2],indata[3]])
+		#print([indata[1],indata[2],indata[3]])
+		
+	#================================================================================================== 
+	# Face
+	#================================================================================================== 
+	#read the FACE0000 header
+	indata = unpack('20s3i',pskfile.read(32))
+	recCount = indata[3]
+	printlog( "Nbr of FACE0000 records: "+ str(recCount) + "\n")
+	#PSK FACE0000 fields: WdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
+	#associate MatIdx to an image, associate SmthGrp to a material
+	SGlist = []
+	counter = 0
+	faces = []
+	faceuv = []
+	while counter < recCount:
+		counter = counter + 1
+		indata = unpack('hhhbbi',pskfile.read(12))
+		#the psk values are: nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
+		#indata[0] = index of UVCoords
+		#UVCoords[indata[0]]=[index to PNTS, U coord, v coord]
+		#UVCoords[indata[0]][0] = index to PNTS
+		PNTSA = UVCoords[indata[0]][0]
+		PNTSB = UVCoords[indata[1]][0]
+		PNTSC = UVCoords[indata[2]][0]
+		#print(PNTSA,PNTSB,PNTSC) #face id vertex
+		#faces.extend([0,1,2,0])
+		faces.extend([PNTSA,PNTSB,PNTSC,0])
+		uv = []
+		u0 = UVCoords[indata[0]][1]
+		v0 = UVCoords[indata[0]][2]
+		uv.append([u0,v0])
+		u1 = UVCoords[indata[1]][1]
+		v1 = UVCoords[indata[1]][2]
+		uv.append([u1,v1])
+		u2 = UVCoords[indata[2]][1]
+		v2 = UVCoords[indata[2]][2]
+		uv.append([u2,v2])
+		faceuv.append(uv)
+		#print("UV: ",u0,v0)
+		#update the uv var of the last item in the Tmsh.faces list
+		# which is the face just added above
+		##Tmsh.faces[-1].uv = [(u0,v0),(u1,v1),(u2,v2)]
+		#print("smooth:",indata[5])
+		#collect a list of the smoothing groups
+		if SGlist.count(indata[5]) == 0:
+			SGlist.append(indata[5])
+			print("smooth:",indata[5])
+		#assign a material index to the face
+		#Tmsh.faces[-1].materialIndex = SGlist.index(indata[5])
+	printlog( "Using Materials to represent PSK Smoothing Groups...\n")
+	#==========
+	# skip something...
+	#==========
+	
+	#================================================================================================== 
+	# Material
+	#================================================================================================== 
+	##
+	#read the MATT0000 header
+	indata = unpack('20s3i',pskfile.read(32))
+	recCount = indata[3]
+	printlog("Nbr of MATT0000 records: " +  str(recCount) + "\n" )
+	printlog(" - Not importing any material data now. PSKs are texture wrapped! \n")
+	counter = 0
+	while counter < recCount:
+		counter = counter + 1
+		indata = unpack('64s6i',pskfile.read(88))
+	##
+	
+	#================================================================================================== 
+	# Bones (Armature)
+	#================================================================================================== 
+	#read the REFSKEL0 header
+	indata = unpack('20s3i',pskfile.read(32))
+	recCount = indata[3]
+	printlog( "Nbr of REFSKEL0 records: " + str(recCount) + "\n")
+	Bns = []
+	bone = []
+	nobone = 0
+	#================================================================================================== 
+	# Bone Data 
+	#==================================================================================================
+	counter = 0
+	print ("---PRASE--BONES---")
+	while counter < recCount:
+		indata = unpack('64s3i11f',pskfile.read(120))
+		#print( "DATA",str(indata))
+		bone.append(indata)
+		
+		createbone = md5_bone()
+		#temp_name = indata[0][:30]
+		temp_name = indata[0]
+		
+		temp_name = bytes.decode(temp_name)
+		temp_name = temp_name.lstrip(" ")
+		temp_name = temp_name.rstrip(" ")
+		temp_name = temp_name.strip()
+		temp_name = temp_name.strip( bytes.decode(b'\x00'))
+		print ("temp_name:", temp_name, "||")
+		createbone.name = temp_name
+		createbone.bone_index = counter
+		createbone.parent_index = indata[3]
+		createbone.bindpos[0] = indata[8]
+		createbone.bindpos[1] = indata[9]
+		createbone.bindpos[2] = indata[10]
+		createbone.scale[0] = indata[12]
+		createbone.scale[1] = indata[13]
+		createbone.scale[2] = indata[14]
+		
+		#w,x,y,z
+		if (counter == 0):#main parent
+			print("no parent bone")
+			createbone.bindmat = mathutils.Quaternion(indata[7],indata[4],indata[5],indata[6])
+			#createbone.bindmat = mathutils.Quaternion(indata[7],-indata[4],-indata[5],-indata[6])
+		else:#parent
+			print("parent bone")
+			createbone.bindmat = mathutils.Quaternion(indata[7],-indata[4],-indata[5],-indata[6])
+			#createbone.bindmat = mathutils.Quaternion(indata[7],indata[4],indata[5],indata[6])
+			
+		md5_bones.append(createbone)
+		counter = counter + 1
+		bnstr = (str(indata[0]))
+		Bns.append(bnstr)
+		
+	for pbone in md5_bones:
+		pbone.parent =  md5_bones[pbone.parent_index].name
+			
+	bonecount = 0
+	for armbone in bone:
+		temp_name = armbone[0][:30]
+		#print ("BONE NAME: ",len(temp_name))
+		temp_name=str((temp_name))
+		#temp_name = temp_name[1]
+		#print ("BONE NAME: ",temp_name)
+		bonecount +=1
+	print ("-------------------------")
+	print ("----Creating--Armature---")
+	print ("-------------------------")
+	
+	#================================================================================================
+	#Check armature if exist if so create or update or remove all and addnew bone
+	#================================================================================================
+	#bpy.ops.object.mode_set(mode='OBJECT')
+	meshname ="ArmObject"
+	objectname = "armaturedata"
+	bfound = False
+	arm = None
+	for obj in bpy.data.objects:
+		if (obj.name == meshname):
+			bfound = True
+			arm = obj
+			break
+			
+	if bfound == False:
+		armdata = bpy.data.armatures.new(objectname)
+		ob_new = bpy.data.objects.new(meshname, armdata)
+		#ob_new = bpy.data.objects.new(meshname, 'ARMATURE')
+		#ob_new.data = armdata
+		bpy.context.scene.objects.link(ob_new)
+		#bpy.ops.object.mode_set(mode='OBJECT')
+		for i in bpy.context.scene.objects: i.selected = False #deselect all objects
+		ob_new.selected = True
+		#set current armature to edit the bone
+		bpy.context.scene.objects.active = ob_new
+		#set mode to able to edit the bone
+		bpy.ops.object.mode_set(mode='EDIT')
+		#newbone = ob_new.data.edit_bones.new('test')
+		#newbone.tail.y = 1
+		print("creating bone(s)")
+		for bone in md5_bones:
+			#print(dir(bone))
+			newbone = ob_new.data.edit_bones.new(bone.name)
+			#parent the bone
+			parentbone = None
+			print("bone name:",bone.name)
+			#note bone location is set in the real space or global not local
+			if bone.name != bone.parent:
+			
+				pos_x = bone.bindpos[0]
+				pos_y = bone.bindpos[1]
+				pos_z = bone.bindpos[2]
+			
+				#print( "LINKING:" , bone.parent ,"j")
+				parentbone = ob_new.data.edit_bones[bone.parent]
+				newbone.parent = parentbone
+				rotmatrix = bone.bindmat.to_matrix().resize4x4().rotation_part()
+				
+				#parent_head = parentbone.head * parentbone.matrix.to_quat().inverse()
+				#parent_tail = parentbone.tail * parentbone.matrix.to_quat().inverse()
+				#location=vector(pos_x,pos_y,pos_z)
+				#set_position = (parent_tail - parent_head) + location
+				#print("tmp head:",set_position)
+				
+				#pos_x = set_position.x
+				#pos_y = set_position.y
+				#pos_z = set_position.z
+				
+				newbone.head.x = parentbone.head.x + pos_x
+				newbone.head.y = parentbone.head.y + pos_y
+				newbone.head.z = parentbone.head.z + pos_z
+				print("head:",newbone.head)
+				newbone.tail.x = parentbone.head.x + (pos_x + bonesize * rotmatrix[1][0])
+				newbone.tail.y = parentbone.head.y + (pos_y + bonesize * rotmatrix[1][1])
+				newbone.tail.z = parentbone.head.z + (pos_z + bonesize * rotmatrix[1][2])
+			else:
+				rotmatrix = bone.bindmat.to_matrix().resize4x4().rotation_part()
+				newbone.head.x = bone.bindpos[0]
+				newbone.head.y = bone.bindpos[1]
+				newbone.head.z = bone.bindpos[2]
+				newbone.tail.x = bone.bindpos[0] + bonesize * rotmatrix[1][0]
+				newbone.tail.y = bone.bindpos[1] + bonesize * rotmatrix[1][1]
+				newbone.tail.z = bone.bindpos[2] + bonesize * rotmatrix[1][2]
+				#print("no parent")
+	
+	bpy.context.scene.update()
+	
+	#==================================================================================================
+	#END BONE DATA BUILD
+	#==================================================================================================
+	VtxCol = []
+	for x in range(len(Bns)):
+		#change the overall darkness of each material in a range between 0.1 and 0.9
+		tmpVal = ((float(x)+1.0)/(len(Bns))*0.7)+0.1
+		tmpVal = int(tmpVal * 256)
+		tmpCol = [tmpVal,tmpVal,tmpVal,0]
+		#Change the color of each material slightly
+		if x % 3 == 0:
+			if tmpCol[0] < 128: tmpCol[0] += 60
+			else: tmpCol[0] -= 60
+		if x % 3 == 1:
+			if tmpCol[1] < 128: tmpCol[1] += 60
+			else: tmpCol[1] -= 60
+		if x % 3 == 2:
+			if tmpCol[2] < 128: tmpCol[2] += 60
+			else: tmpCol[2] -= 60
+		#Add the material to the mesh
+		VtxCol.append(tmpCol)
+	
+	#================================================================================================== 
+	# Bone Weight
+	#================================================================================================== 
+	#read the RAWW0000 header
+	indata = unpack('20s3i',pskfile.read(32))
+	recCount = indata[3]
+	printlog( "Nbr of RAWW0000 records: " + str(recCount) +"\n")
+	#RAWW0000 fields: Weight|PntIdx|BoneIdx
+	RWghts = []
+	counter = 0
+	while counter < recCount:
+		counter = counter + 1
+		indata = unpack('fii',pskfile.read(12))
+		RWghts.append([indata[1],indata[2],indata[0]])
+	#RWghts fields = PntIdx|BoneIdx|Weight
+	RWghts.sort()
+	printlog( "len(RWghts)=" + str(len(RWghts)) + "\n")
+	#Tmsh.update()
+	
+	#set the Vertex Colors of the faces
+	#face.v[n] = RWghts[0]
+	#RWghts[1] = index of VtxCol
+	"""
+	for x in range(len(Tmsh.faces)):
+		for y in range(len(Tmsh.faces[x].v)):
+			#find v in RWghts[n][0]
+			findVal = Tmsh.faces[x].v[y].index
+			n = 0
+			while findVal != RWghts[n][0]:
+				n = n + 1
+			TmpCol = VtxCol[RWghts[n][1]]
+			#check if a vertex has more than one influence
+			if n != len(RWghts)-1:
+				if RWghts[n][0] == RWghts[n+1][0]:
+					#if there is more than one influence, use the one with the greater influence
+					#for simplicity only 2 influences are checked, 2nd and 3rd influences are usually very small
+					if RWghts[n][2] < RWghts[n+1][2]:
+						TmpCol = VtxCol[RWghts[n+1][1]]
+		Tmsh.faces[x].col.append(NMesh.Col(TmpCol[0],TmpCol[1],TmpCol[2],0))
+	"""
+	if (DEBUGLOG):
+		logf.close()
+	#================================================================================================== 
+	#Building Mesh
+	#================================================================================================== 
+	print("vertex:",len(verts),"faces:",len(faces))
+	me_ob.add_geometry(len(verts), 0, int(len(faces)/4))
+	me_ob.verts.foreach_set("co", unpack_list(verts))
+	
+	me_ob.faces.foreach_set("verts_raw", faces)
+	me_ob.faces.foreach_set("smooth", [False] * len(me_ob.faces))
+	me_ob.update()
+	
+	#===================================================================================================
+	#UV Setup
+	#===================================================================================================
+	texture = []
+	texturename = "text1"
+	#print(dir(bpy.data))
+	if (len(faceuv) > 0):
+		me_ob.add_uv_texture() #add one uv texture
+		for i, face in enumerate(me_ob.faces):
+			blender_tface= me_ob.uv_textures[0].data[i] #face
+			blender_tface.uv1 = faceuv[i][0] #uv = (0,0)
+			blender_tface.uv2 = faceuv[i][1] #uv = (0,0)
+			blender_tface.uv3 = faceuv[i][2] #uv = (0,0)
+		texture.append(me_ob.uv_textures[0])
+		
+		#for tex in me_ob.uv_textures:
+			#print("mesh tex:",dir(tex))
+			#print((tex.name))
+	
+	#===================================================================================================
+	#Material Setup
+	#===================================================================================================
+	materialname = "mat"
+	materials = []
+	
+	matdata = bpy.data.materials.new(materialname)
+	#color is 0 - 1 not in 0 - 255
+	#matdata.mirror_color=(float(0.04),float(0.08),float(0.44))
+	matdata.diffuse_color=(float(0.04),float(0.08),float(0.44))#blue color
+	#print(dir(me_ob.uv_textures[0].data))
+	texdata = None
+	texdata = bpy.data.textures[len(bpy.data.textures)-1]
+	if (texdata != None):
+		#print(texdata.name)
+		#print(dir(texdata))
+		texdata.name = "texturelist1"
+		matdata.active_texture = texdata
+	materials.append(matdata)
+	#matdata = bpy.data.materials.new(materialname)
+	#materials.append(matdata)
+	#= make sure the list isnt too big
+	for material in materials:
+		#add material to the mesh list of materials
+		me_ob.add_material(material)
+	#===================================================================================================
+	#
+	#===================================================================================================
+	obmesh = bpy.data.objects.new(objName,me_ob)
+	#check if there is a material to set to
+	if len(materials) > 0:
+		obmesh.active_material = materials[0] #material setup tmp
+	
+	bpy.context.scene.objects.link(obmesh)
+	
+	bpy.context.scene.update()
+	
+	print ("PSK2Blender completed")
+#End of def pskimport#########################
+
+def getInputFilename(filename):
+	checktype = filename.split('\\')[-1].split('.')[1]
+	print ("------------",filename)
+	if checktype.upper() != 'PSK':
+		print ("  Selected file = ",filename)
+		raise (IOError, "The selected input file is not a *.psk file")
+	pskimport(filename)
+  
+from bpy.props import *
+
+class IMPORT_OT_psk(bpy.types.Operator):
+	'''Load a skeleton mesh psk File'''
+	bl_idname = "import_scene.psk"
+	bl_label = "Import PSK"
+
+	# List of operator properties, the attributes will be assigned
+	# to the class instance from the operator settings before calling.
+	path = StringProperty(name="File Path", description="File path used for importing the OBJ file", maxlen= 1024, default= "")
+
+	def execute(self, context):
+		getInputFilename(self.properties.path)
+		return {'FINISHED'}
+
+	def invoke(self, context, event):
+		wm = context.manager
+		wm.add_fileselect(self)
+		return {'RUNNING_MODAL'}  
+  
+menu_func = lambda self, context: self.layout.operator(IMPORT_OT_psk.bl_idname, text="Skeleton Mesh (.psk)")
+
+def register():
+    bpy.types.register(IMPORT_OT_psk)
+    bpy.types.INFO_MT_file_import.append(menu_func)
+    
+def unregister():
+    bpy.types.unregister(IMPORT_OT_psk)
+    bpy.types.INFO_MT_file_import.remove(menu_func)
+
+if __name__ == "__main__":
+	register()
+#note this only read the data and will not be place in the scene	
+#getInputFilename('C:\\blenderfiles\\BotA.psk') 
+#getInputFilename('C:\\blenderfiles\\AA.PSK')