#include "stdafx.h"

struct quaternion {
 float a, b, c, d;
};

struct bone {
 unsigned short index;
 unsigned short parent_index;
 char name[128];
 float relative_x;
 float relative_y;
 float relative_z;
 float absolute_x;
 float absolute_y;
 float absolute_z;
 quaternion q_relative;
 quaternion q_absolute;
 std::set<unsigned short> children;
};

bool extract_bmd6model(const char* filename, const char* savename, const char* bonename);
bool extract_bmodel(const char* filename, const char* savename);
bool extract_all_models_1(void);
bool extract_all_models_2(void);
void traverse_hierarchy(unsigned short node, std::deque<bone>& tree);

int main()
{
 extract_all_models_1();
 extract_all_models_2();
 return -1;
}

bool extract_all_models_1(void)
{
 // get all ism2 files
 deque<string> namelist;
 if(!get_filename_list(namelist, "bmodel")) return false;

 // create a list of save filenames
 deque<string> savelist;
 deque<string> bonelist;
 deque<string> dbuglist;
 for(size_t i = 0; i < namelist.size(); i++)
    {
     // extract filename parameters
     char param1[MAX_PATH];
     char param2[MAX_PATH];
     char param3[MAX_PATH];
     char param4[MAX_PATH];
     _splitpath(namelist[i].c_str(), param1, param2, param3, param4);

     // change extension
     string savename(param1);
     savename += param2;
     savename += param3;
     savename += ".ls";
     savelist.push_back(savename);
    }

 // extract
 for(size_t i = 0; i < namelist.size(); i++)
     extract_bmodel(namelist[i].c_str(), savelist[i].c_str());

 return true;
}

bool extract_all_models_2(void)
{
 // get all ism2 files
 deque<string> namelist;
 if(!get_filename_list(namelist, "bmd6model")) return false;

 // create a list of save filenames
 deque<string> savelist;
 deque<string> bonelist;
 deque<string> dbuglist;
 for(size_t i = 0; i < namelist.size(); i++)
    {
     // extract filename parameters
     char param1[MAX_PATH];
     char param2[MAX_PATH];
     char param3[MAX_PATH];
     char param4[MAX_PATH];
     _splitpath(namelist[i].c_str(), param1, param2, param3, param4);

     // change extension
     string savename(param1);
     savename += param2;
     savename += param3;
     savename += ".ls";
     savelist.push_back(savename);

     // change extension
     string bonename(param1);
     bonename += param2;
     bonename += param3;
     bonename += "_bones.ls";
     bonelist.push_back(bonename);

     // change extension
     string dbugname(param1);
     dbugname += param2;
     dbugname += param3;
     dbugname += ".txt";
     dbuglist.push_back(dbugname);
    }

 // extract
 for(size_t i = 0; i < namelist.size(); i++)
     extract_bmd6model(namelist[i].c_str(), savelist[i].c_str(), bonelist[i].c_str());

 return true;
}

