diff --git a/io_scene_ms3d/__init__.py b/io_scene_ms3d/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ae5199d422b1c8818f9795f623317c1b3e05005
--- /dev/null
+++ b/io_scene_ms3d/__init__.py
@@ -0,0 +1,107 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+bl_info = {
+    'name': "MilkShape3D MS3D format (.ms3d)",
+    'description': "Import / Export MilkShape3D MS3D files"\
+            " (conform with MilkShape3D v1.8.4)",
+    'author': "Alexander Nussbaumer",
+    'version': (0, 93, 1),
+    'blender': (2, 65, 3),
+    'location': "File > Import & File > Export",
+    'warning': "",
+    'wiki_url': "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
+            "Scripts/Import-Export/MilkShape3D_MS3D",
+    'tracker_url': "http://projects.blender.org/tracker/index.php"\
+            "?func=detail&aid=29404",
+    'category': "Import-Export",
+    }
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+
+
+# To support reload properly, try to access a package var,
+# if it's there, reload everything
+if 'bpy' in locals():
+    import imp
+    if 'io_scene_ms3d.ms3d_ui' in locals():
+        imp.reload(io_scene_ms3d.ms3d_ui)
+else:
+    from io_scene_ms3d.ms3d_ui import (
+            Ms3dImportOperator,
+            Ms3dExportOperator,
+            )
+
+
+#import blender stuff
+from bpy.utils import (
+        register_module,
+        unregister_module,
+        )
+from bpy.types import (
+        INFO_MT_file_export,
+        INFO_MT_file_import,
+        )
+
+
+###############################################################################
+# registration
+def register():
+    ####################
+    # F8 - key
+    import imp
+    imp.reload(ms3d_ui)
+    # F8 - key
+    ####################
+
+    ms3d_ui.register()
+
+    register_module(__name__)
+    INFO_MT_file_export.append(Ms3dExportOperator.menu_func)
+    INFO_MT_file_import.append(Ms3dImportOperator.menu_func)
+
+
+def unregister():
+    ms3d_ui.unregister()
+
+    unregister_module(__name__)
+    INFO_MT_file_export.remove(Ms3dExportOperator.menu_func)
+    INFO_MT_file_import.remove(Ms3dImportOperator.menu_func)
+
+
+###############################################################################
+# global entry point
+if (__name__ == "__main__"):
+    register()
+
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####
diff --git a/io_scene_ms3d/ms3d_export.py b/io_scene_ms3d/ms3d_export.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc6425a19af1034cb5140809ec22f5f8e0146e40
--- /dev/null
+++ b/io_scene_ms3d/ms3d_export.py
@@ -0,0 +1,880 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+
+
+#import python stuff
+import io
+from math import (
+        pi,
+        )
+from mathutils import (
+        Matrix,
+        )
+from os import (
+        path,
+        )
+from sys import (
+        exc_info,
+        )
+from time import (
+        time,
+        )
+
+
+# import io_scene_ms3d stuff
+from io_scene_ms3d.ms3d_strings import (
+        ms3d_str,
+        )
+from io_scene_ms3d.ms3d_spec import (
+        Ms3dSpec,
+        Ms3dModel,
+        Ms3dVertex,
+        Ms3dTriangle,
+        Ms3dGroup,
+        Ms3dMaterial,
+        Ms3dJoint,
+        Ms3dRotationKeyframe,
+        Ms3dTranslationKeyframe,
+        Ms3dCommentEx,
+        )
+from io_scene_ms3d.ms3d_utils import (
+        select_all,
+        enable_edit_mode,
+        pre_setup_environment,
+        post_setup_environment,
+        matrix_difference,
+        )
+from io_scene_ms3d.ms3d_ui import (
+        Ms3dUi,
+        Ms3dMaterialProperties,
+        Ms3dMaterialHelper,
+        )
+
+
+#import blender stuff
+from bpy import (
+        ops,
+        )
+import bmesh
+
+
+###############################################################################
+class Ms3dExporter():
+    """
+    Load a MilkShape3D MS3D File
+    """
+    def __init__(self,
+            report,
+            verbose=False,
+            use_blender_names=True,
+            use_blender_materials=False,
+            apply_transform=True,
+            apply_modifiers=True,
+            apply_modifiers_mode='PREVIEW',
+            use_animation=True,
+            normalize_weights=True,
+            shrink_to_keys=False,
+            bake_each_frame=True,
+            ):
+        self.report = report
+        self.options_verbose = verbose
+        self.options_use_blender_names = use_blender_names
+        self.options_use_blender_materials = use_blender_materials
+        self.options_apply_transform = apply_transform
+        self.options_apply_modifiers = apply_modifiers
+        self.options_apply_modifiers_mode = apply_modifiers_mode
+        self.options_use_animation = use_animation
+        self.options_normalize_weights = normalize_weights
+        self.options_shrink_to_keys = shrink_to_keys
+        self.options_bake_each_frame = bake_each_frame
+        pass
+
+    # create a empty ms3d ms3d_model
+    # fill ms3d_model with blender content
+    # writer ms3d file
+    def write(self, blender_context, filepath):
+        """convert bender content to ms3d content and write it to file"""
+
+        t1 = time()
+        t2 = None
+
+        try:
+            # setup environment
+            pre_setup_environment(self, blender_context)
+
+            # create an empty ms3d template
+            ms3d_model = Ms3dModel()
+
+            # inject blender data to ms3d file
+            self.from_blender(blender_context, ms3d_model)
+
+            t2 = time()
+
+            try:
+                # write ms3d file to disk
+                with io.FileIO(filepath, "wb") as raw_io:
+                    ms3d_model.write(raw_io)
+                    raw_io.flush()
+                    raw_io.close()
+            finally:
+                pass
+
+            # if option is set, this time will enlargs the io time
+            if self.options_verbose:
+                ms3d_model.print_internal()
+
+            post_setup_environment(self, blender_context)
+            # restore active object
+            blender_context.scene.objects.active = self.active_object
+
+            if ((not blender_context.scene.objects.active)
+                    and (blender_context.selected_objects)):
+                blender_context.scene.objects.active \
+                        = blender_context.selected_objects[0]
+
+            # restore pre operator undo state
+            blender_context.user_preferences.edit.use_global_undo = self.undo
+
+            is_valid, statistics = ms3d_model.is_valid()
+            print()
+            print("##########################################################")
+            print("Export from Blender to MS3D")
+            print(statistics)
+            print("##########################################################")
+
+        except Exception:
+            type, value, traceback = exc_info()
+            print("write - exception in try block\n  type: '{0}'\n"
+                    "  value: '{1}'".format(type, value, traceback))
+
+            if t2 is None:
+                t2 = time()
+
+            raise
+
+        else:
+            pass
+
+        t3 = time()
+        print(ms3d_str['SUMMARY_EXPORT'].format(
+                (t3 - t1), (t2 - t1), (t3 - t2)))
+
+        return {"FINISHED"}
+
+
+    ###########################################################################
+    def from_blender(self, blender_context, ms3d_model):
+        blender_mesh_objects = []
+
+        source = (blender_context.active_object, )
+
+        for blender_object in source:
+            if blender_object and blender_object.type == 'MESH' \
+                    and blender_object.is_visible(blender_context.scene):
+                blender_mesh_objects.append(blender_object)
+
+        blender_to_ms3d_bones = {}
+
+        self.create_animation(blender_context, ms3d_model, blender_mesh_objects, blender_to_ms3d_bones)
+        self.create_geometry(blender_context, ms3d_model, blender_mesh_objects,
+                blender_to_ms3d_bones)
+
+
+    ###########################################################################
+    def create_geometry(self, blender_context, ms3d_model, blender_mesh_objects, blender_to_ms3d_bones):
+        blender_scene = blender_context.scene
+
+        blender_to_ms3d_vertices = {}
+        blender_to_ms3d_triangles = {}
+        blender_to_ms3d_groups = {}
+        blender_to_ms3d_materials = {}
+
+        for blender_mesh_object in blender_mesh_objects:
+            blender_mesh = blender_mesh_object.data
+
+            ms3d_model._model_ex_object.joint_size = \
+                    blender_mesh.ms3d.joint_size
+            ms3d_model._model_ex_object.alpha_ref = blender_mesh.ms3d.alpha_ref
+            ms3d_model._model_ex_object.transparency_mode = \
+                    Ms3dUi.transparency_mode_to_ms3d(
+                    blender_mesh.ms3d.transparency_mode)
+
+            ##########################
+            # prepare ms3d groups if available
+            # works only for exporting active object
+            ##EXPORT_ACTIVE_ONLY:
+            for ms3d_local_group_index, blender_ms3d_group in enumerate(
+                    blender_mesh.ms3d.groups):
+                ms3d_group = Ms3dGroup()
+                ms3d_group.__index = len(ms3d_model._groups)
+                ms3d_group.name = blender_ms3d_group.name
+                ms3d_group.flags = Ms3dUi.flags_to_ms3d(blender_ms3d_group.flags)
+                if blender_ms3d_group.comment:
+                    ms3d_group._comment_object = Ms3dCommentEx()
+                    ms3d_group._comment_object.comment = \
+                            blender_ms3d_group.comment
+                    ms3d_group._comment_object.index = len(ms3d_model._groups)
+                ms3d_group.material_index = None # to mark as not setted
+                ms3d_model._groups.append(ms3d_group)
+                blender_to_ms3d_groups[blender_ms3d_group.id] = ms3d_group
+
+            ##########################
+            # i have to use BMesh, because there are several custom data stored.
+            # BMesh doesn't support quads_convert_to_tris()
+            # so, i use that very ugly way:
+            # create a complete copy of mesh and bend object data
+            # to be able to apply operations to it.
+
+            # temporary, create a full heavy copy of the model
+            # (object, mesh, modifiers)
+            blender_mesh_temp = blender_mesh_object.data.copy()
+            blender_mesh_object_temp = blender_mesh_object.copy()
+            blender_mesh_object_temp.data = blender_mesh_temp
+            blender_scene.objects.link(blender_mesh_object_temp)
+            blender_scene.objects.active = blender_mesh_object_temp
+
+            # apply transform
+            if self.options_apply_transform:
+                matrix_transform = blender_mesh_object_temp.matrix_local
+            else:
+                matrix_transform = Matrix()
+
+            # apply modifiers
+            for modifier in blender_mesh_object_temp.modifiers:
+                if self.options_apply_modifiers:
+                    # disable only armature modifiers and only,
+                    # when use_animation is enabled
+                    if  self.options_use_animation \
+                            and modifier.type in {'ARMATURE', }:
+                        modifier.show_viewport = False
+                        modifier.show_render = False
+                else:
+                    # disable all modifiers,
+                    # to be able to add and apply triangulate modifier later
+                    modifier.show_viewport = False
+                    modifier.show_render = False
+
+            # convert to tris by using the triangulate modifier
+            blender_mesh_object_temp.modifiers.new("temp", 'TRIANGULATE')
+            blender_mesh_temp = blender_mesh_object_temp.to_mesh(
+                    blender_scene,
+                    True,
+                    self.options_apply_modifiers_mode)
+
+            enable_edit_mode(True, blender_context)
+            bm = bmesh.new()
+            bm.from_mesh(blender_mesh_temp)
+
+            layer_texture = bm.faces.layers.tex.get(
+                    ms3d_str['OBJECT_LAYER_TEXTURE'])
+            if layer_texture is None:
+                layer_texture = bm.faces.layers.tex.new(
+                        ms3d_str['OBJECT_LAYER_TEXTURE'])
+
+            layer_smoothing_group = bm.faces.layers.int.get(
+                    ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+            if layer_smoothing_group is None:
+                layer_smoothing_group = bm.faces.layers.int.new(
+                        ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+
+            layer_group = bm.faces.layers.int.get(
+                    ms3d_str['OBJECT_LAYER_GROUP'])
+            if layer_group is None:
+                layer_group = bm.faces.layers.int.new(
+                        ms3d_str['OBJECT_LAYER_GROUP'])
+
+            layer_uv = bm.loops.layers.uv.get(ms3d_str['OBJECT_LAYER_UV'])
+            if layer_uv is None:
+                if bm.loops.layers.uv:
+                    layer_uv = bm.loops.layers.uv[0]
+                else:
+                    layer_uv = bm.loops.layers.uv.new(
+                            ms3d_str['OBJECT_LAYER_UV'])
+
+            layer_deform = bm.verts.layers.deform.active
+
+            layer_extra = bm.verts.layers.int.get(ms3d_str['OBJECT_LAYER_EXTRA'])
+            if layer_extra is None:
+                layer_extra = bm.verts.layers.int.new(
+                        ms3d_str['OBJECT_LAYER_EXTRA'])
+
+
+            ##########################
+            # handle vertices
+            for bmv in bm.verts:
+                item = blender_to_ms3d_vertices.get(bmv)
+                if item is None:
+                    index = len(ms3d_model._vertices)
+                    ms3d_vertex = Ms3dVertex()
+                    ms3d_vertex.__index = index
+
+                    ms3d_vertex._vertex = self.geometry_correction(
+                            matrix_transform * bmv.co)
+
+                    if self.options_use_animation and layer_deform:
+                        blender_vertex_group_ids = bmv[layer_deform]
+                        if blender_vertex_group_ids:
+                            count = 0
+                            bone_ids = []
+                            weights = []
+                            for blender_index, blender_weight \
+                                    in blender_vertex_group_ids.items():
+                                ms3d_joint = blender_to_ms3d_bones.get(
+                                        blender_mesh_object_temp.vertex_groups[\
+                                                blender_index].name)
+                                if ms3d_joint:
+                                    if count == 0:
+                                        ms3d_vertex.bone_id = ms3d_joint.__index
+                                        weights.append(
+                                                int(blender_weight * 100.0))
+                                    elif count == 1:
+                                        bone_ids.append(ms3d_joint.__index)
+                                        weights.append(
+                                                int(blender_weight * 100.0))
+                                    elif count == 2:
+                                        bone_ids.append(ms3d_joint.__index)
+                                        weights.append(
+                                                int(blender_weight * 100.0))
+                                    elif count == 3:
+                                        bone_ids.append(ms3d_joint.__index)
+                                        self.report(
+                                                {'WARNING', 'INFO'},
+                                                ms3d_str['WARNING_EXPORT_SKIP_WEIGHT'])
+                                    else:
+                                        # only first three weights will be supported / four bones
+                                        self.report(
+                                                {'WARNING', 'INFO'},
+                                                ms3d_str['WARNING_EXPORT_SKIP_WEIGHT_EX'])
+                                        break
+                                count+= 1
+
+                            while len(bone_ids) < 3:
+                                bone_ids.append(Ms3dSpec.DEFAULT_VERTEX_BONE_ID)
+                            while len(weights) < 3:
+                                weights.append(0)
+
+                            # normalize weights to 100
+                            if self.options_normalize_weights:
+                                weight_sum = 0
+                                for weight in weights:
+                                    weight_sum += weight
+
+                                if weight_sum > 100:
+                                    weight_normalize = 100 / weight_sum
+                                else:
+                                    weight_normalize = 1
+
+                                weight_sum = 100
+                                for index, weight in enumerate(weights):
+                                    if index >= count-1:
+                                        weights[index] = weight_sum
+                                        break
+                                    normalized_weight = int(
+                                            weight * weight_normalize)
+                                    weight_sum -= normalized_weight
+                                    weights[index] = normalized_weight
+
+                            ms3d_vertex._vertex_ex_object._bone_ids = \
+                                    tuple(bone_ids)
+                            ms3d_vertex._vertex_ex_object._weights = \
+                                    tuple(weights)
+
+                    if layer_extra:
+                        #ms3d_vertex._vertex_ex_object.extra = bmv[layer_extra]
+                        # bm.verts.layers.int does only support signed int32
+                        # convert signed int32 to unsigned int32 (little-endian)
+                        signed_int32 = bmv[layer_extra]
+                        bytes_int32 = signed_int32.to_bytes(
+                                4, byteorder='little', signed=True)
+                        unsigned_int32 = int.from_bytes(
+                                bytes_int32, byteorder='little', signed=False)
+                        ms3d_vertex._vertex_ex_object.extra = unsigned_int32
+
+                    ms3d_model._vertices.append(ms3d_vertex)
+                    blender_to_ms3d_vertices[bmv] = ms3d_vertex
+
+            ##########################
+            # handle faces / tris
+            for bmf in bm.faces:
+                item = blender_to_ms3d_triangles.get(bmf)
+                if item is None:
+                    index = len(ms3d_model._triangles)
+                    ms3d_triangle = Ms3dTriangle()
+                    ms3d_triangle.__index = index
+                    bmv0 = bmf.verts[0]
+                    bmv1 = bmf.verts[1]
+                    bmv2 = bmf.verts[2]
+                    ms3d_vertex0 = blender_to_ms3d_vertices[bmv0]
+                    ms3d_vertex1 = blender_to_ms3d_vertices[bmv1]
+                    ms3d_vertex2 = blender_to_ms3d_vertices[bmv2]
+                    ms3d_vertex0.reference_count += 1
+                    ms3d_vertex1.reference_count += 1
+                    ms3d_vertex2.reference_count += 1
+                    ms3d_triangle._vertex_indices = (
+                            ms3d_vertex0.__index,
+                            ms3d_vertex1.__index,
+                            ms3d_vertex2.__index,
+                            )
+                    ms3d_triangle._vertex_normals = (
+                            self.geometry_correction(bmv0.normal),
+                            self.geometry_correction(bmv1.normal),
+                            self.geometry_correction(bmv2.normal),
+                            )
+                    ms3d_triangle._s = (
+                            bmf.loops[0][layer_uv].uv.x,
+                            bmf.loops[1][layer_uv].uv.x,
+                            bmf.loops[2][layer_uv].uv.x,
+                            )
+                    ms3d_triangle._t = (
+                            1.0 - bmf.loops[0][layer_uv].uv.y,
+                            1.0 - bmf.loops[1][layer_uv].uv.y,
+                            1.0 - bmf.loops[2][layer_uv].uv.y,
+                            )
+
+                    ms3d_triangle.smoothing_group = bmf[layer_smoothing_group]
+                    ms3d_model._triangles.append(ms3d_triangle)
+
+                    ms3d_material = self.get_ms3d_material_add_if(
+                            blender_mesh, ms3d_model,
+                            blender_to_ms3d_materials, bmf.material_index)
+                    ms3d_group = blender_to_ms3d_groups.get(bmf[layer_group])
+
+                    ##EXPORT_ACTIVE_ONLY:
+                    if ms3d_group is not None:
+                        if ms3d_material is None:
+                            ms3d_group.material_index = \
+                                    Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX
+                        else:
+                            if ms3d_group.material_index is None:
+                                ms3d_group.material_index = \
+                                        ms3d_material.__index
+                            else:
+                                if ms3d_group.material_index != \
+                                        ms3d_material.__index:
+                                    ms3d_group = \
+                                            self.get_ms3d_group_by_material_add_if(
+                                            ms3d_model, ms3d_material)
+                    else:
+                        if ms3d_material is not None:
+                            ms3d_group = self.get_ms3d_group_by_material_add_if(
+                                    ms3d_model, ms3d_material)
+                        else:
+                            ms3d_group = self.get_ms3d_group_default_material_add_if(
+                                    ms3d_model)
+
+                    if ms3d_group is not None:
+                        ms3d_group._triangle_indices.append(
+                                ms3d_triangle.__index)
+                        ms3d_triangle.group_index = ms3d_group.__index
+
+                    blender_to_ms3d_triangles[bmf] = ms3d_triangle
+
+            if bm is not None:
+                bm.free()
+
+            enable_edit_mode(False, blender_context)
+
+            ##########################
+            # remove the temporary data
+            blender_scene.objects.unlink(blender_mesh_object_temp)
+            if blender_mesh_temp is not None:
+                blender_mesh_temp.user_clear()
+                blender_context.blend_data.meshes.remove(blender_mesh_temp)
+            blender_mesh_temp = None
+            if blender_mesh_object_temp is not None:
+                blender_mesh_temp = blender_mesh_object_temp.data.user_clear()
+                blender_mesh_object_temp.user_clear()
+                blender_context.blend_data.objects.remove(
+                        blender_mesh_object_temp)
+            if blender_mesh_temp is not None:
+                blender_mesh_temp.user_clear()
+                blender_context.blend_data.meshes.remove(blender_mesh_temp)
+
+
+    ###########################################################################
+    def create_animation(self, blender_context, ms3d_model,
+            blender_mesh_objects, blender_to_ms3d_bones):
+        ##########################
+        # setup scene
+        blender_scene = blender_context.scene
+
+        if not self.options_use_animation:
+            ms3d_model.animation_fps = 24
+            ms3d_model.number_total_frames = 1
+            ms3d_model.current_time = 0
+            return
+
+        frame_start = blender_scene.frame_start
+        frame_end = blender_scene.frame_end
+        frame_total = (frame_end - frame_start) + 1
+        frame_step = blender_scene.frame_step
+        frame_offset = 0
+
+        fps = blender_scene.render.fps * blender_scene.render.fps_base
+        time_base = 1.0 / fps
+
+        base_bone_correction = Matrix.Rotation(pi / 2, 4, 'Z')
+
+        for blender_mesh_object in blender_mesh_objects:
+            blender_bones = None
+            blender_action = None
+            blender_nla_tracks = None
+            for blender_modifier in blender_mesh_object.modifiers:
+                if blender_modifier.type == 'ARMATURE' \
+                        and blender_modifier.object.pose:
+                    blender_bones = blender_modifier.object.data.bones
+                    blender_pose_bones = blender_modifier.object.pose.bones
+                    if blender_modifier.object.animation_data:
+                        blender_action = \
+                                blender_modifier.object.animation_data.action
+                        blender_nla_tracks = \
+                                blender_modifier.object.animation_data.nla_tracks
+                    break
+
+            if blender_bones is None \
+                    and (blender_action is None and blender_nla_tracks is None):
+                continue
+
+            ##########################
+            # bones
+            blender_bones_ordered = []
+            self.build_blender_bone_dependency_order(
+                    blender_bones, blender_bones_ordered)
+            for blender_bone_name in blender_bones_ordered:
+                blender_bone_oject = blender_bones[blender_bone_name]
+                ms3d_joint = Ms3dJoint()
+                ms3d_joint.__index = len(ms3d_model._joints)
+
+                blender_bone_ms3d = blender_bone_oject.ms3d
+                blender_bone = blender_bone_oject
+
+                ms3d_joint.flags = Ms3dUi.flags_to_ms3d(blender_bone_ms3d.flags)
+                if blender_bone_ms3d.comment:
+                    ms3d_joint._comment_object = Ms3dCommentEx()
+                    ms3d_joint._comment_object.comment = \
+                            blender_bone_ms3d.comment
+                    ms3d_joint._comment_object.index = ms3d_joint.__index
+
+                ms3d_joint.joint_ex_object._color = blender_bone_ms3d.color[:]
+
+                ms3d_joint.name = blender_bone.name
+
+                if blender_bone.parent:
+                    ms3d_joint.parent_name = blender_bone.parent.name
+                    ms3d_joint.__matrix = matrix_difference(
+                            blender_bone.matrix_local,
+                            blender_bone.parent.matrix_local)
+                else:
+                    ms3d_joint.__matrix = base_bone_correction \
+                            * blender_bone.matrix_local
+
+                mat = ms3d_joint.__matrix
+                loc = mat.to_translation()
+                rot = mat.to_euler('XZY')
+                ms3d_joint._position = self.joint_correction(loc)
+                ms3d_joint._rotation = self.joint_correction(rot)
+
+                ms3d_model._joints.append(ms3d_joint)
+                blender_to_ms3d_bones[blender_bone.name] = ms3d_joint
+
+            ##########################
+            # animation
+            frames = None
+            frames_location = set()
+            frames_rotation = set()
+            frames_scale = set()
+
+            if blender_action:
+                self.fill_keyframe_sets(
+                        blender_action.fcurves,
+                        frames_location, frames_rotation, frames_scale,
+                        0)
+
+            if blender_nla_tracks:
+                for nla_track in blender_nla_tracks:
+                    if nla_track.mute:
+                        continue
+                    for strip in nla_track.strips:
+                        if strip.mute:
+                            continue
+                        frame_correction = strip.frame_start \
+                                - strip.action_frame_start
+                        self.fill_keyframe_sets(
+                                strip.action.fcurves,
+                                frames_location, frames_rotation, frames_scale,
+                                frame_correction)
+
+            frames = set(frames_location)
+            frames = frames.union(frames_rotation)
+            frames = frames.union(frames_scale)
+
+            if not self.options_shrink_to_keys:
+                frames = frames.intersection(range(
+                        blender_scene.frame_start, blender_scene.frame_end + 1))
+
+            frames_sorted = list(frames)
+            frames_sorted.sort()
+
+            if self.options_shrink_to_keys and len(frames_sorted) >= 2:
+                frame_start = frames_sorted[0]
+                frame_end = frames_sorted[len(frames_sorted)-1]
+                frame_total = (frame_end - frame_start) + 1
+                frame_offset = frame_start - 1
+
+            if self.options_bake_each_frame:
+                frames_sorted = range(int(frame_start), int(frame_end + 1),
+                        int(frame_step))
+
+            frame_temp = blender_scene.frame_current
+
+            for current_frame in frames_sorted:
+                blender_scene.frame_set(current_frame)
+
+                current_time = (current_frame - frame_offset) * time_base
+                for blender_bone_name in blender_bones_ordered:
+                    blender_bone = blender_bones[blender_bone_name]
+                    blender_pose_bone = blender_pose_bones[blender_bone_name]
+                    ms3d_joint = blender_to_ms3d_bones[blender_bone_name]
+
+                    m1 = blender_bone.matrix_local.inverted()
+                    if blender_pose_bone.parent:
+                        m2 = blender_pose_bone.parent.matrix_channel.inverted()
+                    else:
+                        m2 = Matrix()
+                    m3 = blender_pose_bone.matrix.copy()
+                    m = ((m1 * m2) * m3)
+                    loc = m.to_translation()
+                    rot = m.to_euler('XZY')
+
+                    ms3d_joint.translation_key_frames.append(
+                            Ms3dTranslationKeyframe(
+                                    current_time, self.joint_correction(loc)
+                                    )
+                            )
+                    ms3d_joint.rotation_key_frames.append(
+                            Ms3dRotationKeyframe(
+                                    current_time, self.joint_correction(rot)
+                                    )
+                            )
+
+            blender_scene.frame_set(frame_temp)
+
+        ms3d_model.animation_fps = fps
+        if ms3d_model.number_joints > 0:
+            ms3d_model.number_total_frames = int(frame_total)
+            ms3d_model.current_time = ((blender_scene.frame_current \
+                    - blender_scene.frame_start) + 1) * time_base
+        else:
+            ms3d_model.number_total_frames = 1
+            ms3d_model.current_time = 0
+
+
+    ###########################################################################
+    def get_ms3d_group_default_material_add_if(self, ms3d_model):
+        markerName = "MaterialGroupDefault"
+        markerComment = "group without material"
+
+        for ms3d_group in ms3d_model._groups:
+            if ms3d_group.material_index == \
+                    Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX \
+                    and ms3d_group.name == markerName \
+                    and ms3d_group._comment_object \
+                    and ms3d_group._comment_object.comment == markerComment:
+                return ms3d_group
+
+        ms3d_group = Ms3dGroup()
+        ms3d_group.__index = len(ms3d_model._groups)
+        ms3d_group.name = markerName
+        ms3d_group._comment_object = Ms3dCommentEx()
+        ms3d_group._comment_object.comment = markerComment
+        ms3d_group._comment_object.index = len(ms3d_model._groups)
+        ms3d_group.material_index = Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX
+
+        ms3d_model._groups.append(ms3d_group)
+
+        return ms3d_group
+
+
+    ###########################################################################
+    def get_ms3d_group_by_material_add_if(self, ms3d_model, ms3d_material):
+        if ms3d_material.__index < 0 \
+                or ms3d_material.__index >= len(ms3d_model.materials):
+            return None
+
+        markerName = "MaterialGroup.{}".format(ms3d_material.__index)
+        markerComment = "MaterialGroup({})".format(ms3d_material.name)
+
+        for ms3d_group in ms3d_model._groups:
+            if ms3d_group.name == markerName \
+                    and ms3d_group._comment_object \
+                    and ms3d_group._comment_object.comment == markerComment:
+                return ms3d_group
+
+        ms3d_group = Ms3dGroup()
+        ms3d_group.__index = len(ms3d_model._groups)
+        ms3d_group.name = markerName
+        ms3d_group._comment_object = Ms3dCommentEx()
+        ms3d_group._comment_object.comment = markerComment
+        ms3d_group._comment_object.index = len(ms3d_model._groups)
+        ms3d_group.material_index = ms3d_material.__index
+
+        ms3d_model._groups.append(ms3d_group)
+
+        return ms3d_group
+
+
+    ###########################################################################
+    def get_ms3d_material_add_if(self, blender_mesh, ms3d_model,
+            blender_to_ms3d_materials, blender_index):
+        if blender_index < 0 or blender_index >= len(blender_mesh.materials):
+            return None
+
+        blender_material = blender_mesh.materials[blender_index]
+        ms3d_material = blender_to_ms3d_materials.get(blender_material)
+        if ms3d_material is None:
+            ms3d_material = Ms3dMaterial()
+            ms3d_material.__index = len(ms3d_model.materials)
+
+            blender_ms3d_material = blender_material.ms3d
+
+            if not self.options_use_blender_names \
+                    and not self.options_use_blender_materials \
+                    and blender_ms3d_material.name:
+                ms3d_material.name = blender_ms3d_material.name
+            else:
+                ms3d_material.name = blender_material.name
+
+            temp_material = None
+            if self.options_use_blender_materials:
+                temp_material = Ms3dMaterial()
+                Ms3dMaterialHelper.copy_from_blender(
+                        None, None, temp_material, blender_material)
+            else:
+                temp_material = blender_ms3d_material
+
+            ms3d_material._ambient = temp_material.ambient[:]
+            ms3d_material._diffuse = temp_material.diffuse[:]
+            ms3d_material._specular = temp_material.specular[:]
+            ms3d_material._emissive = temp_material.emissive[:]
+            ms3d_material.shininess = temp_material.shininess
+            ms3d_material.transparency = temp_material.transparency
+            ms3d_material.texture = temp_material.texture
+            ms3d_material.alphamap = temp_material.alphamap
+
+            ms3d_material.mode = Ms3dUi.texture_mode_to_ms3d(
+                    blender_ms3d_material.mode)
+            if blender_ms3d_material.comment:
+                ms3d_material._comment_object = Ms3dCommentEx()
+                ms3d_material._comment_object.comment = \
+                        blender_ms3d_material.comment
+                ms3d_material._comment_object.index = ms3d_material.__index
+
+            ms3d_model.materials.append(ms3d_material)
+
+            blender_to_ms3d_materials[blender_material] = ms3d_material
+
+        return ms3d_material
+
+
+    ###########################################################################
+    def geometry_correction(self, value):
+        return (value[1], value[2], value[0])
+
+
+    ###########################################################################
+    def joint_correction(self, value):
+        return (-value[0], value[2], value[1])
+
+
+    ###########################################################################
+    def build_blender_bone_dependency_order(self, blender_bones,
+            blender_bones_ordered):
+        if not blender_bones:
+            return blender_bones_ordered
+
+        blender_bones_children = {None: []}
+        for blender_bone in blender_bones:
+            if blender_bone.parent:
+                blender_bone_children = blender_bones_children.get(
+                        blender_bone.parent.name)
+                if blender_bone_children is None:
+                    blender_bone_children = blender_bones_children[
+                            blender_bone.parent.name] = []
+            else:
+                blender_bone_children = blender_bones_children[None]
+
+            blender_bone_children.append(blender_bone.name)
+
+        self.traverse_dependencies(
+                blender_bones_ordered,
+                blender_bones_children,
+                None)
+
+        return blender_bones_ordered
+
+
+    ###########################################################################
+    def traverse_dependencies(self, blender_bones_ordered,
+            blender_bones_children, key):
+        blender_bone_children = blender_bones_children.get(key)
+        if blender_bone_children:
+            for blender_bone_name in blender_bone_children:
+                blender_bones_ordered.append(blender_bone_name)
+                self.traverse_dependencies(
+                        blender_bones_ordered,
+                        blender_bones_children,
+                        blender_bone_name)
+
+    ###########################################################################
+    def fill_keyframe_sets(self,
+            fcurves,
+            frames_location, frames_rotation, frames_scale,
+            frame_correction):
+        for fcurve in fcurves:
+            if fcurve.data_path.endswith(".location"):
+                frames = frames_location
+            elif fcurve.data_path.endswith(".rotation_euler"):
+                frames = frames_rotation
+            elif fcurve.data_path.endswith(".rotation_quaternion"):
+                frames = frames_rotation
+            elif fcurve.data_path.endswith(".scale"):
+                frames = frames_scale
+            else:
+                pass
+
+            for keyframe_point in fcurve.keyframe_points:
+                frames.add(int(keyframe_point.co[0] + frame_correction))
+
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####
diff --git a/io_scene_ms3d/ms3d_import.py b/io_scene_ms3d/ms3d_import.py
new file mode 100644
index 0000000000000000000000000000000000000000..f4767f1193183a7aa9cbf78d070e5dd57a57a103
--- /dev/null
+++ b/io_scene_ms3d/ms3d_import.py
@@ -0,0 +1,1005 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+
+
+#import python stuff
+import io
+from mathutils import (
+        Vector,
+        Matrix,
+        )
+from os import (
+        path,
+        )
+from sys import (
+        exc_info,
+        )
+from time import (
+        time,
+        )
+
+
+# import io_scene_ms3d stuff
+from io_scene_ms3d.ms3d_strings import (
+        ms3d_str,
+        )
+from io_scene_ms3d.ms3d_spec import (
+        Ms3dSpec,
+        Ms3dModel,
+        Ms3dVertexEx2,
+        Ms3dVertexEx3,
+        )
+from io_scene_ms3d.ms3d_utils import (
+        select_all,
+        enable_pose_mode,
+        enable_edit_mode,
+        pre_setup_environment,
+        post_setup_environment,
+        get_edge_split_modifier_add_if,
+        )
+from io_scene_ms3d.ms3d_ui import (
+        Ms3dUi,
+        )
+
+
+#import blender stuff
+from bpy import (
+        ops,
+        )
+import bmesh
+from bpy_extras.image_utils import (
+        load_image,
+        )
+
+
+###############################################################################
+class Ms3dImporter():
+    """
+    Load a MilkShape3D MS3D File
+    """
+    def __init__(self,
+            report,
+            verbose=False,
+            use_extended_normal_handling=False,
+            use_animation=True,
+            use_quaternion_rotation=False,
+            use_joint_size=False,
+            joint_size=1.0,
+            use_joint_to_bones=False,
+            ):
+        self.report = report
+        self.options_verbose = verbose
+        self.options_use_extended_normal_handling = use_extended_normal_handling
+        self.options_use_animation = use_animation
+        self.options_use_quaternion_rotation = use_quaternion_rotation
+        self.options_use_joint_size = use_joint_size
+        self.options_joint_size = joint_size
+        self.options_use_joint_to_bones = use_joint_to_bones
+        pass
+
+    ###########################################################################
+    # create empty blender ms3d_model
+    # read ms3d file
+    # fill blender with ms3d_model content
+    """ read ms3d file and convert ms3d content to bender content """
+    def read(self, blender_context, filepath):
+
+        t1 = time()
+        t2 = None
+        self.has_textures = False
+
+        try:
+            # setup environment
+            pre_setup_environment(self, blender_context)
+
+            # inject splitted filepath
+            self.directory_name, self.file_name = path.split(filepath)
+
+            # create an empty ms3d template
+            ms3d_model = Ms3dModel(self.file_name)
+
+            try:
+                # open ms3d file
+                with io.FileIO(filepath, 'rb') as raw_io:
+                    # read and inject ms3d data from disk to internal structure
+                    ms3d_model.read(raw_io)
+                    raw_io.close()
+            finally:
+                pass
+
+            # if option is set, this time will enlargs the io time
+            if self.options_verbose:
+                ms3d_model.print_internal()
+
+            t2 = time()
+
+            is_valid, statistics = ms3d_model.is_valid()
+
+            if is_valid:
+                # inject ms3d data to blender
+                self.to_blender(blender_context, ms3d_model)
+
+                blender_scene = blender_context.scene
+
+                # finalize/restore environment
+                blender_scene.update()
+
+                post_setup_environment(self, blender_context)
+
+            print()
+            print("##########################################################")
+            print("Import from MS3D to Blender")
+            print(statistics)
+            print("##########################################################")
+
+        except Exception:
+            type, value, traceback = exc_info()
+            print("read - exception in try block\n  type: '{0}'\n"
+                    "  value: '{1}'".format(type, value, traceback))
+
+            if t2 is None:
+                t2 = time()
+
+            raise
+
+        else:
+            pass
+
+        t3 = time()
+        print(ms3d_str['SUMMARY_IMPORT'].format(
+                (t3 - t1), (t2 - t1), (t3 - t2)))
+
+        return {"FINISHED"}
+
+
+    def internal_read(self, blender_context, raw_io):
+        try:
+            # setup environment
+            pre_setup_environment(self, blender_context)
+
+            try:
+                ms3d_model.read(raw_io)
+            finally:
+                pass
+
+            # if option is set, this time will enlargs the io time
+            if self.options_verbose:
+                ms3d_model.print_internal()
+
+            is_valid, statistics = ms3d_model.is_valid()
+
+            if is_valid:
+                # inject ms3d data to blender
+                blender_empty_object, blender_mesh_object = self.to_blender(blender_context, ms3d_model)
+
+                blender_scene = blender_context.scene
+
+                # finalize/restore environment
+                blender_scene.update()
+
+                post_setup_environment(self, blender_context)
+
+        except Exception:
+            type, value, traceback = exc_info()
+            print("read - exception in try block\n  type: '{0}'\n"
+                    "  value: '{1}'".format(type, value, traceback))
+
+            raise
+
+        else:
+            pass
+
+        return blender_empty_object, blender_mesh_object
+
+
+    ###########################################################################
+    def to_blender(self, blender_context, ms3d_model):
+        blender_mesh_object = self.create_geometry(blender_context, ms3d_model)
+        blender_armature_object = self.create_animation(
+                blender_context, ms3d_model, blender_mesh_object)
+
+        blender_empty_object = self.organize_objects(
+                blender_context, ms3d_model,
+                [blender_mesh_object, blender_armature_object])
+
+        return blender_empty_object, blender_mesh_object
+
+
+    ###########################################################################
+    def organize_objects(self, blender_context, ms3d_model, blender_objects):
+        ##########################
+        # blender_armature_object to blender_mesh_object
+        # that has bad side effects to the armature
+        # and causes cyclic dependecies
+        ###blender_armature_object.parent = blender_mesh_object
+        ###blender_mesh_object.parent = blender_armature_object
+
+        blender_scene = blender_context.scene
+
+        blender_group = blender_context.blend_data.groups.new(
+                "{}.g".format(ms3d_model.name))
+        blender_empty_object = blender_context.blend_data.objects.new(
+                "{}.e".format(ms3d_model.name), None)
+        blender_empty_object.location = blender_scene.cursor_location
+        blender_scene.objects.link(blender_empty_object)
+        blender_group.objects.link(blender_empty_object)
+
+        for blender_object in blender_objects:
+            if blender_object is not None:
+                blender_group.objects.link(blender_object)
+                blender_object.parent = blender_empty_object
+
+        return blender_empty_object
+
+
+    ###########################################################################
+    def create_geometry(self, blender_context, ms3d_model):
+        ##########################
+        # blender stuff:
+        # create a blender Mesh
+        blender_mesh = blender_context.blend_data.meshes.new(
+                "{}.m".format(ms3d_model.name))
+        blender_mesh.ms3d.name = ms3d_model.name
+
+        ms3d_comment = ms3d_model.comment_object
+        if ms3d_comment is not None:
+            blender_mesh.ms3d.comment = ms3d_comment.comment
+        ms3d_model_ex = ms3d_model.model_ex_object
+        if ms3d_model_ex is not None:
+            blender_mesh.ms3d.joint_size = ms3d_model_ex.joint_size
+            blender_mesh.ms3d.alpha_ref = ms3d_model_ex.alpha_ref
+            blender_mesh.ms3d.transparency_mode \
+                    = Ms3dUi.transparency_mode_from_ms3d(
+                            ms3d_model_ex.transparency_mode)
+
+        ##########################
+        # blender stuff:
+        # link to blender object
+        blender_mesh_object = blender_context.blend_data.objects.new(
+                "{}.m".format(ms3d_model.name), blender_mesh)
+
+        ##########################
+        # blender stuff:
+        # create edge split modifire, to make sharp edges visible
+        blender_modifier = get_edge_split_modifier_add_if(blender_mesh_object)
+
+        ##########################
+        # blender stuff:
+        # link to blender scene
+        blender_scene = blender_context.scene
+        blender_scene.objects.link(blender_mesh_object)
+        #blender_mesh_object.location = blender_scene.cursor_location
+        enable_edit_mode(False, blender_context)
+        select_all(False)
+        blender_mesh_object.select = True
+        blender_scene.objects.active = blender_mesh_object
+
+        ##########################
+        # take this as active object after import
+        self.active_object = blender_mesh_object
+
+        ##########################
+        # blender stuff:
+        # create all (ms3d) groups
+        ms3d_to_blender_group_index = {}
+        blender_group_manager = blender_mesh.ms3d
+        for ms3d_group_index, ms3d_group in enumerate(ms3d_model.groups):
+            blender_group = blender_group_manager.create_group()
+            blender_group.name = ms3d_group.name
+            blender_group.flags = Ms3dUi.flags_from_ms3d(ms3d_group.flags)
+            blender_group.material_index = ms3d_group.material_index
+
+            ms3d_comment = ms3d_group.comment_object
+            if ms3d_comment is not None:
+                blender_group.comment = ms3d_comment.comment
+
+            # translation dictionary
+            ms3d_to_blender_group_index[ms3d_group_index] = blender_group.id
+
+        ####################################################
+        # begin BMesh stuff
+        #
+
+        ##########################
+        # BMesh stuff:
+        # create an empty BMesh
+        bm = bmesh.new()
+
+        ##########################
+        # BMesh stuff:
+        # create new Layers for custom data per "mesh face"
+        layer_texture = bm.faces.layers.tex.get(
+                ms3d_str['OBJECT_LAYER_TEXTURE'])
+        if layer_texture is None:
+            layer_texture = bm.faces.layers.tex.new(
+                    ms3d_str['OBJECT_LAYER_TEXTURE'])
+
+        layer_smoothing_group = bm.faces.layers.int.get(
+                ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+        if layer_smoothing_group is None:
+            layer_smoothing_group = bm.faces.layers.int.new(
+                    ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+
+        layer_group = bm.faces.layers.int.get(
+                ms3d_str['OBJECT_LAYER_GROUP'])
+        if layer_group is None:
+            layer_group = bm.faces.layers.int.new(
+                    ms3d_str['OBJECT_LAYER_GROUP'])
+
+        ##########################
+        # BMesh stuff:
+        # create new Layers for custom data per "face vertex"
+        layer_uv = bm.loops.layers.uv.get(ms3d_str['OBJECT_LAYER_UV'])
+        if layer_uv is None:
+            layer_uv = bm.loops.layers.uv.new(ms3d_str['OBJECT_LAYER_UV'])
+
+        ##########################
+        # BMesh stuff:
+        # create new Layers for custom data per "vertex"
+        layer_extra = bm.verts.layers.int.get(ms3d_str['OBJECT_LAYER_EXTRA'])
+        if layer_extra is None:
+            layer_extra = bm.verts.layers.int.new(ms3d_str['OBJECT_LAYER_EXTRA'])
+
+        ##########################
+        # BMesh stuff:
+        # create all vertices
+        for ms3d_vertex_index, ms3d_vertex in enumerate(ms3d_model.vertices):
+            bmv = bm.verts.new(self.geometry_correction(ms3d_vertex.vertex))
+
+            if layer_extra and ms3d_vertex.vertex_ex_object and \
+                    (isinstance(ms3d_vertex.vertex_ex_object, Ms3dVertexEx2) \
+                    or isinstance(ms3d_vertex.vertex_ex_object, Ms3dVertexEx3)):
+
+                #bmv[layer_extra] = ms3d_vertex.vertex_ex_object.extra
+                # bm.verts.layers.int does only support signed int32
+                # convert unsigned int32 to signed int32 (little-endian)
+                unsigned_int32 = ms3d_vertex.vertex_ex_object.extra
+                bytes_int32 = unsigned_int32.to_bytes(
+                        4, byteorder='little', signed=False)
+                signed_int32 = int.from_bytes(
+                        bytes_int32, byteorder='little', signed=True)
+                bmv[layer_extra] = signed_int32
+
+        ##########################
+        # blender stuff (uses BMesh stuff):
+        # create all materials / image textures
+        ms3d_to_blender_material = {}
+        for ms3d_material_index, ms3d_material in enumerate(
+                ms3d_model.materials):
+            blender_material = blender_context.blend_data.materials.new(
+                    ms3d_material.name)
+
+            # custom datas
+            blender_material.ms3d.name = ms3d_material.name
+            blender_material.ms3d.ambient = ms3d_material.ambient
+            blender_material.ms3d.diffuse = ms3d_material.diffuse
+            blender_material.ms3d.specular = ms3d_material.specular
+            blender_material.ms3d.emissive = ms3d_material.emissive
+            blender_material.ms3d.shininess = ms3d_material.shininess
+            blender_material.ms3d.transparency = ms3d_material.transparency
+            blender_material.ms3d.mode = Ms3dUi.texture_mode_from_ms3d(
+                    ms3d_material.mode)
+
+            if ms3d_material.texture:
+                blender_material.ms3d.texture = ms3d_material.texture
+
+            if ms3d_material.alphamap:
+                blender_material.ms3d.alphamap = ms3d_material.alphamap
+
+            ms3d_comment = ms3d_material.comment_object
+            if ms3d_comment is not None:
+                blender_material.ms3d.comment = ms3d_comment.comment
+
+            # blender datas
+            blender_material.ambient = (
+                    (ms3d_material.ambient[0]
+                    + ms3d_material.ambient[1]
+                    + ms3d_material.ambient[2]) / 3.0)
+
+            blender_material.diffuse_color[0] = ms3d_material.diffuse[0]
+            blender_material.diffuse_color[1] = ms3d_material.diffuse[1]
+            blender_material.diffuse_color[2] = ms3d_material.diffuse[2]
+
+            blender_material.specular_color[0] = ms3d_material.specular[0]
+            blender_material.specular_color[1] = ms3d_material.specular[1]
+            blender_material.specular_color[2] = ms3d_material.specular[2]
+
+            blender_material.emit = (
+                    (ms3d_material.emissive[0]
+                    + ms3d_material.emissive[1]
+                    + ms3d_material.emissive[2]) / 3.0)
+
+            blender_material.specular_hardness = ms3d_material.shininess * 4.0
+            blender_material.alpha = 1.0 - ms3d_material.transparency
+
+            # diffuse texture
+            if ms3d_material.texture:
+                dir_name_diffuse = self.directory_name
+                file_name_diffuse = path.split(ms3d_material.texture)[1]
+                blender_image_diffuse = load_image(
+                        file_name_diffuse, dir_name_diffuse)
+                blender_texture_diffuse = \
+                        blender_context.blend_data.textures.new(
+                        name=file_name_diffuse, type='IMAGE')
+                blender_texture_diffuse.image = blender_image_diffuse
+                blender_texture_slot_diffuse \
+                        = blender_material.texture_slots.add()
+                blender_texture_slot_diffuse.texture = blender_texture_diffuse
+                blender_texture_slot_diffuse.texture_coords = 'UV'
+                blender_texture_slot_diffuse.uv_layer = layer_uv.name
+                blender_texture_slot_diffuse.use_map_color_diffuse = True
+                blender_texture_slot_diffuse.use_map_alpha = False
+                if blender_image_diffuse is not None:
+                    self.has_textures = True
+            else:
+                blender_image_diffuse = None
+
+            # alpha texture
+            if ms3d_material.alphamap:
+                dir_name_alpha = self.directory_name
+                file_name_alpha = path.split(ms3d_material.alphamap)[1]
+                blender_image_alpha = load_image(
+                        file_name_alpha, dir_name_alpha)
+                blender_texture_alpha = blender_context.blend_data.textures.new(
+                        name=file_name_alpha, type='IMAGE')
+                blender_texture_alpha.image = blender_image_alpha
+                blender_texture_slot_alpha \
+                        = blender_material.texture_slots.add()
+                blender_texture_slot_alpha.texture = blender_texture_alpha
+                blender_texture_slot_alpha.texture_coords = 'UV'
+                blender_texture_slot_alpha.uv_layer = layer_uv.name
+                blender_texture_slot_alpha.use_map_color_diffuse = False
+                blender_texture_slot_alpha.use_map_alpha = True
+                blender_texture_slot_alpha.use_rgb_to_intensity = True
+                blender_material.alpha = 0
+                blender_material.specular_alpha = 0
+
+            # append blender material to blender mesh, to be linked to
+            blender_mesh.materials.append(blender_material)
+
+            # translation dictionary
+            ms3d_to_blender_material[ms3d_material_index] \
+                    = blender_image_diffuse
+
+        ##########################
+        # BMesh stuff:
+        # create all triangles
+        length_verts = len(bm.verts)
+        vertex_extra_index = length_verts
+        blender_invalide_normal = Vector()
+        smoothing_group_blender_faces = {}
+        for ms3d_triangle_index, ms3d_triangle in enumerate(
+                ms3d_model.triangles):
+            bmv_list = []
+            bmf_normal = Vector()
+
+            for index, vert_index in enumerate(ms3d_triangle.vertex_indices):
+                if vert_index < 0 or vert_index >= length_verts:
+                    continue
+                bmv = bm.verts[vert_index]
+
+                blender_normal = self.geometry_correction(
+                        ms3d_triangle.vertex_normals[index])
+                if bmv.normal == blender_invalide_normal:
+                    bmv.normal = blender_normal
+                elif bmv.normal != blender_normal \
+                        and self.options_use_extended_normal_handling:
+                    ## search for an already created extra vertex
+                    bmv_new = None
+                    for vert_index_candidat in range(
+                            vertex_extra_index, length_verts):
+                        bmv_candidat = bm.verts[vert_index_candidat]
+                        if bmv_candidat.co == bmv.co \
+                                and bmv_candidat.normal == blender_normal:
+                            bmv_new = bmv_candidat
+                            vert_index = vert_index_candidat
+                            break
+
+                    ## if not exists, create one in blender and ms3d as well
+                    if bmv_new is None:
+                        ms3d_model.vertices.append(
+                                ms3d_model.vertices[vert_index])
+                        bmv_new = bm.verts.new(bmv.co)
+                        bmv_new.normal = blender_normal
+                        bmv_new[layer_extra] = bmv[layer_extra]
+                        vert_index = length_verts
+                        length_verts += 1
+                        self.report({'WARNING', 'INFO'},
+                                ms3d_str['WARNING_IMPORT_EXTRA_VERTEX_NORMAL'].format(
+                                bmv.normal, blender_normal))
+                    bmv = bmv_new
+
+                if [[x] for x in bmv_list if x == bmv]:
+                    self.report(
+                            {'WARNING', 'INFO'},
+                            ms3d_str['WARNING_IMPORT_SKIP_VERTEX_DOUBLE'].format(
+                                    ms3d_triangle_index))
+                    continue
+                bmv_list.append(bmv)
+                bmf_normal += bmv.normal
+
+            if len(bmv_list) < 3:
+                self.report(
+                        {'WARNING', 'INFO'},
+                        ms3d_str['WARNING_IMPORT_SKIP_LESS_VERTICES'].format(
+                                ms3d_triangle_index))
+                continue
+
+            bmf_normal.normalize()
+
+            bmf = bm.faces.get(bmv_list)
+            if bmf is not None:
+                self.report(
+                        {'WARNING', 'INFO'},
+                        ms3d_str['WARNING_IMPORT_SKIP_FACE_DOUBLE'].format(
+                                ms3d_triangle_index))
+                continue
+
+            bmf = bm.faces.new(bmv_list)
+            bmf.normal = bmf_normal
+
+            # blender uv custom data per "face vertex"
+            bmf.loops[0][layer_uv].uv = Vector(
+                    (ms3d_triangle.s[0], 1.0 - ms3d_triangle.t[0]))
+            bmf.loops[1][layer_uv].uv = Vector(
+                    (ms3d_triangle.s[1], 1.0 - ms3d_triangle.t[1]))
+            bmf.loops[2][layer_uv].uv = Vector(
+                    (ms3d_triangle.s[2], 1.0 - ms3d_triangle.t[2]))
+
+            # ms3d custom data per "mesh face"
+            bmf[layer_smoothing_group] = ms3d_triangle.smoothing_group
+
+            blender_group_id = ms3d_to_blender_group_index.get(
+                    ms3d_triangle.group_index)
+            if blender_group_id is not None:
+                bmf[layer_group] = blender_group_id
+
+            if ms3d_triangle.group_index >= 0 \
+                    and ms3d_triangle.group_index < len(ms3d_model.groups):
+                ms3d_material_index \
+                        = ms3d_model.groups[ms3d_triangle.group_index].material_index
+                if ms3d_material_index != Ms3dSpec.NONE_GROUP_MATERIAL_INDEX:
+                    bmf.material_index = ms3d_material_index
+                    # apply diffuse texture image to face, to be visible in 3d view
+                    bmf[layer_texture].image = ms3d_to_blender_material.get(
+                            ms3d_material_index)
+
+            # helper dictionary for post-processing smoothing_groups
+            smoothing_group_blender_face = smoothing_group_blender_faces.get(
+                    ms3d_triangle.smoothing_group)
+            if smoothing_group_blender_face is None:
+                smoothing_group_blender_face = []
+                smoothing_group_blender_faces[ms3d_triangle.smoothing_group] \
+                        = smoothing_group_blender_face
+            smoothing_group_blender_face.append(bmf)
+
+        ##########################
+        # BMesh stuff:
+        # create all sharp edges for blender to make smoothing_groups visible
+        for ms3d_smoothing_group_index, blender_face_list \
+                in smoothing_group_blender_faces.items():
+            edge_dict = {}
+            for bmf in blender_face_list:
+                bmf.smooth = True
+                for bme in bmf.edges:
+                    if edge_dict.get(bme) is None:
+                        edge_dict[bme] = 0
+                    else:
+                        edge_dict[bme] += 1
+                    bme.seam = (edge_dict[bme] == 0)
+                    bme.smooth = (edge_dict[bme] != 0)
+
+        ##########################
+        # BMesh stuff:
+        # finally tranfer BMesh to Mesh
+        bm.to_mesh(blender_mesh)
+        bm.free()
+
+
+        #
+        # end BMesh stuff
+        ####################################################
+
+        blender_mesh.validate(self.options_verbose)
+
+        return blender_mesh_object
+
+
+    ###########################################################################
+    def create_animation(self, blender_context, ms3d_model, blender_mesh_object):
+        ##########################
+        # setup scene
+        blender_scene = blender_context.scene
+        blender_scene.render.fps = ms3d_model.animation_fps
+        if ms3d_model.animation_fps:
+            blender_scene.render.fps_base = (blender_scene.render.fps /
+                    ms3d_model.animation_fps)
+
+        blender_scene.frame_start = 1
+        blender_scene.frame_end = (ms3d_model.number_total_frames
+                + blender_scene.frame_start) - 1
+        blender_scene.frame_current = (ms3d_model.current_time
+                * ms3d_model.animation_fps)
+
+        ##########################
+        if not ms3d_model.joints:
+            return
+
+        ##########################
+        ms3d_armature_name = "{}.a".format(ms3d_model.name)
+        ms3d_action_name = "{}.act".format(ms3d_model.name)
+
+        ##########################
+        # create new blender_armature_object
+        blender_armature = blender_context.blend_data.armatures.new(
+                ms3d_armature_name)
+        blender_armature.ms3d.name = ms3d_model.name
+        blender_armature.draw_type = 'STICK'
+        blender_armature.show_axes = True
+        blender_armature.use_auto_ik = True
+        blender_armature_object = blender_context.blend_data.objects.new(
+                ms3d_armature_name, blender_armature)
+        blender_scene.objects.link(blender_armature_object)
+        #blender_armature_object.location = blender_scene.cursor_location
+        blender_armature_object.show_x_ray = True
+
+        ##########################
+        # create new modifier
+        blender_modifier = blender_mesh_object.modifiers.new(
+                ms3d_armature_name, type='ARMATURE')
+        blender_modifier.show_expanded = False
+        blender_modifier.use_vertex_groups = True
+        blender_modifier.use_bone_envelopes = False
+        blender_modifier.object = blender_armature_object
+
+        ##########################
+        # prepare for vertex groups
+        ms3d_to_blender_vertex_groups = {}
+        for ms3d_vertex_index, ms3d_vertex in enumerate(ms3d_model.vertices):
+            # prepare for later use for blender vertex group
+            if ms3d_vertex.bone_id != Ms3dSpec.NONE_VERTEX_BONE_ID:
+                if ms3d_vertex.vertex_ex_object \
+                        and ( \
+                        ms3d_vertex.vertex_ex_object.bone_ids[0] != \
+                                Ms3dSpec.NONE_VERTEX_BONE_ID \
+                        or ms3d_vertex.vertex_ex_object.bone_ids[1] != \
+                                Ms3dSpec.NONE_VERTEX_BONE_ID \
+                        or ms3d_vertex.vertex_ex_object.bone_ids[2] != \
+                                Ms3dSpec.NONE_VERTEX_BONE_ID \
+                        ):
+                    ms3d_vertex_group_ids_weights = []
+                    ms3d_vertex_group_ids_weights.append(
+                            (ms3d_vertex.bone_id,
+                            float(ms3d_vertex.vertex_ex_object.weights[0] % 101) / 100.0,
+                            ))
+                    if ms3d_vertex.vertex_ex_object.bone_ids[0] != \
+                            Ms3dSpec.NONE_VERTEX_BONE_ID:
+                        ms3d_vertex_group_ids_weights.append(
+                                (ms3d_vertex.vertex_ex_object.bone_ids[0],
+                                float(ms3d_vertex.vertex_ex_object.weights[1] % 101) / 100.0
+                                ))
+                    if ms3d_vertex.vertex_ex_object.bone_ids[1] != \
+                            Ms3dSpec.NONE_VERTEX_BONE_ID:
+                        ms3d_vertex_group_ids_weights.append(
+                                (ms3d_vertex.vertex_ex_object.bone_ids[1],
+                                float(ms3d_vertex.vertex_ex_object.weights[2] % 101) / 100.0
+                                ))
+                    if ms3d_vertex.vertex_ex_object.bone_ids[2] != \
+                            Ms3dSpec.NONE_VERTEX_BONE_ID:
+                        ms3d_vertex_group_ids_weights.append(
+                                (ms3d_vertex.vertex_ex_object.bone_ids[2],
+                                1.0 -
+                                float((ms3d_vertex.vertex_ex_object.weights[0] % 101)
+                                + (ms3d_vertex.vertex_ex_object.weights[1] % 101)
+                                + (ms3d_vertex.vertex_ex_object.weights[2] % 101)) / 100.0
+                                ))
+
+                else:
+                    ms3d_vertex_group_ids_weights = [(ms3d_vertex.bone_id, 1.0), ]
+
+                for ms3d_vertex_group_id_weight in ms3d_vertex_group_ids_weights:
+                    ms3d_vertex_group_id = ms3d_vertex_group_id_weight[0]
+                    blender_vertex_weight = ms3d_vertex_group_id_weight[1]
+                    blender_vertex_group = ms3d_to_blender_vertex_groups.get(
+                            ms3d_vertex_group_id)
+                    if blender_vertex_group is None:
+                        ms3d_to_blender_vertex_groups[ms3d_vertex_group_id] \
+                                = blender_vertex_group = []
+                    blender_vertex_group.append((ms3d_vertex_index,
+                            blender_vertex_weight))
+
+        ##########################
+        # blender stuff:
+        # create all vertex groups to be used for bones
+        for ms3d_bone_id, blender_vertex_index_weight_list \
+                in ms3d_to_blender_vertex_groups.items():
+            ms3d_name = ms3d_model.joints[ms3d_bone_id].name
+            blender_vertex_group = blender_mesh_object.vertex_groups.new(
+                    ms3d_name)
+            for blender_vertex_id_weight in blender_vertex_index_weight_list:
+                blender_vertex_index = blender_vertex_id_weight[0]
+                blender_vertex_weight = blender_vertex_id_weight[1]
+                blender_vertex_group.add((blender_vertex_index, ),
+                        blender_vertex_weight, 'ADD')
+
+        ##########################
+        # bring joints in the correct order
+        ms3d_joints_ordered = []
+        self.build_ms3d_joint_dependency_order(ms3d_model.joints,
+                ms3d_joints_ordered)
+
+        ##########################
+        # prepare joint data for later use
+        ms3d_joint_by_name = {}
+        for ms3d_joint in ms3d_joints_ordered:
+            item = ms3d_joint_by_name.get(ms3d_joint.name)
+            if item is None:
+                ms3d_joint.__children = []
+                ms3d_joint_by_name[ms3d_joint.name] = ms3d_joint
+
+            matrix_local_rot = (Matrix.Rotation(ms3d_joint.rotation[2], 4, 'Z')
+                    * Matrix.Rotation(ms3d_joint.rotation[1], 4, 'Y')
+                    ) * Matrix.Rotation(ms3d_joint.rotation[0], 4, 'X')
+            matrix_local = Matrix.Translation(Vector(ms3d_joint.position)
+                    ) * matrix_local_rot
+
+            ms3d_joint.__matrix_local_rot = matrix_local_rot
+            ms3d_joint.__matrix_global_rot = matrix_local_rot
+            ms3d_joint.__matrix_local = matrix_local
+            ms3d_joint.__matrix_global = matrix_local
+
+            if ms3d_joint.parent_name:
+                ms3d_joint_parent = ms3d_joint_by_name.get(
+                        ms3d_joint.parent_name)
+                if ms3d_joint_parent is not None:
+                    ms3d_joint_parent.__children.append(ms3d_joint)
+
+                    matrix_global = ms3d_joint_parent.__matrix_global \
+                            * matrix_local
+                    ms3d_joint.__matrix_global = matrix_global
+
+                    matrix_global_rot = ms3d_joint_parent.__matrix_global_rot \
+                            * matrix_local_rot
+                    ms3d_joint.__matrix_global_rot = matrix_global_rot
+
+        ##########################
+        # ms3d_joint to blender_edit_bone
+        if ms3d_model.model_ex_object and not self.options_use_joint_size:
+            joint_length = ms3d_model.model_ex_object.joint_size
+        else:
+            joint_length = self.options_joint_size
+        if joint_length < 0.01:
+            joint_length = 0.01
+
+        blender_scene.objects.active = blender_armature_object
+        enable_edit_mode(True, blender_context)
+        for ms3d_joint in ms3d_joints_ordered:
+            blender_edit_bone = blender_armature.edit_bones.new(ms3d_joint.name)
+            blender_edit_bone.use_connect = False
+            blender_edit_bone.use_inherit_rotation = True
+            blender_edit_bone.use_inherit_scale = True
+            blender_edit_bone.use_local_location = True
+            blender_armature.edit_bones.active = blender_edit_bone
+
+            ms3d_joint = ms3d_joint_by_name[ms3d_joint.name]
+            ms3d_joint_vector = ms3d_joint.__matrix_global * Vector()
+
+            blender_edit_bone.head \
+                    = self.geometry_correction(ms3d_joint_vector)
+
+            vector_tail_end_up = ms3d_joint.__matrix_global_rot * Vector((0,1,0))
+            vector_tail_end_dir = ms3d_joint.__matrix_global_rot * Vector((0,0,1))
+            vector_tail_end_up.normalize()
+            vector_tail_end_dir.normalize()
+            blender_edit_bone.tail = blender_edit_bone.head \
+                    + self.geometry_correction(
+                    vector_tail_end_dir * joint_length)
+            blender_edit_bone.align_roll(self.geometry_correction(
+                    vector_tail_end_up))
+
+            if ms3d_joint.parent_name:
+                ms3d_joint_parent = ms3d_joint_by_name[ms3d_joint.parent_name]
+                blender_edit_bone_parent = ms3d_joint_parent.blender_edit_bone
+                blender_edit_bone.parent = blender_edit_bone_parent
+
+            ms3d_joint.blender_bone_name = blender_edit_bone.name
+            ms3d_joint.blender_edit_bone = blender_edit_bone
+        enable_edit_mode(False, blender_context)
+
+        if self.options_use_joint_to_bones:
+            enable_edit_mode(True, blender_context)
+            for ms3d_joint in ms3d_joints_ordered:
+                blender_edit_bone = blender_armature.edit_bones[ms3d_joint.name]
+                if blender_edit_bone.children:
+                    new_length = 0.0
+                    for child_bone in blender_edit_bone.children:
+                        length = (child_bone.head - blender_edit_bone.head).length
+                        if new_length <= 0 or length < new_length:
+                            new_length = length
+                    if new_length >= 0.01:
+                        direction = blender_edit_bone.tail - blender_edit_bone.head
+                        direction.normalize()
+                        blender_edit_bone.tail = blender_edit_bone.head + (direction * new_length)
+            enable_edit_mode(False, blender_context)
+
+        ##########################
+        # post process bones
+        enable_edit_mode(False, blender_context)
+        for ms3d_joint_name, ms3d_joint in ms3d_joint_by_name.items():
+            blender_bone = blender_armature.bones.get(
+                    ms3d_joint.blender_bone_name)
+            if blender_bone is None:
+                continue
+
+            blender_bone.ms3d.name = ms3d_joint.name
+            blender_bone.ms3d.flags = Ms3dUi.flags_from_ms3d(ms3d_joint.flags)
+
+            ms3d_joint_ex = ms3d_joint.joint_ex_object
+            if ms3d_joint_ex is not None:
+                blender_bone.ms3d.color = ms3d_joint_ex.color
+
+            ms3d_comment = ms3d_joint.comment_object
+            if ms3d_comment is not None:
+                blender_bone.ms3d.comment = ms3d_comment.comment
+
+        ##########################
+        if not self.options_use_animation:
+            return blender_armature_object
+
+
+        ##########################
+        # process pose bones
+        enable_pose_mode(True, blender_context)
+
+        blender_action = blender_context.blend_data.actions.new(ms3d_action_name)
+        if blender_armature_object.animation_data is None:
+            blender_armature_object.animation_data_create()
+        blender_armature_object.animation_data.action = blender_action
+
+        ##########################
+        # transition between keys may be incorrect
+        # because of the gimbal-lock problem!
+        # http://www.youtube.com/watch?v=zc8b2Jo7mno
+        # http://www.youtube.com/watch?v=rrUCBOlJdt4
+        # you can fix it manually by selecting the affected keyframes
+        # and allpy the following option to it:
+        # "Graph Editor -> Key -> Discontinuity (Euler) Filter"
+        # ==> "bpy.ops.graph.euler_filter()"
+        # but this option is only available for Euler rotation f-curves!
+        #
+        for ms3d_joint_name, ms3d_joint in ms3d_joint_by_name.items():
+            blender_pose_bone = blender_armature_object.pose.bones.get(
+                    ms3d_joint.blender_bone_name)
+            if blender_pose_bone is None:
+                continue
+
+            data_path = blender_pose_bone.path_from_id('location')
+            fcurve_location_x = blender_action.fcurves.new(data_path, index=0)
+            fcurve_location_y = blender_action.fcurves.new(data_path, index=1)
+            fcurve_location_z = blender_action.fcurves.new(data_path, index=2)
+            for translation_key_frames in ms3d_joint.translation_key_frames:
+                frame = (translation_key_frames.time * ms3d_model.animation_fps)
+                matrix_local = Matrix.Translation(
+                        Vector(translation_key_frames.position))
+                v = (matrix_local) * Vector()
+                fcurve_location_x.keyframe_points.insert(frame, -v[0])
+                fcurve_location_y.keyframe_points.insert(frame, v[2])
+                fcurve_location_z.keyframe_points.insert(frame, v[1])
+
+            if self.options_use_quaternion_rotation:
+                blender_pose_bone.rotation_mode = 'QUATERNION'
+                data_path = blender_pose_bone.path_from_id("rotation_quaternion")
+                fcurve_rotation_w = blender_action.fcurves.new(data_path, index=0)
+                fcurve_rotation_x = blender_action.fcurves.new(data_path, index=1)
+                fcurve_rotation_y = blender_action.fcurves.new(data_path, index=2)
+                fcurve_rotation_z = blender_action.fcurves.new(data_path, index=3)
+                for rotation_key_frames in ms3d_joint.rotation_key_frames:
+                    frame = (rotation_key_frames.time * ms3d_model.animation_fps)
+                    matrix_local_rot = (
+                            Matrix.Rotation(
+                                    rotation_key_frames.rotation[2], 4, 'Y')
+                            * Matrix.Rotation(
+                                    rotation_key_frames.rotation[1], 4, 'Z')
+                            ) * Matrix.Rotation(
+                                    -rotation_key_frames.rotation[0], 4, 'X')
+                    q = (matrix_local_rot).to_quaternion()
+                    fcurve_rotation_w.keyframe_points.insert(frame, q.w)
+                    fcurve_rotation_x.keyframe_points.insert(frame, q.x)
+                    fcurve_rotation_y.keyframe_points.insert(frame, q.y)
+                    fcurve_rotation_z.keyframe_points.insert(frame, q.z)
+            else:
+                blender_pose_bone.rotation_mode = 'XZY'
+                data_path = blender_pose_bone.path_from_id("rotation_euler")
+                fcurve_rotation_x = blender_action.fcurves.new(data_path, index=0)
+                fcurve_rotation_y = blender_action.fcurves.new(data_path, index=1)
+                fcurve_rotation_z = blender_action.fcurves.new(data_path, index=2)
+                for rotation_key_frames in ms3d_joint.rotation_key_frames:
+                    frame = (rotation_key_frames.time * ms3d_model.animation_fps)
+                    fcurve_rotation_x.keyframe_points.insert(
+                            frame, -rotation_key_frames.rotation[0])
+                    fcurve_rotation_y.keyframe_points.insert(
+                            frame, rotation_key_frames.rotation[2])
+                    fcurve_rotation_z.keyframe_points.insert(
+                            frame, rotation_key_frames.rotation[1])
+
+        enable_pose_mode(False, blender_context)
+
+        return blender_armature_object
+
+
+    ###########################################################################
+    def geometry_correction(self, value):
+        return Vector((value[2], value[0], value[1]))
+
+
+    ###########################################################################
+    def build_ms3d_joint_dependency_order(self, ms3d_joints, ms3d_joints_ordered):
+        ms3d_joints_children = {"": {}}
+        for ms3d_joint in ms3d_joints:
+            if ms3d_joint.parent_name:
+                ms3d_joint_children = ms3d_joints_children.get(
+                        ms3d_joint.parent_name)
+                if ms3d_joint_children is None:
+                    ms3d_joint_children = ms3d_joints_children[
+                            ms3d_joint.parent_name] = {}
+            else:
+                ms3d_joint_children = ms3d_joints_children[""]
+
+            ms3d_joint_children[ms3d_joint.name] = ms3d_joint
+
+        self.traverse_dependencies(
+                ms3d_joints_ordered,
+                ms3d_joints_children,
+                "")
+
+
+        return ms3d_joints_ordered
+
+
+    ###########################################################################
+    def traverse_dependencies(self, ms3d_joints_ordered, ms3d_joints_children,
+            key):
+        ms3d_joint_children = ms3d_joints_children.get(key)
+        if ms3d_joint_children:
+            for item in ms3d_joint_children.items():
+                ms3d_joint_name = item[0]
+                ms3d_joint = item[1]
+                ms3d_joints_ordered.append(ms3d_joint)
+                self.traverse_dependencies(
+                        ms3d_joints_ordered,
+                        ms3d_joints_children,
+                        ms3d_joint_name)
+
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####
diff --git a/io_scene_ms3d/ms3d_spec.py b/io_scene_ms3d/ms3d_spec.py
new file mode 100644
index 0000000000000000000000000000000000000000..1138a01eb0be8cfe6c91d1fdeff8c20f4f0463cc
--- /dev/null
+++ b/io_scene_ms3d/ms3d_spec.py
@@ -0,0 +1,2069 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+
+
+from struct import (
+        pack,
+        unpack,
+        )
+from sys import (
+        exc_info,
+        )
+
+###############################################################################
+#
+# MilkShape 3D 1.8.5 File Format Specification
+#
+# all specifications were taken from SDK 1.8.5
+#
+# some additional specifications were taken from
+# MilkShape 3D Viewer v2.0 (Nov 06 2007) - msMesh.h
+#
+
+
+###############################################################################
+#
+# sizes
+#
+
+class Ms3dSpec:
+    ###########################################################################
+    #
+    # max values
+    #
+    MAX_VERTICES = 65534 # 0..65533; note: (65534???, 65535???)
+    MAX_TRIANGLES = 65534 # 0..65533; note: (65534???, 65535???)
+    MAX_GROUPS = 255 # 1..255; note: (0 default group)
+    MAX_MATERIALS = 128 # 0..127; note: (-1 no material)
+    MAX_JOINTS = 128 # 0..127; note: (-1 no joint)
+    MAX_SMOOTH_GROUP = 32 # 0..32; note: (0 no smoothing group)
+    MAX_TEXTURE_FILENAME_SIZE = 128
+
+    ###########################################################################
+    #
+    # flags
+    #
+    FLAG_NONE = 0
+    FLAG_SELECTED = 1
+    FLAG_HIDDEN = 2
+    FLAG_SELECTED2 = 4
+    FLAG_DIRTY = 8
+    FLAG_ISKEY = 16 # additional spec from [2]
+    FLAG_NEWLYCREATED = 32 # additional spec from [2]
+    FLAG_MARKED = 64 # additional spec from [2]
+
+    FLAG_TEXTURE_NONE = 0x00
+    FLAG_TEXTURE_COMBINE_ALPHA = 0x20
+    FLAG_TEXTURE_HAS_ALPHA = 0x40
+    FLAG_TEXTURE_SPHERE_MAP = 0x80
+
+    MODE_TRANSPARENCY_SIMPLE = 0
+    MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF = 1
+    MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES = 2
+
+
+    ###########################################################################
+    #
+    # values
+    #
+    HEADER = "MS3D000000"
+
+
+    ###########################################################################
+    #
+    # min, max, default values
+    #
+    NONE_VERTEX_BONE_ID = -1
+    NONE_GROUP_MATERIAL_INDEX = -1
+
+    DEFAULT_HEADER = HEADER
+    DEFAULT_HEADER_VERSION = 4
+    DEFAULT_VERTEX_BONE_ID = NONE_VERTEX_BONE_ID
+    DEFAULT_TRIANGLE_SMOOTHING_GROUP = 0
+    DEFAULT_TRIANGLE_GROUP = 0
+    DEFAULT_MATERIAL_MODE = FLAG_TEXTURE_NONE
+    DEFAULT_GROUP_MATERIAL_INDEX = NONE_GROUP_MATERIAL_INDEX
+    DEFAULT_MODEL_JOINT_SIZE = 1.0
+    DEFAULT_MODEL_TRANSPARENCY_MODE = MODE_TRANSPARENCY_SIMPLE
+    DEFAULT_MODEL_ANIMATION_FPS = 25.0
+    DEFAULT_MODEL_SUB_VERSION_COMMENTS = 1
+    DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA = 2
+    DEFAULT_MODEL_SUB_VERSION_JOINT_EXTRA = 1
+    DEFAULT_MODEL_SUB_VERSION_MODEL_EXTRA = 1
+    DEFAULT_FLAGS = FLAG_NONE
+    MAX_MATERIAL_SHININESS = 128
+
+    # blender default / OpenGL default
+    DEFAULT_MATERIAL_AMBIENT = (0.2, 0.2, 0.2, 1.0)
+    DEFAULT_MATERIAL_DIFFUSE = (0.8, 0.8, 0.8, 1.0)
+    DEFAULT_MATERIAL_SPECULAR = (1.0, 1.0, 1.0, 1.0)
+    DEFAULT_MATERIAL_EMISSIVE = (0.0, 0.0, 0.0, 1.0)
+    DEFAULT_MATERIAL_SHININESS = 12.5
+
+    DEFAULT_JOINT_COLOR = (0.8, 0.8, 0.8)
+
+###############################################################################
+#
+# helper class for basic raw io
+#
+class Ms3dIo:
+    # sizes for IO
+    SIZE_BYTE = 1
+    SIZE_SBYTE = 1
+    SIZE_WORD = 2
+    SIZE_DWORD = 4
+    SIZE_FLOAT = 4
+    LENGTH_ID = 10
+    LENGTH_NAME = 32
+    LENGTH_FILENAME = 128
+
+    PRECISION = 4
+
+    @staticmethod
+    def read_byte(raw_io):
+        """ read a single byte from raw_io """
+        buffer = raw_io.read(Ms3dIo.SIZE_BYTE)
+        if not buffer:
+            raise EOFError()
+        value = unpack('<B', buffer)[0]
+        return value
+
+    @staticmethod
+    def write_byte(raw_io, value):
+        """ write a single byte to raw_io """
+        raw_io.write(pack('<B', value))
+
+    @staticmethod
+    def read_sbyte(raw_io):
+        """ read a single signed byte from raw_io """
+        buffer = raw_io.read(Ms3dIo.SIZE_BYTE)
+        if not buffer:
+            raise EOFError()
+        value = unpack('<b', buffer)[0]
+        return value
+
+    @staticmethod
+    def write_sbyte(raw_io, value):
+        """ write a single signed byte to raw_io """
+        raw_io.write(pack('<b', value))
+
+    @staticmethod
+    def read_word(raw_io):
+        """ read a word from raw_io """
+        buffer = raw_io.read(Ms3dIo.SIZE_WORD)
+        if not buffer:
+            raise EOFError()
+        value = unpack('<H', buffer)[0]
+        return value
+
+    @staticmethod
+    def write_word(raw_io, value):
+        """ write a word to raw_io """
+        raw_io.write(pack('<H', value))
+
+    @staticmethod
+    def read_dword(raw_io):
+        """ read a double word from raw_io """
+        buffer = raw_io.read(Ms3dIo.SIZE_DWORD)
+        if not buffer:
+            raise EOFError()
+        value = unpack('<I', buffer)[0]
+        return value
+
+    @staticmethod
+    def write_dword(raw_io, value):
+        """ write a double word to raw_io """
+        raw_io.write(pack('<I', value))
+
+    @staticmethod
+    def read_float(raw_io):
+        """ read a float from raw_io """
+        buffer = raw_io.read(Ms3dIo.SIZE_FLOAT)
+        if not buffer:
+            raise EOFError()
+        value = unpack('<f', buffer)[0]
+        return value
+
+    @staticmethod
+    def write_float(raw_io, value):
+        """ write a float to raw_io """
+        raw_io.write(pack('<f', value))
+
+    @staticmethod
+    def read_array(raw_io, itemReader, count):
+        """ read an array[count] of objects from raw_io, by using a itemReader """
+        value = []
+        for i in range(count):
+            itemValue = itemReader(raw_io)
+            value.append(itemValue)
+        return tuple(value)
+
+    @staticmethod
+    def write_array(raw_io, itemWriter, count, value):
+        """ write an array[count] of objects to raw_io, by using a itemWriter """
+        for i in range(count):
+            itemValue = value[i]
+            itemWriter(raw_io, itemValue)
+
+    @staticmethod
+    def read_array2(raw_io, itemReader, count, count2):
+        """ read an array[count][count2] of objects from raw_io,
+            by using a itemReader """
+        value = []
+        for i in range(count):
+            itemValue = Ms3dIo.read_array(raw_io, itemReader, count2)
+            value.append(tuple(itemValue))
+        return value
+
+    @staticmethod
+    def write_array2(raw_io, itemWriter, count, count2, value):
+        """ write an array[count][count2] of objects to raw_io,
+            by using a itemWriter """
+        for i in range(count):
+            itemValue = value[i]
+            Ms3dIo.write_array(raw_io, itemWriter, count2, itemValue)
+
+    @staticmethod
+    def read_string(raw_io, length):
+        """ read a string of a specific length from raw_io """
+        value = []
+        skip = False
+        for i in range(length):
+            buffer = raw_io.read(Ms3dIo.SIZE_SBYTE)
+            if not buffer:
+                raise EOFError()
+            raw = (int)(unpack('<b', buffer)[0])
+            if (raw >= 32) & (raw <= 255):
+                pass
+            else:
+                if (raw == 0):
+                    raw = 0
+                    skip = True
+                else:
+                    raw = 32
+
+            c = chr(raw)
+
+            if (not skip):
+                value.append(c)
+
+        finalValue = "".join(value)
+        return finalValue
+
+    @staticmethod
+    def write_string(raw_io, length, value):
+        """ write a string of a specific length to raw_io """
+        l = len(value)
+        for i in range(length):
+            if(i < l):
+                c = value[i]
+
+                if (isinstance(c, str)):
+                    c = c[0]
+                    raw = ord(c)
+                elif (isinstance(c, int)):
+                    raw = c
+                else:
+                    pass
+            else:
+                raw = 0
+
+            raw_io.write(pack('<b', (int)(raw % 256)))
+
+
+###############################################################################
+#
+# multi complex types
+#
+
+###############################################################################
+class Ms3dHeader:
+    """ Ms3dHeader """
+    __slots__ = (
+            'id',
+            'version',
+            )
+
+    def __init__(
+            self,
+            default_id=Ms3dSpec.DEFAULT_HEADER,
+            default_version=Ms3dSpec.DEFAULT_HEADER_VERSION
+            ):
+        self.id = default_id
+        self.version = default_version
+
+    def __repr__(self):
+        return "\n<id='{}', version={}>".format(
+            self.id,
+            self.version
+            )
+
+    def __hash__(self):
+        return hash(self.id) ^ hash(self.version)
+
+    def __eq__(self, other):
+        return ((self is not None) and (other is not None)
+                and (self.id == other.id)
+                and (self.version == other.version))
+
+    def read(self, raw_io):
+        self.id = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_ID)
+        self.version = Ms3dIo.read_dword(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_ID, self.id)
+        Ms3dIo.write_dword(raw_io, self.version)
+
+
+###############################################################################
+class Ms3dVertex:
+    """ Ms3dVertex """
+    """
+    __slots__ was taking out,
+    to be able to inject additional attributes during runtime
+    __slots__ = (
+            'flags',
+            'bone_id',
+            'reference_count',
+            '_vertex',
+            '_vertex_ex_object', # Ms3dVertexEx
+            )
+    """
+
+    def __init__(
+            self,
+            default_flags=Ms3dSpec.DEFAULT_FLAGS,
+            default_vertex=(0.0, 0.0, 0.0),
+            default_bone_id=Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+            default_reference_count=0,
+            default_vertex_ex_object=None, # Ms3dVertexEx
+            ):
+        self.flags = default_flags
+        self._vertex = default_vertex
+        self.bone_id = default_bone_id
+        self.reference_count = default_reference_count
+
+        if default_vertex_ex_object is None:
+            default_vertex_ex_object = Ms3dVertexEx2()
+            # Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA = 2
+        self._vertex_ex_object = default_vertex_ex_object
+        # Ms3dVertexEx
+
+    def __repr__(self):
+        return "\n<flags={}, vertex=({:.{p}f}, {:.{p}f}, {:.{p}f}), bone_id={},"\
+                " reference_count={}>".format(
+                self.flags,
+                self._vertex[0],
+                self._vertex[1],
+                self._vertex[2],
+                self.bone_id,
+                self.reference_count,
+                p=Ms3dIo.PRECISION
+                )
+
+    def __hash__(self):
+        return (hash(self.vertex)
+                #^ hash(self.flags)
+                #^ hash(self.bone_id)
+                #^ hash(self.reference_count)
+                )
+
+    def __eq__(self, other):
+        return ((self.vertex == other.vertex)
+                #and (self.flags == other.flags)
+                #and (self.bone_id == other.bone_id)
+                #and (self.reference_count == other.reference_count)
+                )
+
+
+    @property
+    def vertex(self):
+        return self._vertex
+
+    @property
+    def vertex_ex_object(self):
+        return self._vertex_ex_object
+
+
+    def read(self, raw_io):
+        self.flags = Ms3dIo.read_byte(raw_io)
+        self._vertex = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        self.bone_id = Ms3dIo.read_sbyte(raw_io)
+        self.reference_count = Ms3dIo.read_byte(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_byte(raw_io, self.flags)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.vertex)
+        Ms3dIo.write_sbyte(raw_io, self.bone_id)
+        Ms3dIo.write_byte(raw_io, self.reference_count)
+
+
+###############################################################################
+class Ms3dTriangle:
+    """ Ms3dTriangle """
+    """
+    __slots__ was taking out,
+    to be able to inject additional attributes during runtime
+    __slots__ = (
+            'flags',
+            'smoothing_group',
+            'group_index',
+            '_vertex_indices',
+            '_vertex_normals',
+            '_s',
+            '_t',
+            )
+    """
+
+    def __init__(
+            self,
+            default_flags=Ms3dSpec.DEFAULT_FLAGS,
+            default_vertex_indices=(0, 0, 0),
+            default_vertex_normals=(
+                    (0.0, 0.0, 0.0),
+                    (0.0, 0.0, 0.0),
+                    (0.0, 0.0, 0.0)),
+            default_s=(0.0, 0.0, 0.0),
+            default_t=(0.0, 0.0, 0.0),
+            default_smoothing_group=Ms3dSpec.DEFAULT_TRIANGLE_SMOOTHING_GROUP,
+            default_group_index=Ms3dSpec.DEFAULT_TRIANGLE_GROUP
+            ):
+        self.flags = default_flags
+        self._vertex_indices = default_vertex_indices
+        self._vertex_normals = default_vertex_normals
+        self._s = default_s
+        self._t = default_t
+        self.smoothing_group = default_smoothing_group
+        self.group_index = default_group_index
+
+    def __repr__(self):
+        return "\n<flags={}, vertex_indices={}, vertex_normals=(({:.{p}f}, "\
+                "{:.{p}f}, {:.{p}f}), ({:.{p}f}, {:.{p}f}, {:.{p}f}), ({:.{p}f}, "\
+                "{:.{p}f}, {:.{p}f})), s=({:.{p}f}, {:.{p}f}, {:.{p}f}), "\
+                "t=({:.{p}f}, {:.{p}f}, {:.{p}f}), smoothing_group={}, "\
+                "group_index={}>".format(
+                self.flags,
+                self.vertex_indices,
+                self.vertex_normals[0][0],
+                self.vertex_normals[0][1],
+                self.vertex_normals[0][2],
+                self.vertex_normals[1][0],
+                self.vertex_normals[1][1],
+                self.vertex_normals[1][2],
+                self.vertex_normals[2][0],
+                self.vertex_normals[2][1],
+                self.vertex_normals[2][2],
+                self.s[0],
+                self.s[1],
+                self.s[2],
+                self.t[0],
+                self.t[1],
+                self.t[2],
+                self.smoothing_group,
+                self.group_index,
+                p=Ms3dIo.PRECISION
+                )
+
+
+    @property
+    def vertex_indices(self):
+        return self._vertex_indices
+
+    @property
+    def vertex_normals(self):
+        return self._vertex_normals
+
+    @property
+    def s(self):
+        return self._s
+
+    @property
+    def t(self):
+        return self._t
+
+
+    def read(self, raw_io):
+        self.flags = Ms3dIo.read_word(raw_io)
+        self._vertex_indices = Ms3dIo.read_array(raw_io, Ms3dIo.read_word, 3)
+        self._vertex_normals = Ms3dIo.read_array2(raw_io, Ms3dIo.read_float, 3, 3)
+        self._s = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        self._t = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        self.smoothing_group = Ms3dIo.read_byte(raw_io)
+        self.group_index = Ms3dIo.read_byte(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_word(raw_io, self.flags)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_word, 3, self.vertex_indices)
+        Ms3dIo.write_array2(raw_io, Ms3dIo.write_float, 3, 3, self.vertex_normals)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.s)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.t)
+        Ms3dIo.write_byte(raw_io, self.smoothing_group)
+        Ms3dIo.write_byte(raw_io, self.group_index)
+
+
+###############################################################################
+class Ms3dGroup:
+    """ Ms3dGroup """
+    """
+    __slots__ was taking out,
+    to be able to inject additional attributes during runtime
+    __slots__ = (
+            'flags',
+            'name',
+            'material_index',
+            '_triangle_indices',
+            '_comment_object', # Ms3dComment
+            )
+    """
+
+    def __init__(
+            self,
+            default_flags=Ms3dSpec.DEFAULT_FLAGS,
+            default_name="",
+            default_triangle_indices=None,
+            default_material_index=Ms3dSpec.DEFAULT_GROUP_MATERIAL_INDEX,
+            default_comment_object=None, # Ms3dComment
+            ):
+        if (default_name is None):
+            default_name = ""
+
+        if (default_triangle_indices is None):
+            default_triangle_indices = []
+
+        self.flags = default_flags
+        self.name = default_name
+        self._triangle_indices = default_triangle_indices
+        self.material_index = default_material_index
+
+        if default_comment_object is None:
+            default_comment_object = Ms3dCommentEx()
+        self._comment_object = default_comment_object # Ms3dComment
+
+    def __repr__(self):
+        return "\n<flags={}, name='{}', number_triangles={},"\
+                " triangle_indices={}, material_index={}>".format(
+                self.flags,
+                self.name,
+                self.number_triangles,
+                self.triangle_indices,
+                self.material_index
+                )
+
+
+    @property
+    def number_triangles(self):
+        if self.triangle_indices is None:
+            return 0
+        return len(self.triangle_indices)
+
+    @property
+    def triangle_indices(self):
+        return self._triangle_indices
+
+    @property
+    def comment_object(self):
+        return self._comment_object
+
+
+    def read(self, raw_io):
+        self.flags = Ms3dIo.read_byte(raw_io)
+        self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
+        _number_triangles = Ms3dIo.read_word(raw_io)
+        self._triangle_indices = Ms3dIo.read_array(
+                raw_io, Ms3dIo.read_word, _number_triangles)
+        self.material_index = Ms3dIo.read_sbyte(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_byte(raw_io, self.flags)
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name)
+        Ms3dIo.write_word(raw_io, self.number_triangles)
+        Ms3dIo.write_array(
+                raw_io, Ms3dIo.write_word, self.number_triangles,
+                self.triangle_indices)
+        Ms3dIo.write_sbyte(raw_io, self.material_index)
+
+
+###############################################################################
+class Ms3dMaterial:
+    """ Ms3dMaterial """
+    """
+    __slots__ was taking out,
+    to be able to inject additional attributes during runtime
+    __slots__ = (
+            'name',
+            'shininess',
+            'transparency',
+            'mode',
+            'texture',
+            'alphamap',
+            '_ambient',
+            '_diffuse',
+            '_specular',
+            '_emissive',
+            '_comment_object', # Ms3dComment
+            )
+    """
+
+    def __init__(
+            self,
+            default_name="",
+            default_ambient=list(Ms3dSpec.DEFAULT_MATERIAL_AMBIENT),
+            default_diffuse=list(Ms3dSpec.DEFAULT_MATERIAL_DIFFUSE),
+            default_specular=list(Ms3dSpec.DEFAULT_MATERIAL_SPECULAR),
+            default_emissive=list(Ms3dSpec.DEFAULT_MATERIAL_EMISSIVE),
+            default_shininess=Ms3dSpec.DEFAULT_MATERIAL_SHININESS,
+            default_transparency=0.0,
+            default_mode=Ms3dSpec.DEFAULT_MATERIAL_MODE,
+            default_texture="",
+            default_alphamap="",
+            default_comment_object=None, # Ms3dComment
+            ):
+        if (default_name is None):
+            default_name = ""
+
+        if (default_texture is None):
+            default_texture = ""
+
+        if (default_alphamap is None):
+            default_alphamap = ""
+
+        self.name = default_name
+        self._ambient = default_ambient
+        self._diffuse = default_diffuse
+        self._specular = default_specular
+        self._emissive = default_emissive
+        self.shininess = default_shininess
+        self.transparency = default_transparency
+        self.mode = default_mode
+        self.texture = default_texture
+        self.alphamap = default_alphamap
+
+        if default_comment_object is None:
+            default_comment_object = Ms3dCommentEx()
+        self._comment_object = default_comment_object # Ms3dComment
+
+    def __repr__(self):
+        return "\n<name='{}', ambient=({:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), "\
+                "diffuse=({:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), specular=("\
+                "{:.{p}f}, {:.{p}f}, {:.{p}f}, {:.{p}f}), emissive=({:.{p}f}, "\
+                "{:.{p}f}, {:.{p}f}, {:.{p}f}), shininess={:.{p}f}, transparency="\
+                "{:.{p}f}, mode={}, texture='{}', alphamap='{}'>".format(
+                self.name,
+                self.ambient[0],
+                self.ambient[1],
+                self.ambient[2],
+                self.ambient[3],
+                self.diffuse[0],
+                self.diffuse[1],
+                self.diffuse[2],
+                self.diffuse[3],
+                self.specular[0],
+                self.specular[1],
+                self.specular[2],
+                self.specular[3],
+                self.emissive[0],
+                self.emissive[1],
+                self.emissive[2],
+                self.emissive[3],
+                self.shininess,
+                self.transparency,
+                self.mode,
+                self.texture,
+                self.alphamap,
+                p=Ms3dIo.PRECISION
+                )
+
+    def __hash__(self):
+        return (hash(self.name)
+
+                ^ hash(self.ambient)
+                ^ hash(self.diffuse)
+                ^ hash(self.specular)
+                ^ hash(self.emissive)
+
+                ^ hash(self.shininess)
+                ^ hash(self.transparency)
+                ^ hash(self.mode)
+
+                ^ hash(self.texture)
+                ^ hash(self.alphamap)
+                )
+
+    def __eq__(self, other):
+        return ((self.name == other.name)
+
+                and (self.ambient == other.ambient)
+                and (self.diffuse == other.diffuse)
+                and (self.specular == other.specular)
+                and (self.emissive == other.emissive)
+
+                and (self.shininess == other.shininess)
+                and (self.transparency == other.transparency)
+                and (self.mode == other.mode)
+
+                #and (self.texture == other.texture)
+                #and (self.alphamap == other.alphamap)
+                )
+
+
+    @property
+    def ambient(self):
+        return self._ambient
+
+    @property
+    def diffuse(self):
+        return self._diffuse
+
+    @property
+    def specular(self):
+        return self._specular
+
+    @property
+    def emissive(self):
+        return self._emissive
+
+    @property
+    def comment_object(self):
+        return self._comment_object
+
+
+    def read(self, raw_io):
+        self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
+        self._ambient = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
+        self._diffuse = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
+        self._specular = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
+        self._emissive = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 4)
+        self.shininess = Ms3dIo.read_float(raw_io)
+        self.transparency = Ms3dIo.read_float(raw_io)
+        self.mode = Ms3dIo.read_byte(raw_io)
+        self.texture = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_FILENAME)
+        self.alphamap = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_FILENAME)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.ambient)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.diffuse)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.specular)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 4, self.emissive)
+        Ms3dIo.write_float(raw_io, self.shininess)
+        Ms3dIo.write_float(raw_io, self.transparency)
+        Ms3dIo.write_byte(raw_io, self.mode)
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_FILENAME, self.texture)
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_FILENAME, self.alphamap)
+
+
+###############################################################################
+class Ms3dRotationKeyframe:
+    """ Ms3dRotationKeyframe """
+    __slots__ = (
+            'time',
+            '_rotation',
+            )
+
+    def __init__(
+            self,
+            default_time=0.0,
+            default_rotation=(0.0, 0.0, 0.0)
+            ):
+        self.time = default_time
+        self._rotation = default_rotation
+
+    def __repr__(self):
+        return "\n<time={:.{p}f}, rotation=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format(
+                self.time,
+                self.rotation[0],
+                self.rotation[1],
+                self.rotation[2],
+                p=Ms3dIo.PRECISION
+                )
+
+
+    @property
+    def rotation(self):
+        return self._rotation
+
+
+    def read(self, raw_io):
+        self.time = Ms3dIo.read_float(raw_io)
+        self._rotation = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_float(raw_io, self.time)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.rotation)
+
+
+###############################################################################
+class Ms3dTranslationKeyframe:
+    """ Ms3dTranslationKeyframe """
+    __slots__ = (
+            'time',
+            '_position',
+            )
+
+    def __init__(
+            self,
+            default_time=0.0,
+            default_position=(0.0, 0.0, 0.0)
+            ):
+        self.time = default_time
+        self._position = default_position
+
+    def __repr__(self):
+        return "\n<time={:.{p}f}, position=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format(
+                self.time,
+                self.position[0],
+                self.position[1],
+                self.position[2],
+                p=Ms3dIo.PRECISION
+                )
+
+
+    @property
+    def position(self):
+        return self._position
+
+
+    def read(self, raw_io):
+        self.time = Ms3dIo.read_float(raw_io)
+        self._position = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_float(raw_io, self.time)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.position)
+
+
+###############################################################################
+class Ms3dJoint:
+    """ Ms3dJoint """
+    """
+    __slots__ was taking out,
+    to be able to inject additional attributes during runtime
+    __slots__ = (
+            'flags',
+            'name',
+            'parent_name',
+            '_rotation',
+            '_position',
+            '_rotation_keyframes',
+            '_translation_keyframes',
+            '_joint_ex_object', # Ms3dJointEx
+            '_comment_object', # Ms3dComment
+            )
+    """
+
+    def __init__(
+            self,
+            default_flags=Ms3dSpec.DEFAULT_FLAGS,
+            default_name="",
+            default_parent_name="",
+            default_rotation=(0.0, 0.0, 0.0),
+            default_position=(0.0, 0.0, 0.0),
+            default_rotation_keyframes=None,
+            default_translation_keyframes=None,
+            default_joint_ex_object=None, # Ms3dJointEx
+            default_comment_object=None, # Ms3dComment
+            ):
+        if (default_name is None):
+            default_name = ""
+
+        if (default_parent_name is None):
+            default_parent_name = ""
+
+        if (default_rotation_keyframes is None):
+            default_rotation_keyframes = [] #Ms3dRotationKeyframe()
+
+        if (default_translation_keyframes is None):
+            default_translation_keyframes = [] #Ms3dTranslationKeyframe()
+
+        self.flags = default_flags
+        self.name = default_name
+        self.parent_name = default_parent_name
+        self._rotation = default_rotation
+        self._position = default_position
+        self._rotation_keyframes = default_rotation_keyframes
+        self._translation_keyframes = default_translation_keyframes
+
+        if default_comment_object is None:
+            default_comment_object = Ms3dCommentEx()
+        self._comment_object = default_comment_object # Ms3dComment
+
+        if default_joint_ex_object is None:
+            default_joint_ex_object = Ms3dJointEx()
+        self._joint_ex_object = default_joint_ex_object # Ms3dJointEx
+
+    def __repr__(self):
+        return "\n<flags={}, name='{}', parent_name='{}', rotation=({:.{p}f}, "\
+                "{:.{p}f}, {:.{p}f}), position=({:.{p}f}, {:.{p}f}, {:.{p}f}), "\
+                "number_rotation_keyframes={}, number_translation_keyframes={},"\
+                " rotation_key_frames={}, translation_key_frames={}>".format(
+                self.flags,
+                self.name,
+                self.parent_name,
+                self.rotation[0],
+                self.rotation[1],
+                self.rotation[2],
+                self.position[0],
+                self.position[1],
+                self.position[2],
+                self.number_rotation_keyframes,
+                self.number_translation_keyframes,
+                self.rotation_key_frames,
+                self.translation_key_frames,
+                p=Ms3dIo.PRECISION
+                )
+
+
+    @property
+    def rotation(self):
+        return self._rotation
+
+    @property
+    def position(self):
+        return self._position
+
+    @property
+    def number_rotation_keyframes(self):
+        if self.rotation_key_frames is None:
+            return 0
+        return len(self.rotation_key_frames)
+
+    @property
+    def number_translation_keyframes(self):
+        if self.translation_key_frames is None:
+            return 0
+        return len(self.translation_key_frames)
+
+    @property
+    def rotation_key_frames(self):
+        return self._rotation_keyframes
+
+    @property
+    def translation_key_frames(self):
+        return self._translation_keyframes
+
+
+    @property
+    def joint_ex_object(self):
+        return self._joint_ex_object
+
+
+    @property
+    def comment_object(self):
+        return self._comment_object
+
+
+    def read(self, raw_io):
+        self.flags = Ms3dIo.read_byte(raw_io)
+        self.name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
+        self.parent_name = Ms3dIo.read_string(raw_io, Ms3dIo.LENGTH_NAME)
+        self._rotation = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        self._position = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        _number_rotation_keyframes = Ms3dIo.read_word(raw_io)
+        _number_translation_keyframes = Ms3dIo.read_word(raw_io)
+        self._rotation_keyframes = []
+        for i in range(_number_rotation_keyframes):
+            self.rotation_key_frames.append(Ms3dRotationKeyframe().read(raw_io))
+        self._translation_keyframes = []
+        for i in range(_number_translation_keyframes):
+            self.translation_key_frames.append(
+                    Ms3dTranslationKeyframe().read(raw_io))
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_byte(raw_io, self.flags)
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.name)
+        Ms3dIo.write_string(raw_io, Ms3dIo.LENGTH_NAME, self.parent_name)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.rotation)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.position)
+        Ms3dIo.write_word(raw_io, self.number_rotation_keyframes)
+        Ms3dIo.write_word(raw_io, self.number_translation_keyframes)
+        for i in range(self.number_rotation_keyframes):
+            self.rotation_key_frames[i].write(raw_io)
+        for i in range(self.number_translation_keyframes):
+            self.translation_key_frames[i].write(raw_io)
+
+
+###############################################################################
+class Ms3dCommentEx:
+    """ Ms3dCommentEx """
+    __slots__ = (
+            'index',
+            'comment',
+            )
+
+    def __init__(
+            self,
+            default_index=0,
+            default_comment=""
+            ):
+        if (default_comment is None):
+            default_comment = ""
+
+        self.index = default_index
+        self.comment = default_comment
+
+    def __repr__(self):
+        return "\n<index={}, comment_length={}, comment='{}'>".format(
+                self.index,
+                self.comment_length,
+                self.comment
+                )
+
+
+    @property
+    def comment_length(self):
+        if self.comment is None:
+            return 0
+        return len(self.comment)
+
+
+    def read(self, raw_io):
+        self.index = Ms3dIo.read_dword(raw_io)
+        _comment_length = Ms3dIo.read_dword(raw_io)
+        self.comment = Ms3dIo.read_string(raw_io, _comment_length)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_dword(raw_io, self.index)
+        Ms3dIo.write_dword(raw_io, self.comment_length)
+        Ms3dIo.write_string(raw_io, self.comment_length, self.comment)
+
+
+###############################################################################
+class Ms3dComment:
+    """ Ms3dComment """
+    __slots__ = (
+            'comment',
+            )
+
+    def __init__(
+            self,
+            default_comment=""
+            ):
+        if (default_comment is None):
+            default_comment = ""
+
+        self.comment = default_comment
+
+    def __repr__(self):
+        return "\n<comment_length={}, comment='{}'>".format(
+                self.comment_length,
+                self.comment
+                )
+
+
+    @property
+    def comment_length(self):
+        if self.comment is None:
+            return 0
+        return len(self.comment)
+
+
+    def read(self, raw_io):
+        _comment_length = Ms3dIo.read_dword(raw_io)
+        self.comment = Ms3dIo.read_string(raw_io, _comment_length)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_dword(raw_io, self.comment_length)
+        Ms3dIo.write_string(raw_io, self.comment_length, self.comment)
+
+
+###############################################################################
+class Ms3dVertexEx1:
+    """ Ms3dVertexEx1 """
+    __slots__ = (
+            '_bone_ids',
+            '_weights',
+            )
+
+    def __init__(
+            self,
+            default_bone_ids=(
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID),
+            default_weights=(100, 0, 0)
+            ):
+        self._bone_ids = default_bone_ids
+        self._weights = default_weights
+
+    def __repr__(self):
+        return "\n<bone_ids={}, weights={}>".format(
+                self.bone_ids,
+                self.weights
+                )
+
+
+    @property
+    def bone_ids(self):
+        return self._bone_ids
+
+    @property
+    def weights(self):
+        return self._weights
+
+
+    @property
+    def weight_bone_id(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights
+        return 100
+
+    @property
+    def weight_bone_id0(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights[0]
+        return 0
+
+    @property
+    def weight_bone_id1(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights[1]
+        return 0
+
+    @property
+    def weight_bone_id2(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return 100 - (self._weights[0] + self._weights[1] \
+                    + self._weights[2])
+        return 0
+
+
+    def read(self, raw_io):
+        self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3)
+        self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights)
+
+
+###############################################################################
+class Ms3dVertexEx2:
+    """ Ms3dVertexEx2 """
+    __slots__ = (
+            'extra',
+            '_bone_ids',
+            '_weights',
+            )
+
+    def __init__(
+            self,
+            default_bone_ids=(
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID),
+            default_weights=(100, 0, 0),
+            default_extra=0
+            ):
+        self._bone_ids = default_bone_ids
+        self._weights = default_weights
+        self.extra = default_extra
+
+    def __repr__(self):
+        return "\n<bone_ids={}, weights={}, extra={}>".format(
+                self.bone_ids,
+                self.weights,
+                self.extra
+                )
+
+
+    @property
+    def bone_ids(self):
+        return self._bone_ids
+
+    @property
+    def weights(self):
+        return self._weights
+
+
+    @property
+    def weight_bone_id(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights
+        return 100
+
+    @property
+    def weight_bone_id0(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights[0]
+        return 0
+
+    @property
+    def weight_bone_id1(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights[1]
+        return 0
+
+    @property
+    def weight_bone_id2(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return 100 - (self._weights[0] + self._weights[1] \
+                    + self._weights[2])
+        return 0
+
+
+    def read(self, raw_io):
+        self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3)
+        self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3)
+        self.extra = Ms3dIo.read_dword(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights)
+        Ms3dIo.write_dword(raw_io, self.extra)
+
+
+###############################################################################
+class Ms3dVertexEx3:
+    """ Ms3dVertexEx3 """
+    #char bone_ids[3]; // index of joint or -1, if -1, then that weight is
+    #    ignored, since subVersion 1
+    #byte weights[3]; // vertex weight ranging from 0 - 100, last weight is
+    #    computed by 1.0 - sum(all weights), since subVersion 1
+    #// weight[0] is the weight for bone_id in Ms3dVertex
+    #// weight[1] is the weight for bone_ids[0]
+    #// weight[2] is the weight for bone_ids[1]
+    #// 1.0f - weight[0] - weight[1] - weight[2] is the weight for bone_ids[2]
+    #unsigned int extra; // vertex extra, which can be used as color or
+    #    anything else, since subVersion 2
+    __slots__ = (
+            'extra',
+            '_bone_ids',
+            '_weights',
+            )
+
+    def __init__(
+            self,
+            default_bone_ids=(
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID,
+                    Ms3dSpec.DEFAULT_VERTEX_BONE_ID),
+            default_weights=(100, 0, 0),
+            default_extra=0
+            ):
+        self._bone_ids = default_bone_ids
+        self._weights = default_weights
+        self.extra = default_extra
+
+    def __repr__(self):
+        return "\n<bone_ids={}, weights={}, extra={}>".format(
+                self.bone_ids,
+                self.weights,
+                self.extra
+                )
+
+
+    @property
+    def bone_ids(self):
+        return self._bone_ids
+
+    @property
+    def weights(self):
+        return self._weights
+
+
+    @property
+    def weight_bone_id(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights
+        return 100
+
+    @property
+    def weight_bone_id0(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights[0]
+        return 0
+
+    @property
+    def weight_bone_id1(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return self._weights[1]
+        return 0
+
+    @property
+    def weight_bone_id2(self):
+        if self._weights[0] or self._weights[1] or self._weights[2]:
+            return 100 - (self._weights[0] + self._weights[1] \
+                    + self._weights[2])
+        return 0
+
+
+    def read(self, raw_io):
+        self._bone_ids = Ms3dIo.read_array(raw_io, Ms3dIo.read_sbyte, 3)
+        self._weights = Ms3dIo.read_array(raw_io, Ms3dIo.read_byte, 3)
+        self.extra = Ms3dIo.read_dword(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_sbyte, 3, self.bone_ids)
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_byte, 3, self.weights)
+        Ms3dIo.write_dword(raw_io, self.extra)
+
+
+###############################################################################
+class Ms3dJointEx:
+    """ Ms3dJointEx """
+    __slots__ = (
+            '_color',
+            )
+
+    def __init__(
+            self,
+            default_color=Ms3dSpec.DEFAULT_JOINT_COLOR
+            ):
+        self._color = default_color
+
+    def __repr__(self):
+        return "\n<color=({:.{p}f}, {:.{p}f}, {:.{p}f})>".format(
+                self.color[0],
+                self.color[1],
+                self.color[2],
+                p=Ms3dIo.PRECISION
+                )
+
+
+    @property
+    def color(self):
+        return self._color
+
+
+    def read(self, raw_io):
+        self._color = Ms3dIo.read_array(raw_io, Ms3dIo.read_float, 3)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_array(raw_io, Ms3dIo.write_float, 3, self.color)
+
+
+###############################################################################
+class Ms3dModelEx:
+    """ Ms3dModelEx """
+    __slots__ = (
+            'joint_size',
+            'transparency_mode',
+            'alpha_ref',
+            )
+
+    def __init__(
+            self,
+            default_joint_size=Ms3dSpec.DEFAULT_MODEL_JOINT_SIZE,
+            default_transparency_mode\
+                    =Ms3dSpec.DEFAULT_MODEL_TRANSPARENCY_MODE,
+            default_alpha_ref=0.0
+            ):
+        self.joint_size = default_joint_size
+        self.transparency_mode = default_transparency_mode
+        self.alpha_ref = default_alpha_ref
+
+    def __repr__(self):
+        return "\n<joint_size={:.{p}f}, transparency_mode={}, alpha_ref={:.{p}f}>".format(
+                self.joint_size,
+                self.transparency_mode,
+                self.alpha_ref,
+                p=Ms3dIo.PRECISION
+                )
+
+    def read(self, raw_io):
+        self.joint_size = Ms3dIo.read_float(raw_io)
+        self.transparency_mode = Ms3dIo.read_dword(raw_io)
+        self.alpha_ref = Ms3dIo.read_float(raw_io)
+        return self
+
+    def write(self, raw_io):
+        Ms3dIo.write_float(raw_io, self.joint_size)
+        Ms3dIo.write_dword(raw_io, self.transparency_mode)
+        Ms3dIo.write_float(raw_io, self.alpha_ref)
+
+
+###############################################################################
+#
+# file format
+#
+###############################################################################
+class Ms3dModel:
+    """ Ms3dModel """
+    __slot__ = (
+            'header',
+            'animation_fps',
+            'current_time',
+            'number_total_frames',
+            'sub_version_comments',
+            'sub_version_vertex_extra',
+            'sub_version_joint_extra',
+            'sub_version_model_extra',
+            'name',
+            '_vertices',
+            '_triangles',
+            '_groups',
+            '_materials',
+            '_joints',
+            '_has_model_comment',
+            '_comment_object', # Ms3dComment
+            '_model_ex_object', # Ms3dModelEx
+            )
+
+    def __init__(
+            self,
+            default_name=""
+            ):
+        if (default_name is None):
+            default_name = ""
+
+        self.name = default_name
+
+        self.animation_fps = Ms3dSpec.DEFAULT_MODEL_ANIMATION_FPS
+        self.current_time = 0.0
+        self.number_total_frames = 0
+        self.sub_version_comments \
+                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_COMMENTS
+        self.sub_version_vertex_extra \
+                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_VERTEX_EXTRA
+        self.sub_version_joint_extra \
+                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_JOINT_EXTRA
+        self.sub_version_model_extra \
+                = Ms3dSpec.DEFAULT_MODEL_SUB_VERSION_MODEL_EXTRA
+
+        self._vertices = [] #Ms3dVertex()
+        self._triangles = [] #Ms3dTriangle()
+        self._groups = [] #Ms3dGroup()
+        self._materials = [] #Ms3dMaterial()
+        self._joints = [] #Ms3dJoint()
+
+        self.header = Ms3dHeader()
+        self._model_ex_object = Ms3dModelEx()
+        self._comment_object = None #Ms3dComment()
+
+
+    @property
+    def number_vertices(self):
+        if self.vertices is None:
+            return 0
+        return len(self.vertices)
+
+    @property
+    def vertices(self):
+        return self._vertices
+
+
+    @property
+    def number_triangles(self):
+        if self.triangles is None:
+            return 0
+        return len(self.triangles)
+
+    @property
+    def triangles(self):
+        return self._triangles
+
+
+    @property
+    def number_groups(self):
+        if self.groups is None:
+            return 0
+        return len(self.groups)
+
+    @property
+    def groups(self):
+        return self._groups
+
+
+    @property
+    def number_materials(self):
+        if self.materials is None:
+            return 0
+        return len(self.materials)
+
+    @property
+    def materials(self):
+        return self._materials
+
+
+    @property
+    def number_joints(self):
+        if self.joints is None:
+            return 0
+        return len(self.joints)
+
+    @property
+    def joints(self):
+        return self._joints
+
+
+    @property
+    def number_group_comments(self):
+        if self.groups is None:
+            return 0
+        number = 0
+        for item in self.groups:
+            if item.comment_object is not None and item.comment_object.comment:
+                number += 1
+        return number
+
+    @property
+    def group_comments(self):
+        if self.groups is None:
+            return None
+        items = []
+        for item in self.groups:
+            if item.comment_object is not None and item.comment_object.comment:
+                items.append(item)
+        return items
+
+
+    @property
+    def number_material_comments(self):
+        if self.materials is None:
+            return 0
+        number = 0
+        for item in self.materials:
+            if item.comment_object is not None and item.comment_object.comment:
+                number += 1
+        return number
+
+    @property
+    def material_comments(self):
+        if self.materials is None:
+            return None
+        items = []
+        for item in self.materials:
+            if item.comment_object is not None and item.comment_object.comment:
+                items.append(item)
+        return items
+
+
+    @property
+    def number_joint_comments(self):
+        if self.joints is None:
+            return 0
+        number = 0
+        for item in self.joints:
+            if item.comment_object is not None and item.comment_object.comment:
+                number += 1
+        return number
+
+    @property
+    def joint_comments(self):
+        if self.joints is None:
+            return None
+        items = []
+        for item in self.joints:
+            if item.comment_object is not None and item.comment_object.comment:
+                items.append(item)
+        return items
+
+
+    @property
+    def has_model_comment(self):
+        if self.comment_object is not None and self.comment_object.comment:
+            return 1
+        return 0
+
+    @property
+    def comment_object(self):
+        return self._comment_object
+
+
+    @property
+    def vertex_ex(self):
+        if not self.sub_version_vertex_extra:
+            return None
+        return [item.vertex_ex_object for item in self.vertices]
+
+    @property
+    def joint_ex(self):
+        if not self.sub_version_joint_extra:
+            return None
+        return [item.joint_ex_object for item in self.joints]
+
+    @property
+    def model_ex_object(self):
+        if not self.sub_version_model_extra:
+            return None
+        return self._model_ex_object
+
+
+    def print_internal(self):
+        print()
+        print("##############################################################")
+        print("## the internal data of Ms3dModel object...")
+        print("##")
+
+        print("header={}".format(self.header))
+
+        print("number_vertices={}".format(self.number_vertices))
+        print("vertices=[", end="")
+        if self.vertices:
+            for obj in self.vertices:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("number_triangles={}".format(self.number_triangles))
+        print("triangles=[", end="")
+        if self.triangles:
+            for obj in self.triangles:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("number_groups={}".format(self.number_groups))
+        print("groups=[", end="")
+        if self.groups:
+            for obj in self.groups:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("number_materials={}".format(self.number_materials))
+        print("materials=[", end="")
+        if self.materials:
+            for obj in self.materials:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("animation_fps={}".format(self.animation_fps))
+        print("current_time={}".format(self.current_time))
+        print("number_total_frames={}".format(self.number_total_frames))
+
+        print("number_joints={}".format(self.number_joints))
+        print("joints=[", end="")
+        if self.joints:
+            for obj in self.joints:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("sub_version_comments={}".format(self.sub_version_comments))
+
+        print("number_group_comments={}".format(self.number_group_comments))
+        print("group_comments=[", end="")
+        if self.group_comments:
+            for obj in self.group_comments:
+                print("{}".format(obj.comment_object), end="")
+        print("]")
+
+        print("number_material_comments={}".format(
+                self.number_material_comments))
+        print("material_comments=[", end="")
+        if self.material_comments:
+            for obj in self.material_comments:
+                print("{}".format(obj.comment_object), end="")
+        print("]")
+
+        print("number_joint_comments={}".format(self.number_joint_comments))
+        print("joint_comments=[", end="")
+        if self.joint_comments:
+            for obj in self.joint_comments:
+                print("{}".format(obj.comment_object), end="")
+        print("]")
+
+        print("has_model_comment={}".format(self.has_model_comment))
+        print("model_comment={}".format(self.comment_object))
+
+        print("sub_version_vertex_extra={}".format(
+                self.sub_version_vertex_extra))
+        print("vertex_ex=[", end="")
+        if self.vertex_ex:
+            for obj in self.vertex_ex:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("sub_version_joint_extra={}".format(
+                self.sub_version_joint_extra))
+        print("joint_ex=[", end="")
+        if self.joint_ex:
+            for obj in self.joint_ex:
+                print("{}".format(obj), end="")
+        print("]")
+
+        print("sub_version_model_extra={}".format(
+                self.sub_version_model_extra))
+        print("model_ex={}".format(self.model_ex_object))
+
+        print("##")
+        print("## ...end")
+        print("##############################################################")
+        print()
+
+
+    def read(self, raw_io):
+        """
+        opens, reads and pars MS3D file.
+        add content to blender scene
+        """
+
+        self.header.read(raw_io)
+        if (self.header != Ms3dHeader()):
+            print("\nwarning, invalid file header")
+
+        _number_vertices = Ms3dIo.read_word(raw_io)
+        if (_number_vertices > Ms3dSpec.MAX_VERTICES):
+            print("\nwarning, invalid count: number_vertices: {}".format(
+                    _number_vertices))
+        self._vertices = []
+        for i in range(_number_vertices):
+            self.vertices.append(Ms3dVertex().read(raw_io))
+
+        _number_triangles = Ms3dIo.read_word(raw_io)
+        if (_number_triangles > Ms3dSpec.MAX_TRIANGLES):
+            print("\nwarning, invalid count: number_triangles: {}".format(
+                    _number_triangles))
+        self._triangles = []
+        for i in range(_number_triangles):
+            self.triangles.append(Ms3dTriangle().read(raw_io))
+
+        _number_groups = Ms3dIo.read_word(raw_io)
+        if (_number_groups > Ms3dSpec.MAX_GROUPS):
+            print("\nwarning, invalid count: number_groups: {}".format(
+                    _number_groups))
+        self._groups = []
+        for i in range(_number_groups):
+            self.groups.append(Ms3dGroup().read(raw_io))
+
+        _number_materials = Ms3dIo.read_word(raw_io)
+        if (_number_materials > Ms3dSpec.MAX_MATERIALS):
+            print("\nwarning, invalid count: number_materials: {}".format(
+                    _number_materials))
+        self._materials = []
+        for i in range(_number_materials):
+            self.materials.append(Ms3dMaterial().read(raw_io))
+
+        self.animation_fps = Ms3dIo.read_float(raw_io)
+        self.current_time = Ms3dIo.read_float(raw_io)
+        self.number_total_frames = Ms3dIo.read_dword(raw_io)
+
+        _progress = set()
+
+        try:
+            # optional data
+            # doesn't matter if doesn't existing.
+
+            _number_joints = Ms3dIo.read_word(raw_io)
+            _progress.add('NUMBER_JOINTS')
+            if (_number_joints > Ms3dSpec.MAX_JOINTS):
+                print("\nwarning, invalid count: number_joints: {}".format(
+                        _number_joints))
+            self._joints = []
+            for i in range(_number_joints):
+                self.joints.append(Ms3dJoint().read(raw_io))
+            _progress.add('JOINTS')
+
+            self.sub_version_comments = Ms3dIo.read_dword(raw_io)
+            _progress.add('SUB_VERSION_COMMENTS')
+            _number_group_comments = Ms3dIo.read_dword(raw_io)
+            _progress.add('NUMBER_GROUP_COMMENTS')
+            if (_number_group_comments > Ms3dSpec.MAX_GROUPS):
+                print("\nwarning, invalid count:"\
+                        " number_group_comments: {}".format(
+                        _number_group_comments))
+            if _number_group_comments > _number_groups:
+                print("\nwarning, invalid count:"\
+                        " number_group_comments: {}, number_groups: {}".format(
+                        _number_group_comments, _number_groups))
+            for i in range(_number_group_comments):
+                item = Ms3dCommentEx().read(raw_io)
+                if item.index >= 0 and item.index < _number_groups:
+                    self.groups[item.index]._comment_object = item
+                else:
+                    print("\nwarning, invalid index:"\
+                            " group_index: {}, number_groups: {}".format(
+                            item.index, _number_groups))
+            _progress.add('GROUP_COMMENTS')
+
+            _number_material_comments = Ms3dIo.read_dword(raw_io)
+            _progress.add('NUMBER_MATERIAL_COMMENTS')
+            if (_number_material_comments > Ms3dSpec.MAX_MATERIALS):
+                print("\nwarning, invalid count:"\
+                        " number_material_comments: {}".format(
+                        _number_material_comments))
+            if _number_material_comments > _number_materials:
+                print("\nwarning, invalid count:"\
+                        " number_material_comments:"\
+                        " {}, number_materials: {}".format(
+                        _number_material_comments, _number_materials))
+            for i in range(_number_material_comments):
+                item = Ms3dCommentEx().read(raw_io)
+                if item.index >= 0 and item.index < _number_materials:
+                    self.materials[item.index]._comment_object = item
+                else:
+                    print("\nwarning, invalid index:"\
+                            " material_index: {}, number_materials:"\
+                            " {}".format(item.index, _number_materials))
+            _progress.add('MATERIAL_COMMENTS')
+
+            _number_joint_comments = Ms3dIo.read_dword(raw_io)
+            _progress.add('NUMBER_JOINT_COMMENTS')
+            if (_number_joint_comments > Ms3dSpec.MAX_JOINTS):
+                print("\nwarning, invalid count:"\
+                        " number_joint_comments: {}".format(
+                        _number_joint_comments))
+            if _number_joint_comments > _number_joints:
+                print("\nwarning, invalid count:"\
+                        " number_joint_comments: {}, number_joints: {}".format(
+                        _number_joint_comments, _number_joints))
+            for i in range(_number_joint_comments):
+                item = Ms3dCommentEx().read(raw_io)
+                if item.index >= 0 and item.index < _number_joints:
+                    self.joints[item.index]._comment_object = item
+                else:
+                    print("\nwarning, invalid index:"\
+                            " joint_index: {}, number_joints: {}".format(
+                            item.index, _number_joints))
+            _progress.add('JOINT_COMMENTS')
+
+            _has_model_comment = Ms3dIo.read_dword(raw_io)
+            _progress.add('HAS_MODEL_COMMENTS')
+            if (_has_model_comment != 0):
+                self._comment_object = Ms3dComment().read(raw_io)
+            else:
+                self._comment_object = None
+            _progress.add('MODEL_COMMENTS')
+
+            self.sub_version_vertex_extra = Ms3dIo.read_dword(raw_io)
+            _progress.add('SUB_VERSION_VERTEX_EXTRA')
+            if self.sub_version_vertex_extra > 0:
+                length = len(self.joints)
+                for i in range(_number_vertices):
+                    if self.sub_version_vertex_extra == 1:
+                        item = Ms3dVertexEx1()
+                    elif self.sub_version_vertex_extra == 2:
+                        item = Ms3dVertexEx2()
+                    elif self.sub_version_vertex_extra == 3:
+                        item = Ms3dVertexEx3()
+                    else:
+                        print("\nwarning, invalid version:"\
+                                " sub_version_vertex_extra: {}".format(
+                                sub_version_vertex_extra))
+                        continue
+                    self.vertices[i]._vertex_ex_object = item.read(raw_io)
+            _progress.add('VERTEX_EXTRA')
+
+            self.sub_version_joint_extra = Ms3dIo.read_dword(raw_io)
+            _progress.add('SUB_VERSION_JOINT_EXTRA')
+            if self.sub_version_joint_extra > 0:
+                for i in range(_number_joints):
+                    self.joints[i]._joint_ex_object = Ms3dJointEx().read(raw_io)
+            _progress.add('JOINT_EXTRA')
+
+            self.sub_version_model_extra = Ms3dIo.read_dword(raw_io)
+            _progress.add('SUB_VERSION_MODEL_EXTRA')
+            if self.sub_version_model_extra > 0:
+                self._model_ex_object.read(raw_io)
+            _progress.add('MODEL_EXTRA')
+
+        except EOFError:
+            # reached end of optional data.
+            print("Ms3dModel.read - optional data read: {}".format(_progress))
+            pass
+
+        except Exception:
+            type, value, traceback = exc_info()
+            print("Ms3dModel.read - exception in optional try block,"
+                    " _progress={0}\n  type: '{1}'\n  value: '{2}'".format(
+                    _progress, type, value, traceback))
+
+        else:
+            pass
+
+        # try best to continue far as possible
+        if not 'JOINTS' in _progress:
+            _number_joints = 0
+            self._joints = []
+
+        if not 'GROUP_COMMENTS' in _progress:
+            self.sub_version_comments = 0
+            _number_group_comments = 0
+
+        if not 'MATERIAL_COMMENTS' in _progress:
+            _number_material_comments = 0
+
+        if not 'JOINT_COMMENTS' in _progress:
+            _number_joint_comments = 0
+
+        if not 'MODEL_COMMENTS' in _progress:
+            _has_model_comment = 0
+            self._comment_object = None # Ms3dComment()
+
+        if not 'VERTEX_EXTRA' in _progress:
+            self.sub_version_vertex_extra = 0
+
+        if not 'JOINT_EXTRA' in _progress:
+            self.sub_version_joint_extra = 0
+
+        if not 'MODEL_EXTRA' in _progress:
+            self.sub_version_model_extra = 0
+            self._model_ex_object = Ms3dModelEx()
+
+        return
+
+
+    def write(self, raw_io):
+        """
+        add blender scene content to MS3D
+        creates, writes MS3D file.
+        """
+
+        self.header.write(raw_io)
+
+        Ms3dIo.write_word(raw_io, self.number_vertices)
+        for i in range(self.number_vertices):
+            self.vertices[i].write(raw_io)
+
+        Ms3dIo.write_word(raw_io, self.number_triangles)
+        for i in range(self.number_triangles):
+            self.triangles[i].write(raw_io)
+
+        Ms3dIo.write_word(raw_io, self.number_groups)
+        for i in range(self.number_groups):
+            self.groups[i].write(raw_io)
+
+        Ms3dIo.write_word(raw_io, self.number_materials)
+        for i in range(self.number_materials):
+            self.materials[i].write(raw_io)
+
+        Ms3dIo.write_float(raw_io, self.animation_fps)
+        Ms3dIo.write_float(raw_io, self.current_time)
+        Ms3dIo.write_dword(raw_io, self.number_total_frames)
+
+        try:
+            # optional part
+            # doesn't matter if it doesn't complete.
+            Ms3dIo.write_word(raw_io, self.number_joints)
+            for i in range(self.number_joints):
+                self.joints[i].write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.sub_version_comments)
+
+            Ms3dIo.write_dword(raw_io, self.number_group_comments)
+            for i in range(self.number_group_comments):
+                self.group_comments[i].comment_object.write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.number_material_comments)
+            for i in range(self.number_material_comments):
+                self.material_comments[i].comment_object.write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.number_joint_comments)
+            for i in range(self.number_joint_comments):
+                self.joint_comments[i].comment_object.write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.has_model_comment)
+            if (self.has_model_comment != 0):
+                self.comment_object.write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.sub_version_vertex_extra)
+            if (self.sub_version_vertex_extra in {1, 2, 3}):
+                for i in range(self.number_vertices):
+                    self.vertex_ex[i].write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.sub_version_joint_extra)
+            for i in range(self.number_joints):
+                self.joint_ex[i].write(raw_io)
+
+            Ms3dIo.write_dword(raw_io, self.sub_version_model_extra)
+            self.model_ex_object.write(raw_io)
+
+        except Exception:
+            type, value, traceback = exc_info()
+            print("Ms3dModel.write - exception in optional try block"
+                    "\n  type: '{0}'\n  value: '{1}'".format(
+                    type, value, traceback))
+            pass
+
+        else:
+            pass
+
+        return
+
+
+    def is_valid(self):
+        valid = True
+        result = []
+
+        format1 = "\n  number of {0}: {1}"
+        format2 = " limit exceeded! (limit is {0})"
+
+        result.append("MS3D statistics:")
+        result.append(format1.format("vertices ........",
+                self.number_vertices))
+        if (self.number_vertices > Ms3dSpec.MAX_VERTICES):
+            result.append(format2.format(Ms3dSpec.MAX_VERTICES))
+            valid &= False
+
+        result.append(format1.format("triangles .......",
+                self.number_triangles))
+        if (self.number_triangles > Ms3dSpec.MAX_TRIANGLES):
+            result.append(format2.format(Ms3dSpec.MAX_TRIANGLES))
+            valid &= False
+
+        result.append(format1.format("groups ..........",
+                self.number_groups))
+        if (self.number_groups > Ms3dSpec.MAX_GROUPS):
+            result.append(format2.format(Ms3dSpec.MAX_GROUPS))
+            valid &= False
+
+        result.append(format1.format("materials .......",
+                self.number_materials))
+        if (self.number_materials > Ms3dSpec.MAX_MATERIALS):
+            result.append(format2.format(Ms3dSpec.MAX_MATERIALS))
+            valid &= False
+
+        result.append(format1.format("joints ..........",
+                self.number_joints))
+        if (self.number_joints > Ms3dSpec.MAX_JOINTS):
+            result.append(format2.format(Ms3dSpec.MAX_JOINTS))
+            valid &= False
+
+        result.append(format1.format("model comments ..",
+                self.has_model_comment))
+        result.append(format1.format("group comments ..",
+                self.number_group_comments))
+        result.append(format1.format("material comments",
+                self.number_material_comments))
+        result.append(format1.format("joint comments ..",
+                self.number_joint_comments))
+
+        #if (not valid):
+        #    result.append("\n\nthe data may be corrupted.")
+
+        return (valid, ("".join(result)))
+
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####
diff --git a/io_scene_ms3d/ms3d_strings.py b/io_scene_ms3d/ms3d_strings.py
new file mode 100644
index 0000000000000000000000000000000000000000..84a5a5681e75f0da8179805eef8aa45f45952396
--- /dev/null
+++ b/io_scene_ms3d/ms3d_strings.py
@@ -0,0 +1,263 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+SEE_MS3D_DOC = "see MilkShape 3D documentation"
+
+ms3d_str = {
+        'lang': "en-US",
+        'RUNTIME_KEY': "Human friendly presentation",
+
+        ###############################
+        # blender key names
+        'OBJECT_LAYER_EXTRA': "ms3d_extra_layer",
+        'OBJECT_LAYER_GROUP': "ms3d_group_layer",
+        'OBJECT_LAYER_SMOOTHING_GROUP': "ms3d_smoothing_group_layer",
+        'OBJECT_MODIFIER_SMOOTHING_GROUP': "ms3d_smoothing_groups",
+        # for some reason after bm.to_mesh(..)
+        # the names of 'bm.loops.layers.uv' becomes to 'bm.faces.layers.tex'
+        # to bypass this issue, i give both the same name.
+        # 'OBJECT_LAYER_TEXTURE': "ms3d_texture_layer",
+        'OBJECT_LAYER_TEXTURE': "ms3d_uv_layer",
+        'OBJECT_LAYER_UV': "ms3d_uv_layer",
+
+        ###############################
+        # strings to be used with 'str().format()'
+        'STRING_FORMAT_GROUP': "Group.{:03d}",
+        'WARNING_IMPORT_SKIP_FACE_DOUBLE': "skipped face #{}:"\
+                " contains double faces with same vertices!",
+        'WARNING_IMPORT_SKIP_LESS_VERTICES': "skipped face #{}:"\
+                " contains faces too less vertices!",
+        'WARNING_IMPORT_SKIP_VERTEX_DOUBLE': "skipped face #{}:"\
+                " contains faces with double vertices!",
+        'WARNING_IMPORT_EXTRA_VERTEX_NORMAL': "created extra vertex"\
+                " because of different normals #{} -> {}.",
+        'SUMMARY_IMPORT': "elapsed time: {0:.4}s (media io:"\
+                " ~{1:.4}s, converter: ~{2:.4}s)",
+        'SUMMARY_EXPORT': "elapsed time: {0:.4}s (converter:"\
+                " ~{1:.4}s, media io: ~{2:.4}s)",
+        'WARNING_EXPORT_SKIP_WEIGHT' : "skipped weight",
+        'WARNING_EXPORT_SKIP_WEIGHT_EX' : "skipped weight:"\
+                " limit exceeded",
+
+        ###############################
+        'TEXT_OPERATOR': "MilkShape 3D (.ms3d)",
+        'FILE_EXT': ".ms3d",
+        'FILE_FILTER': "*.ms3d",
+        'BL_DESCRIPTION_EXPORTER': "Export to a MilkShape 3D file format (.ms3d)",
+        'BL_DESCRIPTION_IMPORTER': "Import from a MilkShape 3D file format (.ms3d)",
+        'BL_LABEL_EXPORTER': "Export MS3D",
+        'BL_LABEL_GROUP_OPERATOR': "MS3D - Group Collection Operator",
+        'BL_LABEL_IMPORTER': "Import MS3D",
+        'BL_LABEL_PANEL_SMOOTHING_GROUP': "MS3D - Smoothing Group",
+        'BL_LABEL_SMOOTHING_GROUP_OPERATOR': "MS3D Set Smoothing Group"\
+                " Operator",
+        'BL_LABEL_MATERIAL_OPERATOR' : "MS3D - Copy Material Operator",
+        'ENUM_ADD_GROUP_1': "Add",
+        'ENUM_ADD_GROUP_2': "adds an item",
+        'ENUM_ASSIGN_1': "Assign",
+        'ENUM_ASSIGN_2_GROUP': "assign selected faces to selected group",
+        'ENUM_ASSIGN_2_SMOOTHING_GROUP': "assign all selected faces to"\
+                " selected smoothing group",
+        'ENUM_DESELECT_1': "Deselect",
+        'ENUM_DESELECT_2_GROUP': "deselects faces of selected group",
+        'ENUM_DESELECT_2_SMOOTHING_GROUP': "deselects all faces of selected"\
+                " smoothing group",
+        'ENUM_FLAG_DIRTY_1': "Dirty",
+        'ENUM_FLAG_DIRTY_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_HIDDEN_1': "Hidden",
+        'ENUM_FLAG_HIDDEN_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_ISKEY_1': "Is Key",
+        'ENUM_FLAG_ISKEY_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_MARKED_1': "Marked",
+        'ENUM_FLAG_MARKED_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_NEWLYCREATED_1': "Newly Created",
+        'ENUM_FLAG_NEWLYCREATED_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_NONE_1': "None",
+        'ENUM_FLAG_NONE_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_SELECTED_1': "Selected",
+        'ENUM_FLAG_SELECTED_2': SEE_MS3D_DOC,
+        'ENUM_FLAG_SELECTED2_1': "Selected Ex.",
+        'ENUM_FLAG_SELECTED2_2': SEE_MS3D_DOC,
+        'ENUM_REMOVE_1': "Remove",
+        'ENUM_REMOVE_2_GROUP': "remove selected faces from selected group",
+        'ENUM_REMOVE_GROUP_1': "Remove",
+        'ENUM_REMOVE_GROUP_2': "removes an item",
+        'ENUM_SELECT_1': "Select",
+        'ENUM_SELECT_2_GROUP': "selects faces of selected group",
+        'ENUM_SELECT_2_SMOOTHING_GROUP': "selects all faces of selected"\
+                " smoothing group",
+        'LABEL_NAME_ANIMATION': "Animation Processing:",
+        'LABEL_NAME_OBJECT': "World Processing:",
+        'LABEL_NAME_OPTIONS': "Advanced Options:",
+        'LABEL_NAME_PROCESSING': "Object Processing:",
+        'LABEL_NAME_MODIFIER': "Modifier Processing:",
+        'LABEL_PANEL_BUTTON_NONE': "None",
+        'LABEL_PANEL_GROUPS': "MS3D - Groups",
+        'LABEL_PANEL_JOINTS': "MS3D - Joint",
+        'LABEL_PANEL_MATERIALS': "MS3D - Material",
+        'LABEL_PANEL_MODEL': "MS3D - Model",
+        'PROP_DESC_ALPHA_REF': "ms3d internal raw 'alpha_ref' of Model",
+        'PROP_DESC_ALPHAMAP': "ms3d internal raw 'alphamap' file name of"\
+                " Material",
+        'PROP_DESC_AMBIENT': "ms3d internal raw 'ambient' of Material",
+        'PROP_DESC_USE_ANIMATION': "keyframes (rotations, positions)",
+        'PROP_DESC_COLOR_JOINT': "ms3d internal raw 'color' of Joint",
+        'PROP_DESC_COMMENT_GROUP': "ms3d internal raw 'comment' of Group",
+        'PROP_DESC_COMMENT_JOINT': "ms3d internal raw 'comment' of Joint",
+        'PROP_DESC_COMMENT_MATERIAL': "ms3d internal raw 'comment' of Material",
+        'PROP_DESC_COMMENT_MODEL': "ms3d internal raw 'comment' of Model",
+        'PROP_DESC_DIFFUSE': "ms3d internal raw 'diffuse' of Material",
+        'PROP_DESC_EMISSIVE': "ms3d internal raw 'emissive' of Material",
+        'PROP_DESC_FLAGS_GROUP': "ms3d internal raw 'flags' of Group",
+        'PROP_DESC_FLAGS_JOINT': "ms3d internal raw 'flags' of Joint",
+        'PROP_DESC_GROUP_NAME': "ms3d internal raw 'name' of Group",
+        'PROP_DESC_JOINT_SIZE': "ms3d internal raw 'joint_size' of Model",
+        'PROP_DESC_MODE_TEXTURE': "ms3d internal raw 'mode' of Material",
+        'PROP_DESC_NAME_ARMATURE': "ms3d internal raw 'name' of Model (not used"\
+                " for export)",
+        'PROP_DESC_NAME_JOINT': "ms3d internal raw 'name' of Joint",
+        'PROP_DESC_NAME_MATERIAL': "ms3d internal raw 'name' of Material",
+        'PROP_DESC_NAME_MODEL': "ms3d internal raw 'name' of Model (not used for export)",
+        'PROP_DESC_SHININESS': "ms3d internal raw 'shininess' of Material",
+        'PROP_DESC_SPECULAR': "ms3d internal raw 'specular' of Material",
+        'PROP_DESC_TEXTURE': "ms3d internal raw 'texture' file name of"\
+                " Material",
+        'PROP_DESC_TRANSPARENCY': "ms3d internal raw 'transparency' of"\
+                " Material",
+        'PROP_DESC_TRANSPARENCY_MODE': "ms3d internal raw 'transparency_mode'"\
+                " of Model",
+        'PROP_DESC_VERBOSE': "Run the converter in debug mode."\
+                " Check the console for output (Warning, may be very slow)",
+        'PROP_FLAG_TEXTURE_COMBINE_ALPHA_1': "Combine Alpha",
+        'PROP_FLAG_TEXTURE_COMBINE_ALPHA_2': SEE_MS3D_DOC,
+        'PROP_FLAG_TEXTURE_HAS_ALPHA_1': "Has Alpha",
+        'PROP_FLAG_TEXTURE_HAS_ALPHA_2': SEE_MS3D_DOC,
+        'PROP_FLAG_TEXTURE_SPHERE_MAP_1': "Sphere Map",
+        'PROP_FLAG_TEXTURE_SPHERE_MAP_2': SEE_MS3D_DOC,
+        'PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_1': "Depth"\
+                " Buffered with Alpha Ref",
+        'PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_2': SEE_MS3D_DOC,
+        'PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_1': "Depth Sorted"\
+                " Triangles",
+        'PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_2': SEE_MS3D_DOC,
+        'PROP_MODE_TRANSPARENCY_SIMPLE_1': "Simple",
+        'PROP_MODE_TRANSPARENCY_SIMPLE_2': SEE_MS3D_DOC,
+        'PROP_NAME_ALPHA_REF': "Alpha Ref.",
+        'PROP_NAME_ALPHAMAP': "Alphamap",
+        'PROP_NAME_AMBIENT': "Ambient",
+        'PROP_NAME_USE_ANIMATION': "Animation",
+        'PROP_NAME_COLOR': "Color",
+        'PROP_NAME_COMMENT': "Comment",
+        'PROP_NAME_DIFFUSE': "Diffuse",
+        'PROP_NAME_EMISSIVE': "Emissive",
+        'PROP_NAME_FLAGS': "Flags",
+        'PROP_NAME_JOINT_SIZE': "Joint Size",
+        'PROP_NAME_MODE': "Mode",
+        'PROP_NAME_NAME': "Name",
+        'PROP_NAME_ACTIVE': "Active Mesh:",
+        'PROP_NAME_SHININESS': "Shininess",
+        'PROP_NAME_SPECULAR': "Specular",
+        'PROP_NAME_TEXTURE': "Texture",
+        'PROP_NAME_TRANSPARENCY': "Transparency",
+        'PROP_NAME_TRANSPARENCY_MODE': "Transp. Mode",
+        'PROP_NAME_VERBOSE': "Verbose",
+        'PROP_SMOOTHING_GROUP_INDEX': "Smoothing group id",
+        'PROP_NAME_ROTATION_MODE' : "Bone Rotation Mode",
+        'PROP_DESC_ROTATION_MODE' : "set the preferred rotation mode of bones",
+        'PROP_ITEM_ROTATION_MODE_EULER_1' : "Euler",
+        'PROP_ITEM_ROTATION_MODE_EULER_2' : "use euler bone rotation"\
+                " (gimbal-lock can be fixed by using "\
+                "'Graph Editor -> Key -> Discontinuity (Euler) Filter')",
+        'PROP_ITEM_ROTATION_MODE_QUATERNION_1' : "Quaternion",
+        'PROP_ITEM_ROTATION_MODE_QUATERNION_2' : "use quaternion bone rotation"\
+                " (no gimbal-lock filter available!)",
+        'PROP_NAME_USE_JOINT_SIZE': "Override Joint Size",
+        'PROP_DESC_USE_JOINT_SIZE': "use value of 'Joint Size', the value of the"\
+                " ms3d file is ignored for representation.",
+        'PROP_NAME_IMPORT_JOINT_SIZE': "Joint Size",
+        'PROP_DESC_IMPORT_JOINT_SIZE': "size of the joint representation in"\
+                " blender",
+        'BL_LABEL_SET_SCENE_TO_METRIC' : "Set Scene to 'Metric' [1 mm]",
+        'BL_DESC_SET_SCENE_TO_METRIC' : "set Scene | Units to Metric"\
+                " (1 Unit = 1 mm),"\
+                " Display | Textured Solid,"\
+                " View | Clip (0.001 mm ... 1 km)",
+        'PROP_NAME_NORMALIZE_WEIGHTS' : "Normalize Weights",
+        'PROP_DESC_NORMALIZE_WEIGHTS' : "normalize weights to 100%,"\
+                " when its sum of weights is greater than 100%",
+        'PROP_NAME_SHRINK_TO_KEYS' : "Shrink To Keys",
+        'PROP_DESC_SHRINK_TO_KEYS' : "shrinks the animation to region from"\
+                " first keyframe to last keyframe",
+        'PROP_NAME_BAKE_EACH_FRAME' : "Bake Each Frame As Key",
+        'PROP_DESC_BAKE_EACH_FRAME' : "if enabled, to each frame there will be"\
+                " a key baked",
+        'LABEL_NAME_JOINT_TO_BONES' : "works only with some models!",
+        'PROP_NAME_JOINT_TO_BONES' : "Joints To Bones",
+        'PROP_DESC_JOINT_TO_BONES' : "changes the length of the bones",
+        'PROP_NAME_USE_BLENDER_NAMES' : "Use Blender Names Only",
+        'PROP_DESC_USE_BLENDER_NAMES' : "use only blender names, ignores ms3d"\
+                " names (bone names will always be taken from blender)",
+        'PROP_NAME_USE_BLENDER_MATERIALS' : "Use Blender Materials",
+        'PROP_DESC_USE_BLENDER_MATERIALS' : "ignores ms3d material definition"\
+                " (you loose some information by choosing this option)",
+        'ENUM_FROM_BLENDER_1' : "Copy From Blender",
+        'ENUM_FROM_BLENDER_2' : "takes and copies all available values from"\
+                " blender",
+        'ENUM_TO_BLENDER_1' : "Copy To Blender",
+        'ENUM_TO_BLENDER_2' : "copies and puts all available values to blender",
+        'PROP_NAME_EXTENDED_NORMAL_HANDLING': "Extended Normal Handling",
+        'PROP_DESC_EXTENDED_NORMAL_HANDLING': "adds extra vertices if normals"\
+                " are different",
+        'PROP_NAME_APPLY_TRANSFORM': "Apply Transform",
+        'PROP_DESC_APPLY_TRANSFORM': "applies location, rotation and scale on"\
+                " export",
+        'PROP_NAME_APPLY_MODIFIERS': "Apply Modifiers",
+        'PROP_DESC_APPLY_MODIFIERS': "applies modifiers on export that are"\
+                " enabled (except of armature modifiers)",
+        'PROP_NAME_APPLY_MODIFIERS_MODE': "Apply Mode",
+        'PROP_DESC_APPLY_MODIFIERS_MODE': "apply modifier, if enabled in its"\
+                " mode",
+        'PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_1': "View",
+        'PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_2': "apply modifiers that are"\
+                " enabled in viewport",
+        'PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_1': "Render",
+        'PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_2': "apply modifiers that are"\
+                " enabled in renderer",
+
+        'PROP_NAME_': "Name",
+        'PROP_DESC_': "Description",
+        # ms3d_str['']
+        }
+
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####
diff --git a/io_scene_ms3d/ms3d_ui.py b/io_scene_ms3d/ms3d_ui.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3e52b1e8aa07153a198f7acd58ba176b69bbc3a
--- /dev/null
+++ b/io_scene_ms3d/ms3d_ui.py
@@ -0,0 +1,1723 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+
+
+#import python stuff
+from random import (
+        randrange,
+        )
+
+
+# import io_scene_ms3d stuff
+from io_scene_ms3d.ms3d_strings import (
+        ms3d_str,
+        )
+from io_scene_ms3d.ms3d_spec import (
+        Ms3dSpec,
+        )
+from io_scene_ms3d.ms3d_utils import (
+        enable_edit_mode,
+        get_edge_split_modifier_add_if,
+        set_sence_to_metric,
+        )
+
+
+#import blender stuff
+from bmesh import (
+        from_edit_mesh,
+        )
+from bpy.utils import (
+        register_class,
+        unregister_class,
+        )
+from bpy_extras.io_utils import (
+        ExportHelper,
+        ImportHelper,
+        )
+from bpy.props import (
+        BoolProperty,
+        CollectionProperty,
+        EnumProperty,
+        FloatProperty,
+        FloatVectorProperty,
+        IntProperty,
+        StringProperty,
+        PointerProperty,
+        )
+from bpy.types import (
+        Operator,
+        PropertyGroup,
+        Panel,
+        Armature,
+        Bone,
+        Mesh,
+        Material,
+        Action,
+        Group,
+        UIList,
+        )
+from bpy.app import (
+        debug,
+        )
+
+
+class Ms3dUi:
+    DEFAULT_VERBOSE = debug
+
+    ###########################################################################
+    FLAG_TEXTURE_COMBINE_ALPHA = 'COMBINE_ALPHA'
+    FLAG_TEXTURE_HAS_ALPHA = 'HAS_ALPHA'
+    FLAG_TEXTURE_SPHERE_MAP = 'SPHERE_MAP'
+
+    @staticmethod
+    def texture_mode_from_ms3d(ms3d_value):
+        ui_value = set()
+        if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA) \
+                == Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA:
+            ui_value.add(Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA)
+        if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA) \
+                == Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA:
+            ui_value.add(Ms3dUi.FLAG_TEXTURE_HAS_ALPHA)
+        if (ms3d_value & Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP) \
+                == Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP:
+            ui_value.add(Ms3dUi.FLAG_TEXTURE_SPHERE_MAP)
+        return ui_value
+
+    @staticmethod
+    def texture_mode_to_ms3d(ui_value):
+        ms3d_value = Ms3dSpec.FLAG_TEXTURE_NONE
+
+        if Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA
+        if Ms3dUi.FLAG_TEXTURE_HAS_ALPHA in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA
+        if Ms3dUi.FLAG_TEXTURE_SPHERE_MAP in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP
+        return ms3d_value
+
+
+    MODE_TRANSPARENCY_SIMPLE = 'SIMPLE'
+    MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF \
+            = 'DEPTH_BUFFERED_WITH_ALPHA_REF'
+    MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES = 'DEPTH_SORTED_TRIANGLES'
+
+    @staticmethod
+    def transparency_mode_from_ms3d(ms3d_value):
+        if(ms3d_value == Ms3dSpec.MODE_TRANSPARENCY_SIMPLE):
+            return Ms3dUi.MODE_TRANSPARENCY_SIMPLE
+        elif(ms3d_value == \
+                Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF):
+            return Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF
+        elif(ms3d_value == Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES):
+            return Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES
+        return None
+
+    @staticmethod
+    def transparency_mode_to_ms3d(ui_value):
+        if(ui_value == Ms3dUi.MODE_TRANSPARENCY_SIMPLE):
+            return Ms3dSpec.MODE_TRANSPARENCY_SIMPLE
+        elif(ui_value == Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF):
+            return Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF
+        elif(ui_value == Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES):
+            return Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES
+        return None
+
+
+    FLAG_NONE = 'NONE'
+    FLAG_SELECTED = 'SELECTED'
+    FLAG_HIDDEN = 'HIDDEN'
+    FLAG_SELECTED2 = 'SELECTED2'
+    FLAG_DIRTY = 'DIRTY'
+    FLAG_ISKEY = 'ISKEY'
+    FLAG_NEWLYCREATED = 'NEWLYCREATED'
+    FLAG_MARKED = 'MARKED'
+
+    @staticmethod
+    def flags_from_ms3d(ms3d_value):
+        ui_value = set()
+        if (ms3d_value & Ms3dSpec.FLAG_SELECTED) == Ms3dSpec.FLAG_SELECTED:
+            ui_value.add(Ms3dUi.FLAG_SELECTED)
+        if (ms3d_value & Ms3dSpec.FLAG_HIDDEN) == Ms3dSpec.FLAG_HIDDEN:
+            ui_value.add(Ms3dUi.FLAG_HIDDEN)
+        if (ms3d_value & Ms3dSpec.FLAG_SELECTED2) == Ms3dSpec.FLAG_SELECTED2:
+            ui_value.add(Ms3dUi.FLAG_SELECTED2)
+        if (ms3d_value & Ms3dSpec.FLAG_DIRTY) == Ms3dSpec.FLAG_DIRTY:
+            ui_value.add(Ms3dUi.FLAG_DIRTY)
+        if (ms3d_value & Ms3dSpec.FLAG_ISKEY) == Ms3dSpec.FLAG_ISKEY:
+            ui_value.add(Ms3dUi.FLAG_ISKEY)
+        if (ms3d_value & Ms3dSpec.FLAG_NEWLYCREATED) == \
+                Ms3dSpec.FLAG_NEWLYCREATED:
+            ui_value.add(Ms3dUi.FLAG_NEWLYCREATED)
+        if (ms3d_value & Ms3dSpec.FLAG_MARKED) == Ms3dSpec.FLAG_MARKED:
+            ui_value.add(Ms3dUi.FLAG_MARKED)
+        return ui_value
+
+    @staticmethod
+    def flags_to_ms3d(ui_value):
+        ms3d_value = Ms3dSpec.FLAG_NONE
+        if Ms3dUi.FLAG_SELECTED in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_SELECTED
+        if Ms3dUi.FLAG_HIDDEN in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_HIDDEN
+        if Ms3dUi.FLAG_SELECTED2 in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_SELECTED2
+        if Ms3dUi.FLAG_DIRTY in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_DIRTY
+        if Ms3dUi.FLAG_ISKEY in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_ISKEY
+        if Ms3dUi.FLAG_NEWLYCREATED in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_NEWLYCREATED
+        if Ms3dUi.FLAG_MARKED in ui_value:
+            ms3d_value |= Ms3dSpec.FLAG_MARKED
+        return ms3d_value
+
+    ###########################################################################
+    ICON_OPTIONS = 'LAMP'
+    ICON_OBJECT = 'WORLD'
+    ICON_PROCESSING = 'OBJECT_DATAMODE'
+    ICON_MODIFIER = 'MODIFIER'
+    ICON_ANIMATION = 'RENDER_ANIMATION'
+    ICON_ROTATION_MODE = 'BONE_DATA'
+    ICON_ERROR = 'ERROR'
+
+    ###########################################################################
+    PROP_DEFAULT_VERBOSE = DEFAULT_VERBOSE
+
+    ###########################################################################
+    PROP_DEFAULT_USE_JOINT_SIZE = False
+    PROP_DEFAULT_JOINT_SIZE = 0.01
+    PROP_JOINT_SIZE_MIN = 0.01
+    PROP_JOINT_SIZE_MAX = 10.0
+    PROP_JOINT_SIZE_STEP = 0.1
+    PROP_JOINT_SIZE_PRECISION = 2
+
+    ###########################################################################
+    PROP_DEFAULT_USE_ANIMATION = True
+    PROP_DEFAULT_NORMALIZE_WEIGHTS = True
+    PROP_DEFAULT_SHRINK_TO_KEYS = False
+    PROP_DEFAULT_BAKE_EACH_FRAME = True
+    PROP_DEFAULT_JOINT_TO_BONES = False
+    PROP_DEFAULT_USE_BLENDER_NAMES = True
+    PROP_DEFAULT_USE_BLENDER_MATERIALS = False
+    PROP_DEFAULT_EXTENDED_NORMAL_HANDLING = False
+    PROP_DEFAULT_APPLY_TRANSFORM = True
+    PROP_DEFAULT_APPLY_MODIFIERS = True
+
+    ###########################################################################
+    PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW = 'PREVIEW'
+    PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER = 'RENDER'
+    PROP_DEFAULT_APPLY_MODIFIERS_MODE = PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW
+
+    ###########################################################################
+    PROP_ITEM_ROTATION_MODE_EULER = 'EULER'
+    PROP_ITEM_ROTATION_MODE_QUATERNION = 'QUATERNION'
+    PROP_DEFAULT_ANIMATION_ROTATION = PROP_ITEM_ROTATION_MODE_EULER
+
+    ###########################################################################
+    OPT_SMOOTHING_GROUP_APPLY = 'io_scene_ms3d.apply_smoothing_group'
+    OPT_GROUP_APPLY = 'io_scene_ms3d.apply_group'
+    OPT_MATERIAL_APPLY = 'io_scene_ms3d.apply_material'
+
+
+###############################################################################
+class Ms3dImportOperator(Operator, ImportHelper):
+    """ Load a MilkShape3D MS3D File """
+    bl_idname = 'import_scene.ms3d'
+    bl_label = ms3d_str['BL_LABEL_IMPORTER']
+    bl_description = ms3d_str['BL_DESCRIPTION_IMPORTER']
+    bl_options = {'PRESET', }
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+
+    filepath = StringProperty(
+            subtype='FILE_PATH',
+            options={'HIDDEN', }
+            )
+
+    verbose = BoolProperty(
+            name=ms3d_str['PROP_NAME_VERBOSE'],
+            description=ms3d_str['PROP_DESC_VERBOSE'],
+            default=Ms3dUi.PROP_DEFAULT_VERBOSE,
+            )
+
+    use_animation = BoolProperty(
+            name=ms3d_str['PROP_NAME_USE_ANIMATION'],
+            description=ms3d_str['PROP_DESC_USE_ANIMATION'],
+            default=Ms3dUi.PROP_DEFAULT_USE_ANIMATION,
+            )
+
+    rotation_mode = EnumProperty(
+            name=ms3d_str['PROP_NAME_ROTATION_MODE'],
+            description=ms3d_str['PROP_DESC_ROTATION_MODE'],
+            items=( (Ms3dUi.PROP_ITEM_ROTATION_MODE_EULER,
+                            ms3d_str['PROP_ITEM_ROTATION_MODE_EULER_1'],
+                            ms3d_str['PROP_ITEM_ROTATION_MODE_EULER_2']),
+                    (Ms3dUi.PROP_ITEM_ROTATION_MODE_QUATERNION,
+                            ms3d_str['PROP_ITEM_ROTATION_MODE_QUATERNION_1'],
+                            ms3d_str['PROP_ITEM_ROTATION_MODE_QUATERNION_2']),
+                    ),
+            default=Ms3dUi.PROP_DEFAULT_ANIMATION_ROTATION,
+            )
+
+    use_joint_size = BoolProperty(
+            name=ms3d_str['PROP_NAME_USE_JOINT_SIZE'],
+            description=ms3d_str['PROP_DESC_USE_JOINT_SIZE'],
+            default=Ms3dUi.PROP_DEFAULT_USE_JOINT_SIZE,
+            )
+
+    joint_size = FloatProperty(
+            name=ms3d_str['PROP_NAME_IMPORT_JOINT_SIZE'],
+            description=ms3d_str['PROP_DESC_IMPORT_JOINT_SIZE'],
+            min=Ms3dUi.PROP_JOINT_SIZE_MIN, max=Ms3dUi.PROP_JOINT_SIZE_MAX,
+            precision=Ms3dUi.PROP_JOINT_SIZE_PRECISION, \
+                    step=Ms3dUi.PROP_JOINT_SIZE_STEP,
+            default=Ms3dUi.PROP_DEFAULT_JOINT_SIZE,
+            subtype='FACTOR',
+            #options={'HIDDEN', },
+            )
+
+    use_joint_to_bones = BoolProperty(
+            name=ms3d_str['PROP_NAME_JOINT_TO_BONES'],
+            description=ms3d_str['PROP_DESC_JOINT_TO_BONES'],
+            default=Ms3dUi.PROP_DEFAULT_JOINT_TO_BONES,
+            )
+
+    use_extended_normal_handling = BoolProperty(
+            name=ms3d_str['PROP_NAME_EXTENDED_NORMAL_HANDLING'],
+            description=ms3d_str['PROP_DESC_EXTENDED_NORMAL_HANDLING'],
+            default=Ms3dUi.PROP_DEFAULT_EXTENDED_NORMAL_HANDLING,
+            )
+
+    filename_ext = StringProperty(
+            default=ms3d_str['FILE_EXT'],
+            options={'HIDDEN', }
+            )
+
+    filter_glob = StringProperty(
+            default=ms3d_str['FILE_FILTER'],
+            options={'HIDDEN', }
+            )
+
+
+    @property
+    def use_euler_rotation(self):
+        return (Ms3dUi.PROP_ITEM_ROTATION_MODE_EULER \
+                in self.rotation_mode)
+
+    @property
+    def use_quaternion_rotation(self):
+        return (Ms3dUi.PROP_ITEM_ROTATION_MODE_QUATERNION \
+                in self.rotation_mode)
+
+
+    # draw the option panel
+    def draw(self, blender_context):
+        layout = self.layout
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_OPTIONS'], icon=Ms3dUi.ICON_OPTIONS)
+        box.prop(self, 'verbose', icon='SPEAKER')
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_PROCESSING'],
+                icon=Ms3dUi.ICON_PROCESSING)
+        box.prop(self, 'use_extended_normal_handling')
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_ANIMATION'], icon=Ms3dUi.ICON_ANIMATION)
+        box.prop(self, 'use_animation')
+        if (self.use_animation):
+            box.prop(self, 'rotation_mode', icon=Ms3dUi.ICON_ROTATION_MODE,
+                    expand=False)
+            box.prop(self, 'use_joint_size')
+            if (self.use_joint_size):
+                col = box.column()
+                row = col.row()
+                row.prop(self, 'joint_size')
+            box.prop(self, 'use_joint_to_bones')
+            if (self.use_joint_to_bones):
+                box.box().label(ms3d_str['LABEL_NAME_JOINT_TO_BONES'],
+                        icon=Ms3dUi.ICON_ERROR)
+
+    # entrypoint for MS3D -> blender
+    def execute(self, blender_context):
+        """ start executing """
+        from io_scene_ms3d.ms3d_import import (Ms3dImporter, )
+        return Ms3dImporter(
+                report=self.report,
+                verbose=self.verbose,
+                use_extended_normal_handling=self.use_extended_normal_handling,
+                use_animation=self.use_animation,
+                use_quaternion_rotation=self.use_quaternion_rotation,
+                use_joint_size=self.use_joint_size,
+                joint_size=self.joint_size,
+                use_joint_to_bones=self.use_joint_to_bones,
+                ).read(
+                        blender_context,
+                        self.filepath
+                        )
+
+    def invoke(self, blender_context, event):
+        blender_context.window_manager.fileselect_add(self)
+        return {'RUNNING_MODAL', }
+
+    @staticmethod
+    def menu_func(cls, blender_context):
+        cls.layout.operator(
+                Ms3dImportOperator.bl_idname,
+                text=ms3d_str['TEXT_OPERATOR'],
+                )
+
+
+class Ms3dExportOperator(Operator, ExportHelper):
+    """Save a MilkShape3D MS3D File"""
+    bl_idname = 'export_scene.ms3d'
+    bl_label = ms3d_str['BL_LABEL_EXPORTER']
+    bl_description = ms3d_str['BL_DESCRIPTION_EXPORTER']
+    bl_options = {'PRESET', }
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+
+    filepath = StringProperty(
+            subtype='FILE_PATH',
+            options={'HIDDEN', }
+            )
+
+    verbose = BoolProperty(
+            name=ms3d_str['PROP_NAME_VERBOSE'],
+            description=ms3d_str['PROP_DESC_VERBOSE'],
+            default=Ms3dUi.PROP_DEFAULT_VERBOSE,
+            )
+
+    use_blender_names = BoolProperty(
+            name=ms3d_str['PROP_NAME_USE_BLENDER_NAMES'],
+            description=ms3d_str['PROP_DESC_USE_BLENDER_NAMES'],
+            default=Ms3dUi.PROP_DEFAULT_USE_BLENDER_NAMES,
+            )
+
+    use_blender_materials = BoolProperty(
+            name=ms3d_str['PROP_NAME_USE_BLENDER_MATERIALS'],
+            description=ms3d_str['PROP_DESC_USE_BLENDER_MATERIALS'],
+            default=Ms3dUi.PROP_DEFAULT_USE_BLENDER_MATERIALS,
+            )
+
+    apply_transform = BoolProperty(
+            name=ms3d_str['PROP_NAME_APPLY_TRANSFORM'],
+            description=ms3d_str['PROP_DESC_APPLY_TRANSFORM'],
+            default=Ms3dUi.PROP_DEFAULT_APPLY_TRANSFORM,
+            )
+
+    apply_modifiers = BoolProperty(
+            name=ms3d_str['PROP_NAME_APPLY_MODIFIERS'],
+            description=ms3d_str['PROP_DESC_APPLY_MODIFIERS'],
+            default=Ms3dUi.PROP_DEFAULT_APPLY_MODIFIERS,
+            )
+
+    apply_modifiers_mode =  EnumProperty(
+            name=ms3d_str['PROP_NAME_APPLY_MODIFIERS_MODE'],
+            description=ms3d_str['PROP_DESC_APPLY_MODIFIERS_MODE'],
+            items=( (Ms3dUi.PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW,
+                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_1'],
+                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_VIEW_2']),
+                    (Ms3dUi.PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER,
+                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_1'],
+                            ms3d_str['PROP_ITEM_APPLY_MODIFIERS_MODE_RENDER_2']),
+                    ),
+            default=Ms3dUi.PROP_DEFAULT_APPLY_MODIFIERS_MODE,
+            )
+
+    use_animation = BoolProperty(
+            name=ms3d_str['PROP_NAME_USE_ANIMATION'],
+            description=ms3d_str['PROP_DESC_USE_ANIMATION'],
+            default=Ms3dUi.PROP_DEFAULT_USE_ANIMATION,
+            )
+
+    normalize_weights = BoolProperty(
+            name=ms3d_str['PROP_NAME_NORMALIZE_WEIGHTS'],
+            description=ms3d_str['PROP_DESC_NORMALIZE_WEIGHTS'],
+            default=Ms3dUi.PROP_DEFAULT_NORMALIZE_WEIGHTS,
+            )
+
+    shrink_to_keys = BoolProperty(
+            name=ms3d_str['PROP_NAME_SHRINK_TO_KEYS'],
+            description=ms3d_str['PROP_DESC_SHRINK_TO_KEYS'],
+            default=Ms3dUi.PROP_DEFAULT_SHRINK_TO_KEYS,
+            )
+
+    bake_each_frame = BoolProperty(
+            name=ms3d_str['PROP_NAME_BAKE_EACH_FRAME'],
+            description=ms3d_str['PROP_DESC_BAKE_EACH_FRAME'],
+            default=Ms3dUi.PROP_DEFAULT_BAKE_EACH_FRAME,
+            )
+
+    check_existing = BoolProperty(
+            default=False,
+            options={'HIDDEN', }
+            )
+
+    filename_ext = StringProperty(
+            default=ms3d_str['FILE_EXT'],
+            options={'HIDDEN', }
+            )
+
+    filter_glob = StringProperty(
+            default=ms3d_str['FILE_FILTER'],
+            options={'HIDDEN', }
+            )
+
+
+    ##EXPORT_ACTIVE_ONLY:
+    ##limit availability to only active mesh object
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.active_object
+                and blender_context.active_object.type in {'MESH', }
+                and blender_context.active_object.data
+                and blender_context.active_object.data.ms3d is not None
+                )
+
+    # draw the option panel
+    def draw(self, blender_context):
+        layout = self.layout
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_OPTIONS'], icon=Ms3dUi.ICON_OPTIONS)
+        box.prop(self, 'verbose', icon='SPEAKER')
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_PROCESSING'],
+                icon=Ms3dUi.ICON_PROCESSING)
+        row = box.row()
+        row.label(ms3d_str['PROP_NAME_ACTIVE'], icon='ROTACTIVE')
+        row.label(blender_context.active_object.name)
+        #box.prop(self, 'use_blender_names', icon='LINK_BLEND')
+        box.prop(self, 'use_blender_names')
+        box.prop(self, 'use_blender_materials')
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_MODIFIER'],
+                icon=Ms3dUi.ICON_MODIFIER)
+        box.prop(self, 'apply_transform')
+        row = box.row()
+        row.prop(self, 'apply_modifiers')
+        sub = row.row()
+        sub.active = self.apply_modifiers
+        sub.prop(self, 'apply_modifiers_mode', text="")
+
+        box = layout.box()
+        box.label(ms3d_str['LABEL_NAME_ANIMATION'],
+                icon=Ms3dUi.ICON_ANIMATION)
+        box.prop(self, 'use_animation')
+        if (self.use_animation):
+            box.prop(self, 'normalize_weights')
+            box.prop(self, 'shrink_to_keys')
+            box.prop(self, 'bake_each_frame')
+
+    # entrypoint for blender -> MS3D
+    def execute(self, blender_context):
+        """start executing"""
+        from io_scene_ms3d.ms3d_export import (Ms3dExporter, )
+        return Ms3dExporter(
+                self.report,
+                verbose=self.verbose,
+                use_blender_names=self.use_blender_names,
+                use_blender_materials=self.use_blender_materials,
+                apply_transform=self.apply_transform,
+                apply_modifiers=self.apply_modifiers,
+                apply_modifiers_mode=self.apply_modifiers_mode,
+                use_animation=self.use_animation,
+                normalize_weights=self.normalize_weights,
+                shrink_to_keys=self.shrink_to_keys,
+                bake_each_frame=self.bake_each_frame,
+                ).write(
+                        blender_context,
+                        self.filepath
+                        )
+
+    #
+    def invoke(self, blender_context, event):
+        blender_context.window_manager.fileselect_add(self)
+        return {"RUNNING_MODAL", }
+
+    @staticmethod
+    def menu_func(cls, blender_context):
+        cls.layout.operator(
+                Ms3dExportOperator.bl_idname,
+                text=ms3d_str['TEXT_OPERATOR']
+                )
+
+
+###############################################################################
+##
+###############################################################################
+
+
+###############################################################################
+class Ms3dSetSmoothingGroupOperator(Operator):
+    bl_idname = Ms3dUi.OPT_SMOOTHING_GROUP_APPLY
+    bl_label = ms3d_str['BL_LABEL_SMOOTHING_GROUP_OPERATOR']
+    bl_options = {'INTERNAL', }
+
+    smoothing_group_index = IntProperty(
+            name=ms3d_str['PROP_SMOOTHING_GROUP_INDEX'],
+            options={'HIDDEN', 'SKIP_SAVE', },
+            )
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                and blender_context.mode == 'EDIT_MESH'
+                and blender_context.tool_settings.mesh_select_mode[2]
+                )
+
+    def execute(self, blender_context):
+        custom_data = blender_context.object.data.ms3d
+        blender_mesh = blender_context.object.data
+        bm = from_edit_mesh(blender_mesh)
+        layer_smoothing_group = bm.faces.layers.int.get(
+                ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+        if custom_data.apply_mode in {'SELECT', 'DESELECT', }:
+            if layer_smoothing_group is not None:
+                is_select = (custom_data.apply_mode == 'SELECT')
+                for bmf in bm.faces:
+                    if (bmf[layer_smoothing_group] \
+                            == self.smoothing_group_index):
+                        bmf.select_set(is_select)
+        elif custom_data.apply_mode == 'ASSIGN':
+            if layer_smoothing_group is None:
+                layer_smoothing_group = bm.faces.layers.int.new(
+                        ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+                blender_mesh_object = blender_context.object
+                get_edge_split_modifier_add_if(blender_mesh_object)
+            blender_face_list = []
+            for bmf in bm.faces:
+                if not bmf.smooth:
+                    bmf.smooth = True
+                if bmf.select:
+                    bmf[layer_smoothing_group] = self.smoothing_group_index
+                    blender_face_list.append(bmf)
+            edge_dict = {}
+            for bmf in blender_face_list:
+                bmf.smooth = True
+                for bme in bmf.edges:
+                    if edge_dict.get(bme) is None:
+                        edge_dict[bme] = 0
+                    else:
+                        edge_dict[bme] += 1
+                    is_border = (edge_dict[bme] == 0)
+                    if is_border:
+                        surround_face_smoothing_group_index \
+                                = self.smoothing_group_index
+                        for bmf in bme.link_faces:
+                            if bmf[layer_smoothing_group] \
+                                    != surround_face_smoothing_group_index:
+                                surround_face_smoothing_group_index \
+                                        = bmf[layer_smoothing_group]
+                                break;
+                        if surround_face_smoothing_group_index \
+                                == self.smoothing_group_index:
+                            is_border = False
+                    bme.seam = is_border
+                    bme.smooth = not is_border
+        bm.free()
+        enable_edit_mode(False, blender_context)
+        enable_edit_mode(True, blender_context)
+        return {'FINISHED', }
+
+
+class Ms3dGroupOperator(Operator):
+    bl_idname = Ms3dUi.OPT_GROUP_APPLY
+    bl_label = ms3d_str['BL_LABEL_GROUP_OPERATOR']
+    bl_options = {'INTERNAL', }
+
+    mode = EnumProperty(
+            items=( ('', "", ""),
+                    ('ADD_GROUP',
+                            ms3d_str['ENUM_ADD_GROUP_1'],
+                            ms3d_str['ENUM_ADD_GROUP_2']),
+                    ('REMOVE_GROUP',
+                            ms3d_str['ENUM_REMOVE_GROUP_1'],
+                            ms3d_str['ENUM_REMOVE_GROUP_2']),
+                    ('ASSIGN',
+                            ms3d_str['ENUM_ASSIGN_1'],
+                            ms3d_str['ENUM_ASSIGN_2_GROUP']),
+                    ('REMOVE',
+                            ms3d_str['ENUM_REMOVE_1'],
+                            ms3d_str['ENUM_REMOVE_2_GROUP']),
+                    ('SELECT',
+                            ms3d_str['ENUM_SELECT_1'],
+                            ms3d_str['ENUM_SELECT_2_GROUP']),
+                    ('DESELECT',
+                            ms3d_str['ENUM_DESELECT_1'],
+                            ms3d_str['ENUM_DESELECT_2_GROUP']),
+                    ),
+            options={'HIDDEN', 'SKIP_SAVE', },
+            )
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                and blender_context.mode == 'EDIT_MESH'
+                #and blender_context.object.data.ms3d.selected_group_index != -1
+                )
+
+    def execute(self, blender_context):
+        custom_data = blender_context.object.data.ms3d
+        blender_mesh = blender_context.object.data
+        bm = None
+        bm = from_edit_mesh(blender_mesh)
+
+        if self.mode == 'ADD_GROUP':
+            item = custom_data.create_group()
+            layer_group = bm.faces.layers.int.get(
+                    ms3d_str['OBJECT_LAYER_GROUP'])
+            if layer_group is None:
+                bm.faces.layers.int.new(ms3d_str['OBJECT_LAYER_GROUP'])
+
+        elif self.mode == 'REMOVE_GROUP':
+            custom_data.remove_group()
+
+        elif (custom_data.selected_group_index >= 0) and (
+                custom_data.selected_group_index < len(custom_data.groups)):
+            if self.mode in {'SELECT', 'DESELECT', }:
+                layer_group = bm.faces.layers.int.get(
+                        ms3d_str['OBJECT_LAYER_GROUP'])
+                if layer_group is not None:
+                    is_select = (self.mode == 'SELECT')
+                    id = custom_data.groups[
+                            custom_data.selected_group_index].id
+                    for bmf in bm.faces:
+                        if bmf[layer_group] == id:
+                            bmf.select_set(is_select)
+
+            elif self.mode in {'ASSIGN', 'REMOVE', }:
+                layer_group = bm.faces.layers.int.get(
+                        ms3d_str['OBJECT_LAYER_GROUP'])
+                if layer_group is None:
+                    layer_group = bm.faces.layers.int.new(
+                            ms3d_str['OBJECT_LAYER_GROUP'])
+
+                is_assign = (self.mode == 'ASSIGN')
+                id = custom_data.groups[custom_data.selected_group_index].id
+                for bmf in bm.faces:
+                    if bmf.select:
+                        if is_assign:
+                            bmf[layer_group] = id
+                        else:
+                            bmf[layer_group] = -1
+        if bm is not None:
+            bm.free()
+        enable_edit_mode(False, blender_context)
+        enable_edit_mode(True, blender_context)
+        return {'FINISHED', }
+
+
+class Ms3dMaterialOperator(Operator):
+    bl_idname = Ms3dUi.OPT_MATERIAL_APPLY
+    bl_label = ms3d_str['BL_LABEL_MATERIAL_OPERATOR']
+    bl_options = {'INTERNAL', }
+
+    mode = EnumProperty(
+            items=( ('', "", ""),
+                    ('FROM_BLENDER',
+                            ms3d_str['ENUM_FROM_BLENDER_1'],
+                            ms3d_str['ENUM_FROM_BLENDER_2']),
+                    ('TO_BLENDER',
+                            ms3d_str['ENUM_TO_BLENDER_1'],
+                            ms3d_str['ENUM_TO_BLENDER_2']),
+                    ),
+            options={'HIDDEN', 'SKIP_SAVE', },
+            )
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                and blender_context.material
+                and blender_context.material.ms3d is not None
+                )
+
+    def execute(self, blender_context):
+        blender_material = blender_context.active_object.active_material
+        ms3d_material = blender_material.ms3d
+
+        if self.mode == 'FROM_BLENDER':
+            Ms3dMaterialHelper.copy_from_blender(self, blender_context,
+                    ms3d_material, blender_material)
+            pass
+
+        elif self.mode == 'TO_BLENDER':
+            # not implemented
+            pass
+
+        return {'FINISHED', }
+
+    # entrypoint for option via UI
+    def invoke(self, blender_context, event):
+        return blender_context.window_manager.invoke_props_dialog(self)
+
+
+###############################################################################
+class Ms3dGroupProperties(PropertyGroup):
+    name = StringProperty(
+            name=ms3d_str['PROP_NAME_NAME'],
+            description=ms3d_str['PROP_DESC_GROUP_NAME'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+    flags = EnumProperty(
+            name=ms3d_str['PROP_NAME_FLAGS'],
+            description=ms3d_str['PROP_DESC_FLAGS_GROUP'],
+            items=(#(Ms3dUi.FLAG_NONE, ms3d_str['ENUM_FLAG_NONE_1'],
+                   #         ms3d_str['ENUM_FLAG_NONE_2'],
+                   #         Ms3dSpec.FLAG_NONE),
+                    (Ms3dUi.FLAG_SELECTED,
+                            ms3d_str['ENUM_FLAG_SELECTED_1'],
+                            ms3d_str['ENUM_FLAG_SELECTED_2'],
+                            Ms3dSpec.FLAG_SELECTED),
+                    (Ms3dUi.FLAG_HIDDEN,
+                            ms3d_str['ENUM_FLAG_HIDDEN_1'],
+                            ms3d_str['ENUM_FLAG_HIDDEN_2'],
+                            Ms3dSpec.FLAG_HIDDEN),
+                    (Ms3dUi.FLAG_SELECTED2,
+                            ms3d_str['ENUM_FLAG_SELECTED2_1'],
+                            ms3d_str['ENUM_FLAG_SELECTED2_2'],
+                            Ms3dSpec.FLAG_SELECTED2),
+                    (Ms3dUi.FLAG_DIRTY,
+                            ms3d_str['ENUM_FLAG_DIRTY_1'],
+                            ms3d_str['ENUM_FLAG_DIRTY_2'],
+                            Ms3dSpec.FLAG_DIRTY),
+                    (Ms3dUi.FLAG_ISKEY,
+                            ms3d_str['ENUM_FLAG_ISKEY_1'],
+                            ms3d_str['ENUM_FLAG_ISKEY_2'],
+                            Ms3dSpec.FLAG_ISKEY),
+                    (Ms3dUi.FLAG_NEWLYCREATED,
+                            ms3d_str['ENUM_FLAG_NEWLYCREATED_1'],
+                            ms3d_str['ENUM_FLAG_NEWLYCREATED_2'],
+                            Ms3dSpec.FLAG_NEWLYCREATED),
+                    (Ms3dUi.FLAG_MARKED,
+                            ms3d_str['ENUM_FLAG_MARKED_1'],
+                            ms3d_str['ENUM_FLAG_MARKED_2'],
+                            Ms3dSpec.FLAG_MARKED),
+                    ),
+            default=Ms3dUi.flags_from_ms3d(Ms3dSpec.DEFAULT_FLAGS),
+            options={'ENUM_FLAG', 'ANIMATABLE', },
+            )
+
+    comment = StringProperty(
+            name=ms3d_str['PROP_NAME_COMMENT'],
+            description=ms3d_str['PROP_DESC_COMMENT_GROUP'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+    id = IntProperty(options={'HIDDEN', },)
+
+
+class Ms3dModelProperties(PropertyGroup):
+    name = StringProperty(
+            name=ms3d_str['PROP_NAME_NAME'],
+            description=ms3d_str['PROP_DESC_NAME_MODEL'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+    joint_size = FloatProperty(
+            name=ms3d_str['PROP_NAME_JOINT_SIZE'],
+            description=ms3d_str['PROP_DESC_JOINT_SIZE'],
+            min=Ms3dUi.PROP_JOINT_SIZE_MIN, max=Ms3dUi.PROP_JOINT_SIZE_MAX,
+            precision=Ms3dUi.PROP_JOINT_SIZE_PRECISION, \
+                    step=Ms3dUi.PROP_JOINT_SIZE_STEP,
+            default=Ms3dUi.PROP_DEFAULT_JOINT_SIZE,
+            subtype='FACTOR',
+            #options={'HIDDEN', },
+            )
+
+    transparency_mode = EnumProperty(
+            name=ms3d_str['PROP_NAME_TRANSPARENCY_MODE'],
+            description=ms3d_str['PROP_DESC_TRANSPARENCY_MODE'],
+            items=( (Ms3dUi.MODE_TRANSPARENCY_SIMPLE,
+                            ms3d_str['PROP_MODE_TRANSPARENCY_SIMPLE_1'],
+                            ms3d_str['PROP_MODE_TRANSPARENCY_SIMPLE_2'],
+                            Ms3dSpec.MODE_TRANSPARENCY_SIMPLE),
+                    (Ms3dUi.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES,
+                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_1'],
+                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES_2'],
+                            Ms3dSpec.MODE_TRANSPARENCY_DEPTH_SORTED_TRIANGLES),
+                    (Ms3dUi.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF,
+                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_1'],
+                            ms3d_str['PROP_MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF_2'],
+                            Ms3dSpec.MODE_TRANSPARENCY_DEPTH_BUFFERED_WITH_ALPHA_REF),
+                    ),
+            default=Ms3dUi.transparency_mode_from_ms3d(
+                    Ms3dSpec.DEFAULT_MODEL_TRANSPARENCY_MODE),
+            #options={'HIDDEN', },
+            )
+
+    alpha_ref = FloatProperty(
+            name=ms3d_str['PROP_NAME_ALPHA_REF'],
+            description=ms3d_str['PROP_DESC_ALPHA_REF'],
+            min=0, max=1, precision=3, step=0.1,
+            default=0.5,
+            subtype='FACTOR',
+            #options={'HIDDEN', },
+            )
+
+    comment = StringProperty(
+            name=ms3d_str['PROP_NAME_COMMENT'],
+            description=ms3d_str['PROP_DESC_COMMENT_MODEL'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+    ##########################
+    # ms3d group handling
+    #
+    apply_mode = EnumProperty(
+            items=( ('ASSIGN',
+                            ms3d_str['ENUM_ASSIGN_1'],
+                            ms3d_str['ENUM_ASSIGN_2_SMOOTHING_GROUP']),
+                    ('SELECT',
+                            ms3d_str['ENUM_SELECT_1'],
+                            ms3d_str['ENUM_SELECT_2_SMOOTHING_GROUP']),
+                    ('DESELECT',
+                            ms3d_str['ENUM_DESELECT_1'],
+                            ms3d_str['ENUM_DESELECT_2_SMOOTHING_GROUP']),
+                    ),
+            default='SELECT',
+            options={'HIDDEN', 'SKIP_SAVE', },
+            )
+
+    selected_group_index = IntProperty(
+            default=-1,
+            min=-1,
+            options={'HIDDEN', 'SKIP_SAVE', },
+            )
+    #
+    # ms3d group handling
+    ##########################
+
+    groups = CollectionProperty(
+            type=Ms3dGroupProperties,
+            #options={'HIDDEN', },
+            )
+
+
+    def generate_unique_id(self):
+        return randrange(1, 0x7FFFFFFF) # pseudo unique id
+
+    def create_group(self):
+        item = self.groups.add()
+        item.id = self.generate_unique_id()
+        length = len(self.groups)
+        self.selected_group_index = length - 1
+
+        item.name = ms3d_str['STRING_FORMAT_GROUP'].format(length)
+        return item
+
+    def remove_group(self):
+        index = self.selected_group_index
+        length = len(self.groups)
+        if (index >= 0) and (index < length):
+            if index > 0 or length == 1:
+                self.selected_group_index = index - 1
+            self.groups.remove(index)
+
+
+class Ms3dArmatureProperties(PropertyGroup):
+    name = StringProperty(
+            name=ms3d_str['PROP_NAME_NAME'],
+            description=ms3d_str['PROP_DESC_NAME_ARMATURE'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+
+class Ms3dJointProperties(PropertyGroup):
+    name = StringProperty(
+            name=ms3d_str['PROP_NAME_NAME'],
+            description=ms3d_str['PROP_DESC_NAME_JOINT'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+    flags = EnumProperty(
+            name=ms3d_str['PROP_NAME_FLAGS'],
+            description=ms3d_str['PROP_DESC_FLAGS_JOINT'],
+            items=(#(Ms3dUi.FLAG_NONE,
+                   #         ms3d_str['ENUM_FLAG_NONE_1'],
+                   #         ms3d_str['ENUM_FLAG_NONE_2'],
+                   #         Ms3dSpec.FLAG_NONE),
+                    (Ms3dUi.FLAG_SELECTED,
+                            ms3d_str['ENUM_FLAG_SELECTED_1'],
+                            ms3d_str['ENUM_FLAG_SELECTED_2'],
+                            Ms3dSpec.FLAG_SELECTED),
+                    (Ms3dUi.FLAG_HIDDEN,
+                            ms3d_str['ENUM_FLAG_HIDDEN_1'],
+                            ms3d_str['ENUM_FLAG_HIDDEN_2'],
+                            Ms3dSpec.FLAG_HIDDEN),
+                    (Ms3dUi.FLAG_SELECTED2,
+                            ms3d_str['ENUM_FLAG_SELECTED2_1'],
+                            ms3d_str['ENUM_FLAG_SELECTED2_2'],
+                            Ms3dSpec.FLAG_SELECTED2),
+                    (Ms3dUi.FLAG_DIRTY,
+                            ms3d_str['ENUM_FLAG_DIRTY_1'],
+                            ms3d_str['ENUM_FLAG_DIRTY_2'],
+                            Ms3dSpec.FLAG_DIRTY),
+                    (Ms3dUi.FLAG_ISKEY,
+                            ms3d_str['ENUM_FLAG_ISKEY_1'],
+                            ms3d_str['ENUM_FLAG_ISKEY_2'],
+                            Ms3dSpec.FLAG_ISKEY),
+                    (Ms3dUi.FLAG_NEWLYCREATED,
+                            ms3d_str['ENUM_FLAG_NEWLYCREATED_1'],
+                            ms3d_str['ENUM_FLAG_NEWLYCREATED_2'],
+                            Ms3dSpec.FLAG_NEWLYCREATED),
+                    (Ms3dUi.FLAG_MARKED,
+                            ms3d_str['ENUM_FLAG_MARKED_1'],
+                            ms3d_str['ENUM_FLAG_MARKED_2'],
+                            Ms3dSpec.FLAG_MARKED),
+                    ),
+            default=Ms3dUi.flags_from_ms3d(Ms3dSpec.DEFAULT_FLAGS),
+            options={'ENUM_FLAG', 'ANIMATABLE', },
+            )
+
+    color = FloatVectorProperty(
+            name=ms3d_str['PROP_NAME_COLOR'],
+            description=ms3d_str['PROP_DESC_COLOR_JOINT'],
+            subtype='COLOR', size=3, min=0, max=1, precision=3, step=0.1,
+            default=Ms3dSpec.DEFAULT_JOINT_COLOR,
+            #options={'HIDDEN', },
+            )
+
+    comment = StringProperty(
+            name=ms3d_str['PROP_NAME_COMMENT'],
+            description=ms3d_str['PROP_DESC_COMMENT_JOINT'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+
+class Ms3dMaterialHelper:
+    @staticmethod
+    def copy_to_blender_ambient(cls, blender_context):
+        pass
+
+    @staticmethod
+    def copy_to_blender_diffuse(cls, blender_context):
+        cls.id_data.diffuse_color = cls.diffuse[0:3]
+        #cls.id_data.diffuse_intensity = cls.diffuse[3]
+        pass
+
+    @staticmethod
+    def copy_to_blender_specular(cls, blender_context):
+        cls.id_data.specular_color = cls.specular[0:3]
+        #cls.id_data.specular_intensity = cls.specular[3]
+        pass
+
+    @staticmethod
+    def copy_to_blender_emissive(cls, blender_context):
+        cls.id_data.emit = (cls.emissive[0] + cls.emissive[1] \
+                + cls.emissive[2]) / 3.0
+        pass
+
+    @staticmethod
+    def copy_to_blender_shininess(cls, blender_context):
+        cls.id_data.specular_hardness = cls.shininess * 4.0
+        pass
+
+    @staticmethod
+    def copy_to_blender_transparency(cls, blender_context):
+        cls.id_data.alpha = 1.0 - cls.transparency
+        pass
+
+
+    @staticmethod
+    def copy_from_blender(cls, blender_context, ms3d_material, blender_material):
+        # copy, bacause of auto update, it would distord original values
+        blender_material_diffuse_color = blender_material.diffuse_color.copy()
+        blender_material_diffuse_intensity = blender_material.diffuse_intensity
+        blender_material_specular_color = blender_material.specular_color.copy()
+        blender_material_specular_intensity = \
+                blender_material.specular_intensity
+        blender_material_emit = blender_material.emit
+        blender_material_specular_hardness = \
+                blender_material.specular_hardness
+        blender_material_alpha = blender_material.alpha
+
+        blender_material_texture = None
+        for slot in blender_material.texture_slots:
+            if slot and slot.use_map_color_diffuse \
+                    and slot.texture.type == 'IMAGE':
+                blender_material_texture = slot.texture.image.filepath
+                break
+
+        blender_material_alphamap = None
+        for slot in blender_material.texture_slots:
+            if slot and not slot.use_map_color_diffuse \
+                    and slot.use_map_alpha and slot.texture.type == 'IMAGE':
+                blender_material_alphamap = slot.texture.image.filepath
+                break
+
+        ms3d_material.diffuse[0] = blender_material_diffuse_color[0]
+        ms3d_material.diffuse[1] = blender_material_diffuse_color[1]
+        ms3d_material.diffuse[2] = blender_material_diffuse_color[2]
+        ms3d_material.diffuse[3] = 1.0
+        ms3d_material.specular[0] = blender_material_specular_color[0]
+        ms3d_material.specular[1] = blender_material_specular_color[1]
+        ms3d_material.specular[2] = blender_material_specular_color[2]
+        ms3d_material.specular[3] = 1.0
+        ms3d_material.emissive[0] = blender_material_emit
+        ms3d_material.emissive[1] = blender_material_emit
+        ms3d_material.emissive[2] = blender_material_emit
+        ms3d_material.emissive[3] = 1.0
+        ms3d_material.shininess = blender_material_specular_hardness / 4.0
+        ms3d_material.transparency = 1.0 - blender_material_alpha
+
+        if blender_material_texture:
+            ms3d_material.texture = blender_material_texture
+        else:
+            ms3d_material.texture = ""
+
+        if blender_material_alphamap:
+            ms3d_material.alphamap = blender_material_alphamap
+        else:
+            ms3d_material.alphamap = ""
+
+
+class Ms3dMaterialProperties(PropertyGroup):
+    name = StringProperty(
+            name=ms3d_str['PROP_NAME_NAME'],
+            description=ms3d_str['PROP_DESC_NAME_MATERIAL'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+    ambient = FloatVectorProperty(
+            name=ms3d_str['PROP_NAME_AMBIENT'],
+            description=ms3d_str['PROP_DESC_AMBIENT'],
+            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
+            default=Ms3dSpec.DEFAULT_MATERIAL_AMBIENT,
+            update=Ms3dMaterialHelper.copy_to_blender_ambient,
+            #options={'HIDDEN', },
+            )
+
+    diffuse = FloatVectorProperty(
+            name=ms3d_str['PROP_NAME_DIFFUSE'],
+            description=ms3d_str['PROP_DESC_DIFFUSE'],
+            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
+            default=Ms3dSpec.DEFAULT_MATERIAL_DIFFUSE,
+            update=Ms3dMaterialHelper.copy_to_blender_diffuse,
+            #options={'HIDDEN', },
+            )
+
+    specular = FloatVectorProperty(
+            name=ms3d_str['PROP_NAME_SPECULAR'],
+            description=ms3d_str['PROP_DESC_SPECULAR'],
+            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
+            default=Ms3dSpec.DEFAULT_MATERIAL_SPECULAR,
+            update=Ms3dMaterialHelper.copy_to_blender_specular,
+            #options={'HIDDEN', },
+            )
+
+    emissive = FloatVectorProperty(
+            name=ms3d_str['PROP_NAME_EMISSIVE'],
+            description=ms3d_str['PROP_DESC_EMISSIVE'],
+            subtype='COLOR', size=4, min=0, max=1, precision=3, step=0.1,
+            default=Ms3dSpec.DEFAULT_MATERIAL_EMISSIVE,
+            update=Ms3dMaterialHelper.copy_to_blender_emissive,
+            #options={'HIDDEN', },
+            )
+
+    shininess = FloatProperty(
+            name=ms3d_str['PROP_NAME_SHININESS'],
+            description=ms3d_str['PROP_DESC_SHININESS'],
+            min=0, max=Ms3dSpec.MAX_MATERIAL_SHININESS, precision=3, step=0.1,
+            default=Ms3dSpec.DEFAULT_MATERIAL_SHININESS,
+            subtype='FACTOR',
+            update=Ms3dMaterialHelper.copy_to_blender_shininess,
+            #options={'HIDDEN', },
+            )
+
+    transparency = FloatProperty(
+            name=ms3d_str['PROP_NAME_TRANSPARENCY'],
+            description=ms3d_str['PROP_DESC_TRANSPARENCY'],
+            min=0, max=1, precision=3, step=0.1,
+            default=0,
+            subtype='FACTOR',
+            update=Ms3dMaterialHelper.copy_to_blender_transparency,
+            #options={'HIDDEN', },
+            )
+
+    mode = EnumProperty(
+            name=ms3d_str['PROP_NAME_MODE'],
+            description=ms3d_str['PROP_DESC_MODE_TEXTURE'],
+            items=( (Ms3dUi.FLAG_TEXTURE_COMBINE_ALPHA,
+                            ms3d_str['PROP_FLAG_TEXTURE_COMBINE_ALPHA_1'],
+                            ms3d_str['PROP_FLAG_TEXTURE_COMBINE_ALPHA_2'],
+                            Ms3dSpec.FLAG_TEXTURE_COMBINE_ALPHA),
+                    (Ms3dUi.FLAG_TEXTURE_HAS_ALPHA,
+                            ms3d_str['PROP_FLAG_TEXTURE_HAS_ALPHA_1'],
+                            ms3d_str['PROP_FLAG_TEXTURE_HAS_ALPHA_2'],
+                            Ms3dSpec.FLAG_TEXTURE_HAS_ALPHA),
+                    (Ms3dUi.FLAG_TEXTURE_SPHERE_MAP,
+                            ms3d_str['PROP_FLAG_TEXTURE_SPHERE_MAP_1'],
+                            ms3d_str['PROP_FLAG_TEXTURE_SPHERE_MAP_2'],
+                            Ms3dSpec.FLAG_TEXTURE_SPHERE_MAP),
+                    ),
+            default=Ms3dUi.texture_mode_from_ms3d(
+                    Ms3dSpec.DEFAULT_MATERIAL_MODE),
+            options={'ANIMATABLE', 'ENUM_FLAG', },
+            )
+
+    texture = StringProperty(
+            name=ms3d_str['PROP_NAME_TEXTURE'],
+            description=ms3d_str['PROP_DESC_TEXTURE'],
+            default="",
+            subtype = 'FILE_PATH'
+            #options={'HIDDEN', },
+            )
+
+    alphamap = StringProperty(
+            name=ms3d_str['PROP_NAME_ALPHAMAP'],
+            description=ms3d_str['PROP_DESC_ALPHAMAP'],
+            default="",
+            subtype = 'FILE_PATH'
+            #options={'HIDDEN', },
+            )
+
+    comment = StringProperty(
+            name=ms3d_str['PROP_NAME_COMMENT'],
+            description=ms3d_str['PROP_DESC_COMMENT_MATERIAL'],
+            default="",
+            #options={'HIDDEN', },
+            )
+
+
+###############################################################################
+# http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=53355
+class Ms3dGroupUILise(UIList):
+    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+        layout.label(item.name, icon_value=icon)
+
+
+###############################################################################
+class Ms3dMeshPanel(Panel):
+    bl_label = ms3d_str['LABEL_PANEL_MODEL']
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = 'object'
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                )
+
+    def draw_header(self, blender_context):
+        layout = self.layout
+        layout.label(icon='PLUGIN')
+
+    def draw(self, blender_context):
+        layout = self.layout
+        custom_data = blender_context.object.data.ms3d
+
+        row = layout.row()
+        row.prop(custom_data, 'name')
+
+        col = layout.column()
+        row = col.row()
+        row.prop(custom_data, 'joint_size')
+        row = col.row()
+        row.prop(custom_data, 'transparency_mode')
+        row = col.row()
+        row.prop(custom_data, 'alpha_ref', )
+
+        row = layout.row()
+        row.prop(custom_data, 'comment')
+
+
+class Ms3dMaterialPanel(Panel):
+    bl_label = ms3d_str['LABEL_PANEL_MATERIALS']
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = 'material'
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                and blender_context.material
+                and blender_context.material.ms3d is not None
+                )
+
+    def draw_header(self, blender_context):
+        layout = self.layout
+        layout.label(icon='PLUGIN')
+
+    def draw(self, blender_context):
+        layout = self.layout
+        custom_data = blender_context.material.ms3d
+
+        row = layout.row()
+        row.prop(custom_data, 'name')
+
+        col = layout.column()
+        row = col.row()
+        row.prop(custom_data, 'diffuse')
+        row.prop(custom_data, 'ambient')
+        row = col.row()
+        row.prop(custom_data, 'specular')
+        row.prop(custom_data, 'emissive')
+        row = col.row()
+        row.prop(custom_data, 'shininess')
+        row.prop(custom_data, 'transparency')
+
+        col = layout.column()
+        row = col.row()
+        row.prop(custom_data, 'texture')
+        row = col.row()
+        row.prop(custom_data, 'alphamap')
+        row = col.row()
+        row.prop(custom_data, 'mode', expand=True)
+
+        row = layout.row()
+        row.prop(custom_data, 'comment')
+
+        layout.row().operator(
+                Ms3dUi.OPT_MATERIAL_APPLY,
+                text=ms3d_str['ENUM_FROM_BLENDER_1'],
+                icon='APPEND_BLEND').mode = 'FROM_BLENDER'
+        pass
+
+
+class Ms3dBonePanel(Panel):
+    bl_label = ms3d_str['LABEL_PANEL_JOINTS']
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = 'bone'
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object.type in {'ARMATURE', }
+                and blender_context.active_bone
+                and isinstance(blender_context.active_bone, Bone)
+                and blender_context.active_bone.ms3d is not None
+                )
+
+    def draw_header(self, blender_context):
+        layout = self.layout
+        layout.label(icon='PLUGIN')
+
+    def draw(self, blender_context):
+        import bpy
+        layout = self.layout
+        custom_data = blender_context.active_bone.ms3d
+
+        row = layout.row()
+        row.prop(custom_data, 'name')
+        row = layout.row()
+        row.prop(custom_data, 'flags', expand=True)
+        row = layout.row()
+        row.prop(custom_data, 'color')
+        row = layout.row()
+        row.prop(custom_data, 'comment')
+
+
+class Ms3dGroupPanel(Panel):
+    bl_label = ms3d_str['LABEL_PANEL_GROUPS']
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = 'data'
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                )
+
+    def draw_header(self, blender_context):
+        layout = self.layout
+        layout.label(icon='PLUGIN')
+
+    def draw(self, blender_context):
+        layout = self.layout
+        custom_data = blender_context.object.data.ms3d
+        layout.enabled = (blender_context.mode == 'EDIT_MESH') and (
+                blender_context.tool_settings.mesh_select_mode[2])
+
+        row = layout.row()
+
+        # http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=53355
+        row.template_list(
+                listtype_name='Ms3dGroupUILise',
+                dataptr=custom_data,
+                propname='groups',
+                active_dataptr=custom_data,
+                active_propname='selected_group_index',
+                rows=2,
+                type='DEFAULT',
+                )
+
+        col = row.column(align=True)
+        col.operator(
+                Ms3dUi.OPT_GROUP_APPLY,
+                text="", icon='ZOOMIN').mode = 'ADD_GROUP'
+        col.operator(
+                Ms3dUi.OPT_GROUP_APPLY,
+                text="", icon='ZOOMOUT').mode = 'REMOVE_GROUP'
+
+        index = custom_data.selected_group_index
+        collection = custom_data.groups
+        if (index >= 0 and index < len(collection)):
+            row = layout.row()
+            row.prop(collection[index], 'name')
+
+            row = layout.row()
+            subrow = row.row(align=True)
+            subrow.operator(
+                    Ms3dUi.OPT_GROUP_APPLY,
+                    text=ms3d_str['ENUM_ASSIGN_1']).mode = 'ASSIGN'
+            subrow.operator(
+                    Ms3dUi.OPT_GROUP_APPLY,
+                    text=ms3d_str['ENUM_REMOVE_1']).mode = 'REMOVE'
+            subrow = row.row(align=True)
+            subrow.operator(
+                    Ms3dUi.OPT_GROUP_APPLY,
+                    text=ms3d_str['ENUM_SELECT_1']).mode = 'SELECT'
+            subrow.operator(
+                    Ms3dUi.OPT_GROUP_APPLY,
+                    text=ms3d_str['ENUM_DESELECT_1']).mode = 'DESELECT'
+
+            row = layout.row()
+            row.prop(collection[index], 'flags', expand=True)
+
+            row = layout.row()
+            row.prop(collection[index], 'comment')
+
+
+class Ms3dSmoothingGroupPanel(Panel):
+    bl_label = ms3d_str['BL_LABEL_PANEL_SMOOTHING_GROUP']
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = 'data'
+
+    def preview(self, dict, id, text):
+        item = dict.get(id)
+        if item is None:
+            return "{}".format(text)
+        elif item:
+            return "{}:".format(text)
+
+        return "{}.".format(text)
+
+    def build_preview(self, blender_context):
+        dict = {}
+        if (blender_context.mode != 'EDIT_MESH') or (
+                not blender_context.tool_settings.mesh_select_mode[2]):
+            return dict
+
+        custom_data = blender_context.object.data.ms3d
+        blender_mesh = blender_context.object.data
+        bm = from_edit_mesh(blender_mesh)
+        layer_smoothing_group = bm.faces.layers.int.get(
+                ms3d_str['OBJECT_LAYER_SMOOTHING_GROUP'])
+        if layer_smoothing_group is not None:
+            for bmf in bm.faces:
+                item = dict.get(bmf[layer_smoothing_group])
+                if item is None:
+                    dict[bmf[layer_smoothing_group]] = bmf.select
+                else:
+                    if not item:
+                        dict[bmf[layer_smoothing_group]] = bmf.select
+        return dict
+
+    @classmethod
+    def poll(cls, blender_context):
+        return (blender_context
+                and blender_context.object
+                and blender_context.object.type in {'MESH', }
+                and blender_context.object.data
+                and blender_context.object.data.ms3d is not None
+                )
+
+    def draw_header(self, blender_context):
+        layout = self.layout
+        layout.label(icon='PLUGIN')
+
+    def draw(self, blender_context):
+        dict = self.build_preview(blender_context)
+
+        custom_data = blender_context.object.data.ms3d
+        layout = self.layout
+        layout.enabled = (blender_context.mode == 'EDIT_MESH') and (
+                blender_context.tool_settings.mesh_select_mode[2])
+
+        row = layout.row()
+        subrow = row.row()
+        subrow.prop(custom_data, 'apply_mode', expand=True)
+
+        col = layout.column(align=True)
+        subrow = col.row(align=True)
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 1, "1")
+                ).smoothing_group_index = 1
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 2, "2")
+                ).smoothing_group_index = 2
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 3, "3")
+                ).smoothing_group_index = 3
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 4, "4")
+                ).smoothing_group_index = 4
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 5, "5")
+                ).smoothing_group_index = 5
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 6, "6")
+                ).smoothing_group_index = 6
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 7, "7")
+                ).smoothing_group_index = 7
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 8, "8")
+                ).smoothing_group_index = 8
+        subrow = col.row(align=True)
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 9, "9")
+                ).smoothing_group_index = 9
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 10, "10")
+                ).smoothing_group_index = 10
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 11, "11")
+                ).smoothing_group_index = 11
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 12, "12")
+                ).smoothing_group_index = 12
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 13, "13")
+                ).smoothing_group_index = 13
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 14, "14")
+                ).smoothing_group_index = 14
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 15, "15")
+                ).smoothing_group_index = 15
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 16, "16")
+                ).smoothing_group_index = 16
+        subrow = col.row(align=True)
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 17, "17")
+                ).smoothing_group_index = 17
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 18, "18")
+                ).smoothing_group_index = 18
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 19, "19")
+                ).smoothing_group_index = 19
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 20, "20")
+                ).smoothing_group_index = 20
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 21, "21")
+                ).smoothing_group_index = 21
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 22, "22")
+                ).smoothing_group_index = 22
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 23, "23")
+                ).smoothing_group_index = 23
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 24, "24")
+                ).smoothing_group_index = 24
+        subrow = col.row(align=True)
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 25, "25")
+                ).smoothing_group_index = 25
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 26, "26")
+                ).smoothing_group_index = 26
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 27, "27")
+                ).smoothing_group_index = 27
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 28, "28")
+                ).smoothing_group_index = 28
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 29, "29")
+                ).smoothing_group_index = 29
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 30, "30")
+                ).smoothing_group_index = 30
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 31, "31")
+                ).smoothing_group_index = 31
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 32, "32")
+                ).smoothing_group_index = 32
+        subrow = col.row()
+        subrow.operator(
+                Ms3dUi.OPT_SMOOTHING_GROUP_APPLY,
+                text=self.preview(dict, 0, ms3d_str['LABEL_PANEL_BUTTON_NONE'])
+                ).smoothing_group_index = 0
+
+
+###############################################################################
+class Ms3dSetSceneToMetricOperator(Operator):
+    """ . """
+    bl_idname = 'io_scene_ms3d.set_sence_to_metric'
+    bl_label = ms3d_str['BL_LABEL_SET_SCENE_TO_METRIC']
+    bl_description = ms3d_str['BL_DESC_SET_SCENE_TO_METRIC']
+
+
+    #
+    @classmethod
+    def poll(cls, blender_context):
+        return True
+
+    # entrypoint for option
+    def execute(self, blender_context):
+        return self.set_sence_to_metric(blender_context)
+
+    # entrypoint for option via UI
+    def invoke(self, blender_context, event):
+        return blender_context.window_manager.invoke_props_dialog(self)
+
+
+    ###########################################################################
+    def set_sence_to_metric(self, blender_context):
+        set_sence_to_metric(blender_context)
+        return {"FINISHED"}
+
+
+###############################################################################
+def register():
+    register_class(Ms3dSetSceneToMetricOperator)
+    register_class(Ms3dGroupProperties)
+    register_class(Ms3dModelProperties)
+    register_class(Ms3dArmatureProperties)
+    register_class(Ms3dJointProperties)
+    register_class(Ms3dMaterialProperties)
+    inject_properties()
+    register_class(Ms3dSetSmoothingGroupOperator)
+    register_class(Ms3dGroupOperator)
+
+def unregister():
+    unregister_class(Ms3dGroupOperator)
+    unregister_class(Ms3dSetSmoothingGroupOperator)
+    delete_properties()
+    unregister_class(Ms3dMaterialProperties)
+    unregister_class(Ms3dJointProperties)
+    unregister_class(Ms3dArmatureProperties)
+    unregister_class(Ms3dModelProperties)
+    unregister_class(Ms3dGroupProperties)
+    unregister_class(Ms3dSetSceneToMetricOperator)
+
+def inject_properties():
+    Mesh.ms3d = PointerProperty(type=Ms3dModelProperties)
+    Armature.ms3d = PointerProperty(type=Ms3dArmatureProperties)
+    Bone.ms3d = PointerProperty(type=Ms3dJointProperties)
+    Material.ms3d = PointerProperty(type=Ms3dMaterialProperties)
+    Action.ms3d = PointerProperty(type=Ms3dArmatureProperties)
+    Group.ms3d = PointerProperty(type=Ms3dGroupProperties)
+
+def delete_properties():
+    del Mesh.ms3d
+    del Armature.ms3d
+    del Bone.ms3d
+    del Material.ms3d
+    del Action.ms3d
+    del Group.ms3d
+
+###############################################################################
+
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####
diff --git a/io_scene_ms3d/ms3d_utils.py b/io_scene_ms3d/ms3d_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..6617a8da703b6a7c0b1e5b739cd57ec0bb2b70fc
--- /dev/null
+++ b/io_scene_ms3d/ms3d_utils.py
@@ -0,0 +1,222 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+
+
+# ##### BEGIN COPYRIGHT BLOCK #####
+#
+# initial script copyright (c)2011,2012 Alexander Nussbaumer
+#
+# ##### END COPYRIGHT BLOCK #####
+
+
+#import python stuff
+from os import (
+        path
+        )
+
+
+# import io_scene_ms3d stuff
+from io_scene_ms3d.ms3d_strings import (
+        ms3d_str,
+        )
+
+
+#import blender stuff
+from bpy import (
+        ops,
+        )
+
+
+###############################################################################
+def enable_edit_mode(enable, blender_context):
+    if blender_context.active_object is None \
+            or not blender_context.active_object.type in {'MESH', 'ARMATURE', }:
+        return
+
+    if enable:
+        modeString = 'EDIT'
+    else:
+        modeString = 'OBJECT'
+
+    if ops.object.mode_set.poll():
+        ops.object.mode_set(mode=modeString)
+
+
+###############################################################################
+def enable_pose_mode(enable, blender_context):
+    if blender_context.active_object is None \
+            or not blender_context.active_object.type in {'ARMATURE', }:
+        return
+
+    if enable:
+        modeString = 'POSE'
+    else:
+        modeString = 'OBJECT'
+
+    if ops.object.mode_set.poll():
+        ops.object.mode_set(mode=modeString)
+
+
+###############################################################################
+def select_all(select):
+    if select:
+        actionString = 'SELECT'
+    else:
+        actionString = 'DESELECT'
+
+    if ops.object.select_all.poll():
+        ops.object.select_all(action=actionString)
+
+    if ops.mesh.select_all.poll():
+        ops.mesh.select_all(action=actionString)
+
+    if ops.pose.select_all.poll():
+        ops.pose.select_all(action=actionString)
+
+
+###############################################################################
+def pre_setup_environment(porter, blender_context):
+    # inject undo to porter
+    # and turn off undo
+    porter.undo = blender_context.user_preferences.edit.use_global_undo
+    blender_context.user_preferences.edit.use_global_undo = False
+
+    # inject active_object to self
+    porter.active_object = blender_context.scene.objects.active
+
+    # change to a well defined mode
+    enable_edit_mode(True, blender_context)
+
+    # enable face-selection-mode
+    blender_context.tool_settings.mesh_select_mode = (False, False, True)
+
+    # change back to object mode
+    enable_edit_mode(False, blender_context)
+
+    blender_context.scene.update()
+
+
+###############################################################################
+def post_setup_environment(porter, blender_context):
+    # restore active object
+    blender_context.scene.objects.active = porter.active_object
+
+    if not blender_context.scene.objects.active \
+            and blender_context.selected_objects:
+        blender_context.scene.objects.active \
+                = blender_context.selected_objects[0]
+
+    # restore pre operator undo state
+    blender_context.user_preferences.edit.use_global_undo = porter.undo
+
+
+###############################################################################
+def get_edge_split_modifier_add_if(blender_mesh_object):
+    blender_modifier = blender_mesh_object.modifiers.get(
+            ms3d_str['OBJECT_MODIFIER_SMOOTHING_GROUP'])
+
+    if blender_modifier is None:
+        blender_modifier = blender_mesh_object.modifiers.new(
+                ms3d_str['OBJECT_MODIFIER_SMOOTHING_GROUP'],
+                type='EDGE_SPLIT')
+        blender_modifier.show_expanded = False
+        blender_modifier.use_edge_angle = False
+        blender_modifier.use_edge_sharp = True
+
+        blender_mesh_object.data.show_edge_seams = True
+        blender_mesh_object.data.show_edge_sharp = True
+
+    return blender_modifier
+
+
+###########################################################################
+def rotation_matrix(v_track, v_up):
+    ## rotation matrix from two vectors
+    ## http://gamedev.stackexchange.com/questions/20097/how-to-calculate-a-3x3-rotation-matrix-from-2-direction-vectors
+    ## http://www.fastgraph.com/makegames/3drotation/
+    matrix = Matrix().to_3x3()
+
+    c1 = v_track
+    c1.normalize()
+
+    c0 = c1.cross(v_up)
+    c0.normalize()
+
+    c2 = c0.cross(c1)
+    c2.normalize()
+
+    matrix.col[0] = c0
+    matrix.col[1] = c1
+    matrix.col[2] = c2
+
+    return matrix
+
+
+###############################################################################
+def matrix_difference(mat_src, mat_dst):
+    mat_dst_inv = mat_dst.copy()
+    mat_dst_inv.invert()
+    return mat_dst_inv * mat_src
+
+
+###############################################################################
+def set_sence_to_metric(blender_context):
+    try:
+        # set metrics
+        blender_context.scene.unit_settings.system = 'METRIC'
+        blender_context.scene.unit_settings.system_rotation = 'DEGREES'
+        blender_context.scene.unit_settings.scale_length = 0.001 # 1.0mm
+        blender_context.scene.unit_settings.use_separate = False
+        blender_context.tool_settings.normal_size = 1.0 # 1.0mm
+
+        # set all 3D views to texture shaded
+        # and set up the clipping
+        for screen in blender_context.blend_data.screens:
+            for area in screen.areas:
+                if (area.type != 'VIEW_3D'):
+                    continue
+
+                for space in area.spaces:
+                    if (space.type != 'VIEW_3D'):
+                        continue
+
+                    #space.viewport_shade = 'SOLID'
+                    space.show_textured_solid = True
+                    space.clip_start = 0.1 # 0.1mm
+                    space.clip_end = 1000000.0 # 1km
+            #screen.scene.game_settings.material_mode = 'MULTITEXTURE'
+
+    except Exception:
+        raise
+
+    else:
+        pass
+
+
+###############################################################################
+
+###############################################################################
+#234567890123456789012345678901234567890123456789012345678901234567890123456789
+#--------1---------2---------3---------4---------5---------6---------7---------
+# ##### END OF FILE #####