bool extract_bmodel(const char* filename, const char* savename)
{
 // open file
 std::cout << "Processing file: " << filename << std::endl;
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("Could not open file.");

 // magic number
 if(BE_read_uint32(ifile) != 0x184C4D42) return error("Invalid magic number.");
 BE_read_uint32(ifile); // 0 or some number
 unsigned int n_surfaces = BE_read_uint32(ifile); // 1, 2, 3

 // create output file
 ofstream ofile(savename);
 ofile << "main" << endl;
 ofile << "{" << endl;

 // loop through surfaces
 size_t namelen = LE_read_uint32(ifile);
 size_t surface = 0;
 do {
    // read texture filename
    char name[1024];
    memset(name, 0, 1024);
    ifile.read(name, namelen);

    // store texture
    ofile << " editbegin();" << endl;
    ofile << " uvmap = VMap(\"texture\", \"" << name << "\", 2);" << endl;
    ofile << " editend();" << endl;

    // set layer and surface
    ofile << " lyrsetfg(" << (surface + 1) << ");" << endl;
    ofile << " setsurface(\"" << name  << "\");" << endl;

    // read 4-bytes (0xFFFFFFFF, 0, or 1)
    BE_read_uint32(ifile);

    // read point information
    size_t n_points = BE_read_uint32(ifile);
    size_t n_values = BE_read_uint32(ifile);
    size_t filetype = BE_read_uint32(ifile);
    cout << " points = " << n_points << endl;
    cout << " values = " << n_values << endl;
    cout << " filetype = " << filetype << endl;

    if(filetype == 0x1F)
      {
       // read 40 bytes
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
   
       // read points
       ofile << " editbegin();" << endl;
       for(size_t i = 0; i < n_points; i++) {
           float x = BE_read_float(ifile);
           float y = BE_read_float(ifile);
           float z = BE_read_float(ifile);
           float u = BE_read_float(ifile);
           float v = BE_read_float(ifile);
           BE_read_uint32(ifile);
           BE_read_uint32(ifile);
           BE_read_uint32(ifile);
           ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
           ofile << " uvmap.setValue(pointlist[" << (i + 1) << "]," << "@" << u << "," << v << "@);" << endl;
          }
       ofile << " editend();" << endl;

       // read triangles
       if(n_values % 3) return error("Indicies must be divisible by 3 for type 0x1F.");
   
       // read triangles
       ofile << " editbegin();" << endl;
       boost::shared_array<unsigned short> data(new unsigned short[n_values]);
       ifile.read((char*)&data.get()[0], n_values*sizeof(unsigned short));
       for(size_t i = 0; i < n_values; i++) reverse_byte_order(&data.get()[i]);
       ofile << " editbegin();" << endl;    
       size_t index = 0;
       for(size_t i = 0; i < n_values/3; i++) {
           unsigned short a = data.get()[index++] + 1;
           unsigned short b = data.get()[index++] + 1;
           unsigned short c = data.get()[index++] + 1;
           ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
          }
       ofile << " editend();" << endl;
      }
    else if(filetype == 0x63)
      {
       // read 40 bytes
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);

       // read points
       struct point { short x, y, z, w; unsigned short u, v; };
       boost::shared_array<point> points(new point[n_points]);
       ifile.read((char*)&points.get()[0], n_points*sizeof(point));

       ofile << " editbegin();" << endl;
       for(size_t i = 0; i < n_points; i++) {
           reverse_byte_order(&points[i].x);
           reverse_byte_order(&points[i].y);
           reverse_byte_order(&points[i].z);
           reverse_byte_order(&points[i].w);
           reverse_byte_order(&points[i].u);
           reverse_byte_order(&points[i].v);
           float x = points[i].x/32767.0f;
           float y = points[i].y/32767.0f;
           float z = points[i].z/32767.0f;
           float u = points[i].u/65535.0f;
           float v = points[i].v/65535.0f;
           ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
           ofile << " uvmap.setValue(pointlist[" << (i + 1) << "]," << "@" << u << "," << v << "@);" << endl;
          }
       ofile << " editend();" << endl;

       // read triangles
       if(n_values % 3) return error("Indicies must be divisible by 3 for type 0x1F.");
   
       // read triangles
       ofile << " editbegin();" << endl;
       boost::shared_array<unsigned short> data(new unsigned short[n_values]);
       ifile.read((char*)&data.get()[0], n_values*sizeof(unsigned short));
       for(size_t i = 0; i < n_values; i++) reverse_byte_order(&data.get()[i]);
       ofile << " editbegin();" << endl;    
       size_t index = 0;
       for(size_t i = 0; i < n_values/3; i++) {
           unsigned short a = data.get()[index++] + 1;
           unsigned short b = data.get()[index++] + 1;
           unsigned short c = data.get()[index++] + 1;
           ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
          }
       ofile << " editend();" << endl;
      }
    else if(filetype == 0x077)
      {
       // read 40 bytes
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);

       // read points (20 bytes per point)
       struct point { short x, y, z; unsigned short a, b, c, d, e, f, g; };
       boost::shared_array<point> points(new point[n_points]);
       ifile.read((char*)&points.get()[0], n_points*sizeof(point));

       ofile << " editbegin();" << endl;
       for(size_t i = 0; i < n_points; i++) {
           reverse_byte_order(&points[i].x);
           reverse_byte_order(&points[i].y);
           reverse_byte_order(&points[i].z);
           reverse_byte_order(&points[i].a);
           reverse_byte_order(&points[i].b);
           reverse_byte_order(&points[i].c);
           reverse_byte_order(&points[i].d);
           reverse_byte_order(&points[i].e);
           reverse_byte_order(&points[i].f);
           reverse_byte_order(&points[i].g);
           float x = points[i].x/32767.0f;
           float y = points[i].y/32767.0f;
           float z = points[i].z/32767.0f;
           float a = points[i].a/65535.0f;
           float b = points[i].b/65535.0f;
           float c = points[i].c/65535.0f;
           float d = points[i].d/65535.0f;
           float e = points[i].e/65535.0f;
           float f = points[i].f/65535.0f;
           float g = points[i].g/65535.0f;
           ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
           ofile << " uvmap.setValue(pointlist[" << (i + 1) << "]," << "@" << b << "," << c << "@);" << endl;
          }
       ofile << " editend();" << endl;

       // read triangles
       if(n_values % 3) return error("Indicies must be divisible by 3 for type 0x1F.");
   
       // read triangles
       ofile << " editbegin();" << endl;
       boost::shared_array<unsigned short> data(new unsigned short[n_values]);
       ifile.read((char*)&data.get()[0], n_values*sizeof(unsigned short));
       for(size_t i = 0; i < n_values; i++) reverse_byte_order(&data.get()[i]);
       ofile << " editbegin();" << endl;    
       size_t index = 0;
       for(size_t i = 0; i < n_values/3; i++) {
           unsigned short a = data.get()[index++] + 1;
           unsigned short b = data.get()[index++] + 1;
           unsigned short c = data.get()[index++] + 1;
           ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
          }
       ofile << " editend();" << endl;
      }
    else if(filetype == 0x21F)
      {
       // read 40 bytes
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
   
       // read points
       ofile << " editbegin();" << endl;
       for(size_t i = 0; i < n_points; i++) {
           float x = BE_read_float(ifile);
           float y = BE_read_float(ifile);
           float z = BE_read_float(ifile);
           float u = BE_read_float(ifile);
           float v = BE_read_float(ifile);
           BE_read_uint32(ifile);
           BE_read_uint32(ifile);
           BE_read_uint32(ifile);
           ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
           ofile << " uvmap.setValue(pointlist[" << (i + 1) << "]," << "@" << u << "," << v << "@);" << endl;
          }
       ofile << " editend();" << endl;

       // read triangles
       ofile << " editbegin();" << endl;
       deque<unsigned short> data;
       for(size_t i = 0; i < n_values; i++)
          {
           unsigned short temp = BE_read_uint16(ifile);
           if(temp != 0xFFFF) data.push_back(temp);
      
           if(temp == 0xFFFF || i == n_values - 1)
             {
              // must have at least 3 elements
              if(data.size() < 3)
                 return error("Expecting at least three tristrip indices.");
      
              // process first triangle
              unsigned short a = data[0] + 1;
              unsigned short b = data[1] + 1;
              unsigned short c = data[2] + 1;
              ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
      
              // process deque
              for(size_t j = 3; j < data.size(); j++) {
                  a = b;
                  b = c;
                  c = data[j] + 1;
                  if(j % 2) ofile << " addtriangle(pointlist[" << a << "], pointlist[" << b << "], pointlist[" << c << "]);" << endl;
                  else ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
                 }
      
              // clear deque
              data.erase(data.begin(), data.end());
              data.clear();
             }
          }
       ofile << " editend();" << endl;
       break;
      }
    else if(filetype == 0x263)
      {
       // read 40 bytes
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
   
       // read points
       struct point { short x, y, z, w; unsigned short u, v; };
       boost::shared_array<point> points(new point[n_points]);
       ifile.read((char*)&points.get()[0], n_points*sizeof(point));

       ofile << " editbegin();" << endl;
       for(size_t i = 0; i < n_points; i++) {
           reverse_byte_order(&points[i].x);
           reverse_byte_order(&points[i].y);
           reverse_byte_order(&points[i].z);
           reverse_byte_order(&points[i].w);
           reverse_byte_order(&points[i].u);
           reverse_byte_order(&points[i].v);
           float x = points[i].x/32767.0f;
           float y = points[i].y/32767.0f;
           float z = points[i].z/32767.0f;
           float u = points[i].u/65535.0f;
           float v = points[i].v/65535.0f;
           ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
           ofile << " uvmap.setValue(pointlist[" << (i + 1) << "]," << "@" << u << "," << v << "@);" << endl;
          }
       ofile << " editend();" << endl;
   
       // read triangles
       ofile << " editbegin();" << endl;
       deque<unsigned short> data;
       for(size_t i = 0; i < n_values; i++)
          {
           unsigned short temp = BE_read_uint16(ifile);
           if(temp != 0xFFFF) data.push_back(temp);
      
           if(temp == 0xFFFF || i == n_values - 1)
             {
              // must have at least 3 elements
              if(data.size() < 3)
                 return error("Expecting at least three tristrip indices.");
      
              // process first triangle
              unsigned short a = data[0] + 1;
              unsigned short b = data[1] + 1;
              unsigned short c = data[2] + 1;
              ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
      
              // process deque
              for(size_t j = 3; j < data.size(); j++) {
                  a = b;
                  b = c;
                  c = data[j] + 1;
                  if(j % 2) ofile << " addtriangle(pointlist[" << a << "], pointlist[" << b << "], pointlist[" << c << "]);" << endl;
                  else ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
                 }
      
              // clear deque
              data.erase(data.begin(), data.end());
              data.clear();
             }
          }
       ofile << " editend();" << endl;
      }
    else if(filetype == 0x277)
      {
       // read 40 bytes
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
       BE_read_float(ifile);
      
       // read points
       struct point { short x, y, z; unsigned short a, b, c, d, e, f, g; };
       boost::shared_array<point> points(new point[n_points]);
       ifile.read((char*)&points.get()[0], n_points*sizeof(point));

       ofile << " editbegin();" << endl;
       for(size_t i = 0; i < n_points; i++) {
           reverse_byte_order(&points[i].x);
           reverse_byte_order(&points[i].y);
           reverse_byte_order(&points[i].z);
           reverse_byte_order(&points[i].a);
           reverse_byte_order(&points[i].b);
           reverse_byte_order(&points[i].c);
           reverse_byte_order(&points[i].d);
           reverse_byte_order(&points[i].e);
           reverse_byte_order(&points[i].f);
           reverse_byte_order(&points[i].g);
           float x = points[i].x/32767.0f;
           float y = points[i].y/32767.0f;
           float z = points[i].z/32767.0f;
           float a = points[i].a/65535.0f;
           float b = points[i].b/65535.0f;
           float c = points[i].c/65535.0f;
           float d = points[i].d/65535.0f;
           float e = points[i].e/65535.0f;
           float f = points[i].f/65535.0f;
           float g = points[i].g/65535.0f;
           ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
           ofile << " uvmap.setValue(pointlist[" << (i + 1) << "]," << "@" << b << "," << c << "@);" << endl;
          }
       ofile << " editend();" << endl;

       // read triangles
       ofile << " editbegin();" << endl;
       deque<unsigned short> data;
       for(size_t i = 0; i < n_values; i++)
          {
           unsigned short temp = BE_read_uint16(ifile);
           if(temp != 0xFFFF) data.push_back(temp);
      
           if(temp == 0xFFFF || i == n_values - 1)
             {
              // must have at least 3 elements
              if(data.size() > 2)
                {
                 // process first triangle
                 unsigned short a = data[0] + 1;
                 unsigned short b = data[1] + 1;
                 unsigned short c = data[2] + 1;
                 ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
      
                 // process deque
                 for(size_t j = 3; j < data.size(); j++) {
                     a = b;
                     b = c;
                     c = data[j] + 1;
                     if(j % 2) ofile << " addtriangle(pointlist[" << a << "], pointlist[" << b << "], pointlist[" << c << "]);" << endl;
                     else ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
                    }
                }
      
              // clear deque
              data.erase(data.begin(), data.end());
              data.clear();
             }
          }
       ofile << " editend();" << endl;
      }
    else {
       Sleep(10000);
       return error("Cannot process surface. Unknown surface type.");
      }

    // increment number of surfaces processed
    surface++;

    // read footer
    char footer[28];
    ifile.read(&footer[0], 28);

    // continue processing?
    if(BE_read_uint32(ifile) != 0x184C4D42) {
       Sleep(10000);
       return error("Invalid magic number.");
      }
    namelen = LE_read_uint32(ifile);
 }
 while(surface < n_surfaces);

 // finalize lscript
 ofile << "}" << endl;

 return true;
}

void traverse_hierarchy(unsigned short node, std::deque<bone>& tree)
{
 typedef std::set<unsigned short>::iterator set_iterator;
 for(set_iterator i = tree[node].children.begin(); i != tree[node].children.end(); i++) {
     tree[*i].absolute_x += tree[node].relative_x;
     tree[*i].absolute_y += tree[node].relative_y;
     tree[*i].absolute_z += tree[node].relative_z;
     traverse_hierarchy(*i, tree);
    }
}

bool extract_bmd6model(const char* filename, const char* savename, const char* bonename)
{
 std::cout << "Processing file: " << filename << std::endl;
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("Could not open file.");

 // 0x00
 BE_read_uint32(ifile);
 BE_read_uint16(ifile);
 BE_read_uint16(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);

 // 0x10
 BE_read_float(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);

 // 0x20
 BE_read_uint08(ifile);
 BE_read_uint16(ifile);
 BE_read_uint16(ifile);
 BE_read_uint16(ifile);
 BE_read_uint16(ifile);
 BE_read_uint16(ifile);

 unsigned short offset1 = BE_read_uint16(ifile); cout << "offset1 = " << offset1 << endl;
 unsigned short offset2 = BE_read_uint16(ifile);
 unsigned short n_strings_actual = BE_read_uint16(ifile);
 unsigned short n_strings = (n_strings_actual + 7) & (~7);
 boost::shared_array<unsigned short> offsets(new unsigned short[22]);
 ifile.read((char*)&offsets[0], 44);
 for(size_t i = 0; i < 22; i++) reverse_byte_order(&offsets[i]);

 // bone list
 std::deque<bone> bone_list;
 for(size_t i = 0; i < n_strings; i++) {
     bone b = { i, 0xFFFF, "", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
     bone_list.push_back(b);
    }

 // read quaternions
 cout << "n_strings_actual = " << n_strings_actual << endl;
 cout << "n_strings = " << n_strings << endl;
 for(size_t i = 0; i < n_strings; i++) {
     float q[4];
     ifile.read((char*)&q[0], 16);
     reverse_byte_order(&q[0]);
     reverse_byte_order(&q[1]);
     reverse_byte_order(&q[2]);
     reverse_byte_order(&q[3]);
     bone_list[i].q_relative.a = q[0];
     bone_list[i].q_relative.b = q[1];
     bone_list[i].q_relative.c = q[2];
     bone_list[i].q_relative.d = q[3];
     bone_list[i].q_absolute.a = q[0];
     bone_list[i].q_absolute.b = q[1];
     bone_list[i].q_absolute.c = q[2];
     bone_list[i].q_absolute.d = q[3];
     //cout << "q = " << q[0] << "," << q[1] << "," << q[2] << "," << q[3] << endl;
    }

 // read scale data
 for(size_t i = 0; i < n_strings; i++) {
     float s[3];
     ifile.read((char*)&s[0], 12);
     reverse_byte_order(&s[0]);
     reverse_byte_order(&s[1]);
     reverse_byte_order(&s[2]);
    }

 // read translation data
 deque<sl::vector3D<float>> translation_data;
 for(size_t i = 0; i < n_strings; i++) {
     float t[3];
     ifile.read((char*)&t[0], 12);
     reverse_byte_order(&t[0]);
     reverse_byte_order(&t[1]);
     reverse_byte_order(&t[2]);
     sl::vector3D<float> temp(t[0], t[1], t[2]);
     translation_data.push_back(temp);
     bone_list[i].relative_x = bone_list[i].absolute_x = t[0];
     bone_list[i].relative_y = bone_list[i].absolute_y = t[1];
     bone_list[i].relative_z = bone_list[i].absolute_z = t[2];
    }

 // read bone parent data
 typedef std::map<unsigned short, std::set<unsigned short>> map_type;

 // bone hierarchy
 for(size_t i = 0; i < n_strings; i++)
    {
     // get parent
     unsigned short parent;
     ifile.read((char*)&parent, sizeof(parent));
     reverse_byte_order(&parent);

     // add child
     if(parent == 0xFFFF) continue;
     bone_list[parent].children.insert(i);
     bone_list[i].parent_index = parent;
    }

 traverse_hierarchy(0, bone_list);

 // move to bone strings
 ifile.seekg(0x2D + offset1);

 // read bone strings
 for(size_t i = 0; i < n_strings; i++) {
     unsigned int slen = LE_read_uint32(ifile);
     if(!slen) continue;
     char buffer[1024];
     ifile.read((char*)&buffer[0], slen);
     buffer[slen] = '\0';
     memmove(bone_list[i].name, buffer, strlen(buffer) + 1);
    }

 // open destination file
 //ofstream bfile(bonename);
 //if(!bfile) return error("Could not create output file.");

 // save bone heirarchy
 //bfile << "@version 2.3" << endl;
 //bfile << "@warnings" << endl;
 //bfile << "@script generic" << endl;
 //bfile << "@name ShowHide" << endl;
 //bfile << "generic" << endl;
 //bfile << "{" << endl;

 for(size_t i = 0; i < bone_list.size(); i++)
    {
     // add child bone
     if(bone_list[i].parent_index != 0xFFFF) {
        //bfile << " SelectByName(\"" << bone_list[bone_list[i].parent_index].name << "\");" << endl;
        //bfile << " AddChildBone(\"" << bone_list[i].name << "\");" << endl;
        //bfile << " BoneType(2);" << endl;
        //bfile << " Position(" << bone_list[i].absolute_x << "," << bone_list[i].absolute_y << "," << bone_list[i].absolute_z << ");" << endl;
       }
     else if(strlen(bone_list[i].name) > 0) {
        //bfile << " AddBone(\"" << bone_list[i].name << "\");" << endl;
        //bfile << " BoneType(2);" << endl;
        //bfile << " Position(" << bone_list[i].absolute_x << "," << bone_list[i].absolute_y << "," << bone_list[i].absolute_z << ");" << endl;
       }
    }
 //bfile << "}" << endl;

 // read bone information
 boost::shared_array<unsigned char> bone_info(new unsigned char[n_strings]);
 ifile.read((char*)&bone_info.get()[0], n_strings);

 // read rotation matrices
 size_t bone_matrix_size = 12*n_strings;
 boost::shared_array<float> bone_matrix(new float[bone_matrix_size]);
 ifile.read((char*)&bone_matrix.get()[0], bone_matrix_size*sizeof(float));
 for(size_t i = 0; i < bone_matrix_size; i++) reverse_byte_order(&bone_matrix.get()[i]);

 // read (min, max)
 BE_read_float(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);
 BE_read_float(ifile);

 // read number of surfaces
 size_t surfaces = BE_read_uint32(ifile);
 if(!surfaces) return error("No surfaces");
 cout << " surfaces = " << surfaces << endl;

 // create file
 ofstream ofile(savename);
 ofile << "main" << endl;
 ofile << "{" << endl;

 // for each surface
 for(size_t s = 0; s < surfaces; s++)
    {
     // read surface name
     char sname[1024];
     memset(sname, 0, 1024);
     unsigned int sname_size = LE_read_uint32(ifile);
     if(sname_size) ifile.read(&sname[0], sname_size);
     else sprintf(sname, "surface_%d", s);

     // read path name
     char pname[1024];
     memset(pname, 0, 1024);
     unsigned int pname_size = LE_read_uint32(ifile);
     if(pname_size) ifile.read(&pname[0], pname_size);
     else sprintf(pname, "path_%d", s);

     // set foreground layer and surface name
     ofile << " lyrsetfg(" << (s + 1) << ");" << endl;
     ofile << " setsurface(\"" << sname  << "\");" << endl;

     // create UV map
     ofile << " editbegin();" << endl;
     ofile << " uvmap_" << (s + 1) << " = VMap(\"texture\", \"" << sname << "\", 2);" << endl;
     ofile << " editend();" << endl;

     // read surface information
     unsigned char infobyte = BE_read_uint08(ifile);
     size_t n_points = BE_read_uint32(ifile);
     size_t n_triangles = BE_read_uint32(ifile);
     cout << " points = " << n_points << endl;
     cout << " triangles = " << n_triangles << endl;
     BE_read_float(ifile);
     BE_read_float(ifile);
     BE_read_float(ifile);
     BE_read_float(ifile);
     BE_read_float(ifile);
     BE_read_float(ifile);

     // read points
     ofile << " editbegin();" << endl;
     for(size_t i = 0; i < n_points; i++)
        {
         float x = BE_read_float(ifile);
         float y = BE_read_float(ifile);
         float z = BE_read_float(ifile);
         float u = BE_read_float(ifile);
         float v = BE_read_float(ifile);
         BE_read_uint16(ifile);
         BE_read_uint16(ifile);
         BE_read_uint16(ifile);
         BE_read_uint16(ifile);
         BE_read_uint16(ifile);
         BE_read_uint16(ifile);
         ofile << " pointlist[" << i + 1 << "]" << " = " << "addpoint(" << x << "," << y << "," << z << ");" << endl;
         ofile << " uvmap_" << (s + 1) << ".setValue(pointlist[" << (i + 1) << "]," << "@" << u << "," << v << "@);" << endl;
        }
     ofile << " editend();" << endl;

     // read triangles
     boost::shared_array<unsigned short> data(new unsigned short[n_triangles*3]);
     ifile.read((char*)&data.get()[0], n_triangles*3*sizeof(unsigned short));
     for(size_t i = 0; i < 3*n_triangles; i++) reverse_byte_order(&data.get()[i]);
     ofile << " editbegin();" << endl;    
     size_t index = 0;
     for(size_t i = 0; i < n_triangles; i++) {
         unsigned short a = data.get()[index++] + 1;
         unsigned short b = data.get()[index++] + 1;
         unsigned short c = data.get()[index++] + 1;
         ofile << " addtriangle(pointlist[" << a << "], pointlist[" << c << "], pointlist[" << b << "]);" << endl;
        }
     ofile << " editend();" << endl;

     // padding?
     BE_read_uint32(ifile); // usually 0
     BE_read_uint32(ifile); // usually number of bones repeated
     unsigned int has_weights = BE_read_uint08(ifile); // usually 0, but can be 1
     BE_read_uint32(ifile); // usually 0

     // load weights
     if(has_weights) {
        boost::shared_array<float> weights(new float[n_points]);
        ifile.read((char*)&weights.get()[0], n_points*sizeof(float));
        for(size_t i = 0; i < n_points; i++) reverse_byte_order(&weights[i]);
       }
    }

 ofile << "}" << endl;

 return true;
}