#include "xentax.h"
#include "xb360_onechanbara2.h"

namespace XB360 { namespace Onechanbara2 {

struct SURFACEINFO {
 float32 bbox_xmin;
 float32 bbox_ymin;
 float32 bbox_zmin;
 float32 bbox_xmax;
 float32 bbox_ymax;
 float32 bbox_zmax;
 uint32  index;
 uint16  elem;
 uint16  type;
 uint32  name;
};

struct TEXTASSINFO {
 uint32 material_index;
 uint32 trilist;
};

struct TRILISTINFO {
 uint32 triangles;
 uint32 offset;
};

class extractor {
 private :
  std::string pathname;
 private :
  bool decompress(const char* filename, const char* extension);
  bool processPK1(const char* filename);
  bool processPK2(const char* filename);
  bool processPK3(const char* filename);
  bool processTEX(const char* filename);
  bool processMDL(const char* filename);
  bool processTMO(const char* filename);
 public :
  bool extract(void);
 public :
  extractor(const char* pn);
 ~extractor();
};

std::string GetTEXFromMDL(const std::string& filename);
bool GetFilenamesFromTEX(const std::string& texfile, std::deque<std::string>& filenames);

};};

namespace XB360 { namespace Onechanbara2 {

extractor::extractor(const char* pn) : pathname(pn)
{
}

extractor::~extractor()
{
}

bool extractor::extract(void)
{
 using namespace std;

 // CAT files are archives that do not contain filenames
 bool decompressCAT = true;
 bool doPK1 = true;

 // CAN files are archives that also contain filenames
 bool decompressCAN = true;
 bool doPK2 = true;

 // CMP files are variable files in the demo, gamegui, and sys directories
 bool decompressCMP = true;
 bool doPK3 = true;

 // XPR files are texture files
 bool decompressXPR = true;
 bool doTEX = true;

 // TMD files are model files
 bool decompressTMD = true;
 bool doMDL = true;
 bool doTMO = false;

 // decompress CAT files
 if(decompressCAT) {
    cout << "STAGE 1" << endl;
    cout << "Decompressing CAT files to PK1 files using XBox360 unbundler." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "cat", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!decompress(filelist[i].c_str(), "pk1")) return false;
       }
    cout << endl;
   }

 // decompress CAN files
 if(decompressCAN) {
    cout << "STAGE 2" << endl;
    cout << "Decompressing CAN files to PK2 files using XBox360 unbundler." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "can", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!decompress(filelist[i].c_str(), "pk2")) return false;
       }
    cout << endl;
   }

 // decompress CMP files
 if(decompressCMP) {
    cout << "STAGE 3" << endl;
    cout << "Decompressing CMP files to PK3 files using XBox360 unbundler." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "cmp", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!decompress(filelist[i].c_str(), "pk3")) return false;
       }
    cout << endl;
   }

 // decompress XPR files
 if(decompressXPR) {
    cout << "STAGE 4" << endl;
    cout << "Decompressing XPR files to TEX files using XBox360 unbundler." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "xpr", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!decompress(filelist[i].c_str(), "tex")) return false;
       }
    cout << endl;
   }

 // decompress TMD files
 if(decompressTMD) {
    cout << "STAGE 5" << endl;
    cout << "Decompressing TMD files to MDL files using XBox360 unbundler." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "tmd", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!decompress(filelist[i].c_str(), "mdl")) return false;
       }
    cout << endl;
   }

 // process PK1 files
 if(doPK1) {
    cout << "STAGE 6" << endl;
    cout << "Processing PK1 files." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "pk1", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!processPK1(filelist[i].c_str())) return false;
       }
    cout << endl;
   }

 // process PK2 files
 if(doPK2) {
    cout << "STAGE 7" << endl;
    cout << "Processing PK2 files." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "pk2", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!processPK2(filelist[i].c_str())) return false;
       }
    cout << endl;
   }

 // process PK3 files
 if(doPK3) {
    cout << "STAGE 8" << endl;
    cout << "Processing PK3 files." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "pk3", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!processPK3(filelist[i].c_str())) return false;
       }
    cout << endl;
   }

 // process TEX files
 if(doTEX) {
    cout << "STAGE 9" << endl;
    cout << "Processing TEX files." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "tex", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!processTEX(filelist[i].c_str())) return false;
       }
    cout << endl;
   }

 // process MDL files
 if(doMDL) {
    cout << "STAGE 10" << endl;
    cout << "Processing MDL files." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "mdl", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!processMDL(filelist[i].c_str())) return false;
       }
    cout << endl;
   }

 // process TMO files
 if(doTMO) {
    cout << "STAGE 11" << endl;
    cout << "Processing TMO files." << endl;
    deque<string> filelist;
    BuildFilenameList(filelist, "tmo", pathname.c_str());
    for(size_t i = 0; i < filelist.size(); i++) {
        cout << "Processing file " << (i + 1) << " of " << filelist.size() << ": " << filelist[i] << "." << endl;
        if(!processTMO(filelist[i].c_str())) return false;
       }
    cout << endl;
   }

 //processMDL("C:\\Users\\semory\\sticklove\\source\\xentax\\Release\\onechanbara\\Player\\pl00_00_00\\000.mdl");
 //processTMO("C:\\Users\\semory\\sticklove\\source\\xentax\\Release\\onechanbara\\Player\\pl00_00mot\\000.tmo");

 return true;
}

bool extractor::decompress(const char* filename, const char* extension)
{
 using namespace std;

 // make sure file is compressed
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("Failed to open file.");
 uint32 magic = BE_read_uint32(ifile);
 if(ifile.fail()) return error("Read failure.");

 // file is not compressed
 if(!(magic >= 0xFF51200 && magic <= 0xFF512FF))
   {
    // build filename
    string ofname = GetPathnameFromFilename(filename);
    ofname += GetShortFilenameWithoutExtension(filename);
    ofname += ".";
    ofname += extension;

    // create output file
    ofstream ofile(ofname.c_str(), ios::binary);
    if(!ofile) return error("Failed to create output file.");

    // compute filesize
    ifile.seekg(0, ios::end);
    size_t filesize = (size_t)ifile.tellg();
    ifile.seekg(0, ios::beg);

    // read data
    boost::shared_array<char> data(new char[filesize]);
    ifile.read(data.get(), filesize);
    if(ifile.fail()) return error("Read failure.");
    ifile.close();

    // copy data
    ofile.write(data.get(), filesize);

    // delete original
    //DeleteFileA(filename);
    return true;
   }
 else
    ifile.close();

 // command line
 string param1 = "\"";
 param1 += pathname;
 param1 += "xbdecompress.exe";
 param1 += "\"";
 param1 += " ";
 param1 += "\"";
 param1 += filename;
 param1 += "\"";
 param1 += " ";
 param1 += "\"";
 param1 += GetPathnameFromFilename(filename);
 param1 += GetShortFilenameWithoutExtension(filename);
 param1 += ".";
 param1 += extension;
 param1 += "\"";

 // process startup information
 STARTUPINFOA sinfo;
 ZeroMemory(&sinfo, sizeof(sinfo));
 sinfo.cb = sizeof(sinfo);

 // process information
 PROCESS_INFORMATION pinfo;
 ZeroMemory(&pinfo, sizeof(pinfo));

 // run xbdecompress
 char buffer[1024];
 memmove(buffer, param1.c_str(), param1.length() + 1);
 if(!CreateProcessA(NULL, buffer, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &sinfo, &pinfo)) return error("Failed to run xbdecompress on XPR file.");
 WaitForSingleObject(pinfo.hProcess, INFINITE);
 CloseHandle(pinfo.hProcess);
 CloseHandle(pinfo.hThread);

 // delete original
 //DeleteFileA(filename);
 return true;
}

bool extractor::processPK1(const char* filename)
{
 using namespace std;

 // open file
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("error");

 // read header
 std::deque<std::pair<size_t, size_t>> header;
 for(;;) {
     uint32 p01 = BE_read_uint32(ifile); if(ifile.fail()) return error("Read failure.");
     uint32 p02 = BE_read_uint32(ifile); if(ifile.fail()) return error("Read failure.");
     if(p01 == 0xFFFFFFFF && p02 == 0xFFFFFFFF) break;
     if(header.size() > 1000) return error("Unexpected number of PAK sections.");
     header.push_back(std::pair<size_t, size_t>(p01, p02));
    }

 // create directory to store files
 string pakpath = GetPathnameFromFilename(filename);
 pakpath += GetShortFilenameWithoutExtension(filename);
 pakpath += "\\";
 CreateDirectoryA(pakpath.c_str(), NULL);

 // process files
 deque<string> subpacks;
 for(size_t i = 0; i < header.size(); i++)
    {
     // move to data
     ifile.seekg(header[i].first);
     if(ifile.fail()) return error("Seek failure.");
     if(header[i].second == 0) continue;

     // read data
     boost::shared_array<char> data(new char[header[i].second]);
     ifile.read(data.get(), header[i].second);
     if(ifile.fail()) return error("Read failure.");

     // create filename
     stringstream ss;
     ss << pakpath << setfill('0') << setw(3) << i << ".";
     uint32 value = *reinterpret_cast<uint32*>(data.get());
     switch(value) {
       case(0x746D6F30) : ss << "tmo"; break;
       case(0x746D6430) : ss << "mdl"; break;
       case(0x32525058) : ss << "tex"; break;
       case(0x54495400) : ss << "tit"; break;
       case(0x544C4900) : ss << "tli"; break;
       case(0x50524D70) : ss << "prm"; break;
       case(0x52435354) : ss << "rcs"; break;
       case(0x44544F4C) : ss << "dto"; break;
       case(0x4C43504F) : ss << "lcp"; break;
       case(0x4244534C) : ss << "bds"; break;
       case(0x4553544D) : ss << "mts"; break;
       case(0x64697242) : ss << "dir"; break;
       case(0x6C6C6F52) : ss << "llo"; break;
       case(0x706F7250) : ss << "por"; break;
       case(0x7366664F) : ss << "sff"; break;
       case(0x74697753) : ss << "tiw"; break;
       case(0x00000000) : ss << "dat"; break;
       case(0x01000000) : ss << "dat"; break;
       case(0x01000100) : ss << "dat"; break;
       case(0x01000200) : ss << "dat"; break;
       case(0x01000300) : ss << "dat"; break;
       case(0x01000400) : ss << "dat"; break;
       case(0x01000500) : ss << "dat"; break;
       case(0x02000000) : ss << "pla"; break;
       case(0x10000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x20000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x30000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x40000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x50000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x60000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x70000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x80000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0x90000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0xA0000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0xB0000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       case(0xC0000000) : ss << "pk1"; subpacks.push_back(ss.str()); break; // more pk1 files!
       default : ss << "dat";
      }

     // save data
     ofstream ofile(ss.str().c_str(), ios::binary);
     ofile.write(data.get(), header[i].second);
    }

 // delete original
 ifile.close();
 //DeleteFileA(filename);

 // process subpacks
 for(size_t i = 0; i < subpacks.size(); i++)
     if(!processPK1(subpacks[i].c_str())) return false;

 return true;
}

bool extractor::processPK2(const char* filename)
{
 using namespace std;

 // open file
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("error");

 // filesize
 ifile.seekg(0, ios::end);
 size_t filesize = (size_t)ifile.tellg();
 ifile.seekg(0, ios::beg);

 // read header
 struct triple { size_t offset, size; string name; };
 deque<triple> header;
 size_t position = 0;
 for(;;)
    {
     // seek position
     ifile.seekg(position);
     if(ifile.fail()) return error("Seek failure.");

     // read item
     uint32 p01 = BE_read_uint32(ifile); if(ifile.fail()) return error("Read failure.");
     uint32 p02 = BE_read_uint32(ifile); if(ifile.fail()) return error("Read failure.");
     if(p01 == 0xFFFFFFFF && p02 == 0xFFFFFFFF) break;

     // name follows
     bool is_name = false;
     uint32 namelen = 0;
     if(p01 < p02) {
        if((p02 - p01) == 0x10) { namelen = 0x10; is_name = true; }
        else if((p02 - p01) == 0x20) { namelen = 0x20; is_name = true; }
        else if((p02 - p01) == 0x30) { namelen = 0x30; is_name = true; }
        else if(p02 == 0xFFFFFFFF) { namelen = filesize - p01; is_name = true; }
       }

     // extract name
     if(is_name) {
        ifile.seekg(p01);
        char buffer[1024];
        read_string(ifile, buffer, 1024);
        header.back().name = buffer;
       }
     else {
        triple item;
        item.offset = p01;
        item.size = p02;
        header.push_back(item);
       }

     // move position
     if(is_name) position += 4;
     else position += 8;
    }

 // create directory to store files
 string sendpath = GetPathnameFromFilename(filename);
 sendpath += GetShortFilenameWithoutExtension(filename);
 sendpath += "\\";
 CreateDirectoryA(sendpath.c_str(), NULL);

 // process files
 deque<string> subpacks;
 for(size_t i = 0; i < header.size(); i++)
    {
     // move to data
     ifile.seekg(header[i].offset);
     if(ifile.fail()) return error("Seek failure.");
     if(header[i].size == 0) continue;

     // read data
     boost::shared_array<char> data(new char[header[i].size]);
     ifile.read(data.get(), header[i].size);
     if(ifile.fail()) return error("Read failure.");

     // create filename
     stringstream ss;
     ss << sendpath;
     if(header[i].name.length()) ss << GetShortFilenameWithoutExtension(header[i].name) << ".";
     else ss << setfill('0') << setw(3) << i << ".";

     // save data
     uint32 value = *reinterpret_cast<uint32*>(data.get());
     switch(value) {
       case(0x746D6F30) : ss << "tmo"; break;
       case(0x746D6430) : ss << "mdl"; break;
       case(0x32525058) : ss << "tex"; break;
       case(0x54495400) : ss << "tit"; break;
       case(0x544C4900) : ss << "tli"; break;
       case(0x50524D70) : ss << "prm"; break;
       case(0x52435354) : ss << "rcs"; break;
       case(0x44544F4C) : ss << "dto"; break;
       case(0x4C43504F) : ss << "lcp"; break;
       case(0x4244534C) : ss << "bds"; break;
       case(0x4553544D) : ss << "mts"; break;
       case(0x64697242) : ss << "dir"; break;
       case(0x6C6C6F52) : ss << "llo"; break;
       case(0x706F7250) : ss << "por"; break;
       case(0x7366664F) : ss << "sff"; break;
       case(0x74697753) : ss << "tiw"; break;
       case(0x00000000) : ss << "dat"; break;
       case(0x01000000) : ss << "dat"; break;
       case(0x01000100) : ss << "dat"; break;
       case(0x01000200) : ss << "dat"; break;
       case(0x01000300) : ss << "dat"; break;
       case(0x01000400) : ss << "dat"; break;
       case(0x01000500) : ss << "dat"; break;
       case(0x02000000) : ss << "pla"; break;
       case(0x10000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x20000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x30000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x40000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x50000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x60000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x70000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x80000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0x90000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0xA0000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0xB0000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       case(0xC0000000) : ss << "pak"; subpacks.push_back(ss.str()); break; // more pak files!
       default : ss << "dat";
      }

     // save data
     ofstream ofile(ss.str().c_str(), ios::binary);
     ofile.write(data.get(), header[i].size);
    }

 // delete original
 ifile.close();
 //DeleteFileA(filename);

 // process subpacks
 for(size_t i = 0; i < subpacks.size(); i++)
     if(!processPK1(subpacks[i].c_str())) return false;

 return true;
}

bool extractor::processPK3(const char* filename)
{
 using namespace std;

 // open file
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("Failed to open file.");

 // read magic
 uint32 magic = BE_read_uint32(ifile);
 if(ifile.fail()) return error("Read failure.");

 // determine file extension
 string extension = "";
 if(magic == 0x30646D74) extension = "mdl";
 else if(magic == 0x58505232) extension = "tex";
 else if(magic == 0x54455854) extension = "sub";
 else return error("Unknown PK3 file format.");

 // build filename
 string ofname = GetPathnameFromFilename(filename);
 ofname += GetShortFilenameWithoutExtension(filename);
 ofname += ".";
 ofname += extension;

 // create output file
 ofstream ofile(ofname.c_str(), ios::binary);
 if(!ofile) return error("Failed to create output file.");

 // compute filesize
 ifile.seekg(0, ios::end);
 size_t filesize = (size_t)ifile.tellg();
 ifile.seekg(0, ios::beg);

 // read data
 boost::shared_array<char> data(new char[filesize]);
 ifile.read(data.get(), filesize);
 if(ifile.fail()) return error("Read failure.");
 ifile.close();

 // copy data
 ofile.write(data.get(), filesize);

 // delete original
 //DeleteFileA(filename);
 return true;
}

bool extractor::processTEX(const char* filename)
{
 using namespace std;

 // file properties
 string filepath = GetPathnameFromFilename(filename);
 string shrtname = GetShortFilename(filename);

 // create storage path
 string savepath = GetPathnameFromFilename(filename);
 savepath += GetShortFilenameWithoutExtension(filename);
 savepath += "\\";
 CreateDirectoryA(savepath.c_str(), NULL);

 // create storage path subdirectory
 // some XPR files have subdirectories, and unless you have a subdirectory name that way, unbundler won't save anything
 // fortunately, the subdirectory is always called tex
 string savepathsubdir = savepath;
 savepathsubdir += "tex\\";
 CreateDirectoryA(savepathsubdir.c_str(), NULL);

 // unbundle and delete RDF files
 bool retval = XB360Unbundle(pathname.c_str(), filename, savepath.c_str());
 string rdffile = filepath;
 rdffile += shrtname;
 rdffile += ".rdf";
 DeleteFileA(rdffile.c_str());
 return retval;
}

bool extractor::processMDL(const char* filename)
{
 // validate
 using namespace std;
 if(!filename || !strlen(filename)) return error("Invalid filename.");

 // variables
 string fn_path = GetPathnameFromFilename(filename);
 string fn_name = GetShortFilenameWithoutExtension(filename);

 // data
 uint32 fileinfo[30];
 deque<SURFACEINFO> surface_data;
 deque<TEXTASSINFO> textass_data;
 deque<TRILISTINFO> trilist_data;

 // data
 GEOMETRY geometry;
 MATERIAL_LIST materials;
 TEXTURE_LIST textures;
 SKELETON_LIST skeletons;

 // initialize geometry
 geometry.id = "mesh";
 geometry.name = "mesh";
 geometry.meshlist.push_back(boost::shared_ptr<MESH>(new MESH()));

 // enable debug mode
 bool debug = true;
 ofstream dfile;
 if(debug) {
    stringstream ss;
    ss << fn_path << fn_name << ".txt";
    dfile.open(ss.str().c_str());
   }

 // open file
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("Failed to open MDL file.");

 //
 // PROCESS #01
 // READ AND PROCESS FILE HEADER
 //

 // read header
 auto head01 = BE_read_uint32(ifile);
 auto head02 = BE_read_uint32(ifile);
 auto head03 = BE_read_uint32(ifile);
 auto head04 = BE_read_uint32(ifile);
 auto head05 = BE_read_float32(ifile);
 auto head06 = BE_read_float32(ifile);
 auto head07 = BE_read_float32(ifile);
 auto head08 = BE_read_float32(ifile);
 auto head09 = BE_read_float32(ifile);
 auto head10 = BE_read_float32(ifile);
 if(debug) dfile << "HEADER" << endl;
 if(debug) dfile << head01 << endl;
 if(debug) dfile << head02 << endl;
 if(debug) dfile << head03 << endl;
 if(debug) dfile << head04 << endl;
 if(debug) dfile << head05 << endl;
 if(debug) dfile << head06 << endl;
 if(debug) dfile << head07 << endl;
 if(debug) dfile << head08 << endl;
 if(debug) dfile << head09 << endl;
 if(debug) dfile << head10 << endl;
 if(debug) dfile << endl;

 // validate header
 if(head01 != 0x30646D74) return error("Invalid MDL file.");
 if(head04 != 0x28) return error("Invalid MDL file.");

 // determine vertex size
 uint32 vertexsize = 0;
 switch(head02) {
   case(0x1D01AF) : vertexsize = 0x0C; break; // OK (HUD)
   case(0x1D01BF) : vertexsize = 0x0C; break; // OK (PLAYER WEAPONS)
   case(0x1D02BF) : vertexsize = 0x0C; break; // OK
   case(0x1D80A7) : vertexsize = 0x18; break; // OK
   case(0x1D80AF) : vertexsize = 0x1C; break; // OK
   case(0x1D81A7) : vertexsize = 0x18; break; // OK
   case(0x1D81AF) : vertexsize = 0x1C; break; // OK
   case(0x1D82A7) : vertexsize = 0x18; break; // OK (HUGE MAPS)
   case(0x1D82AF) : vertexsize = 0x1C; break; // OK (HUGE MAPS)
   case(0x1DB8A7) : vertexsize = 0x20; break; // OK (LEVEL MODELS)
   case(0x1DB8AF) : vertexsize = 0x24; break; // OK (LEVEL MODELS)
   case(0x1DB9A7) : vertexsize = 0x20; break; // OK (PLAYER AND ENEMIES)
   default : {
        stringstream msg;
        msg << "Unknown vertex type " << head02 << ".";
        return error(msg.str().c_str());
       }
 }

 // determine index size and type
 unsigned char indextype = FACE_FORMAT_UINT_16;
 unsigned char indexsize = 2;
 if(head02 == 0x1D02BF || head02 == 0x1D82A7 || head02 == 0x1D82AF) {
    indextype = FACE_FORMAT_UINT_32;
    indexsize = 4;
   }

 //
 // PROCESS #02
 // READ AND SAVE FILE INFORMATION
 //
 if(debug) dfile << "FILE INFORMATION" << endl;
 for(size_t i = 0; i < 30; i++) fileinfo[i] = BE_read_uint32(ifile);
 if(debug) for(size_t i = 0; i < 30; i++) if(debug) dfile << fileinfo[i] << endl;
 if(debug) dfile << endl;

 //
 // PROCESS #03
 // READ SURFACE INFORMATION PART 1
 //

 // move to surface information
 if(debug) dfile << "SURFACE INFORMATION PART 1" << endl;
 ifile.seekg(fileinfo[0]);
 if(ifile.fail()) return error("Seek failure.");

 // process surfaces
 uint32 n_surfaces = fileinfo[1];
 if(n_surfaces == 0 || n_surfaces > 1000) return error("Unexpected number of surfaces.");
 for(uint32 i = 0; i < n_surfaces; i++)
    {
     auto param01 = BE_read_float32(ifile);
     auto param02 = BE_read_float32(ifile);
     auto param03 = BE_read_float32(ifile);
     auto param04 = BE_read_float32(ifile);
     auto param05 = BE_read_float32(ifile);
     auto param06 = BE_read_float32(ifile);
     auto param07 = BE_read_uint32(ifile);
     auto param08 = BE_read_uint16(ifile);
     auto param09 = BE_read_uint16(ifile);
     auto param10 = BE_read_uint32(ifile);
     auto param11 = BE_read_uint32(ifile);
     auto param12 = BE_read_uint32(ifile);
     auto param13 = BE_read_uint32(ifile);
     auto param14 = BE_read_uint32(ifile);
     auto param15 = BE_read_uint32(ifile);
     if(debug) dfile << setw(3) << param07 << " ";
     if(debug) dfile << setw(3) << param08 << " ";
     if(debug) dfile << setw(3) << param09 << " ";
     if(debug) dfile << param10 << " ";
     if(debug) dfile << setw(3) << param11 << " ";
     if(debug) dfile << setw(3) << param12 << " ";
     if(debug) dfile << setw(3) << param13 << " ";
     if(debug) dfile << setw(3) << param14 << " ";
     if(debug) dfile << endl;
     SURFACEINFO si;
     si.bbox_xmin = param01;
     si.bbox_ymin = param02;
     si.bbox_zmin = param03;
     si.bbox_xmax = param04;
     si.bbox_ymax = param05;
     si.bbox_zmax = param06;
     si.index = param07;
     si.elem = param08;
     si.type = param09;
     si.name = param10;
     surface_data.push_back(si);
    }
  if(debug) dfile << endl;

 //
 // PROCESS #04
 // READ SURFACE INFORMATION PART 2
 //

 // move to surface information
 if(debug) dfile << "SURFACE INFORMATION PART 2" << endl;
 ifile.seekg(fileinfo[2]);
 if(ifile.fail()) return error("Seek failure.");

 // read surface information
 uint32 n_values = fileinfo[3];
 for(size_t i = 0; i < n_surfaces; i++)
    {
     // read group information
     boost::shared_array<uint16> data(new uint16[surface_data[i].elem]);
     BE_read_array(ifile, data.get(), surface_data[i].elem);
     if(ifile.fail()) return error("Read failure.");

     // 20XX reference to part 4
     // 40XX reference to part 3
     // 30XX reference to group
     uint16 curr_0x2000 = 0xFFFF; // texture_data reference
     uint16 curr_0x3000 = 0xFFFF; // trilist_data reference
     uint16 curr_0x4000 = 0xFFFF; // unknown reference

     // for each item read
     for(size_t j = 0; j < surface_data[i].elem; j++) {
         if(data[j] == 0x1000) {
            curr_0x2000 = 0xFFFF;
            curr_0x3000 = 0xFFFF;
            curr_0x4000 = 0xFFFF;
           }
         else if((data[j] & 0xFF00) == 0x2000) curr_0x2000 = (uint16)(data[j] & 0x00FF);
         else if((data[j] & 0xFF00) == 0x3000) {
            curr_0x3000 = (uint16)(data[j] & 0x00FF);
            TEXTASSINFO info = { curr_0x2000, curr_0x3000 };
            textass_data.push_back(info);
           }
         else if((data[j] & 0xFF00) == 0x4000) curr_0x4000 = (uint16)(data[j] & 0x00FF);
        }
    }
 if(debug) dfile << "Number of texture-group associations = " << textass_data.size() << endl;
 if(debug) for(size_t i = 0; i < textass_data.size(); i++)  dfile << textass_data[i].material_index << "," << textass_data[i].trilist << endl;
 if(debug) dfile << endl;

 //
 // PROCESS #05
 // READ SURFACE INFORMATION PART 3
 //

 if(fileinfo[6] != 0)
   {
    // move to surface information
    if(debug) dfile << "SURFACE INFORMATION PART 3" << endl;
    ifile.seekg(fileinfo[6]);
    if(ifile.fail()) return error("Seek failure.");
   
    // read data
    uint32 n_items = fileinfo[7];
    if(debug) dfile << "Number of items = " << n_items << endl;
    for(uint32 i = 0; i < n_items; i++)
       {
        auto p01 = BE_read_uint32(ifile);
        auto p02 = BE_read_uint32(ifile);
        auto p03 = BE_read_uint32(ifile);
        auto p04 = BE_read_uint32(ifile);
        auto p05 = BE_read_uint32(ifile);
        auto p06 = BE_read_uint32(ifile);
        auto p07 = BE_read_uint32(ifile);
        auto p08 = BE_read_uint32(ifile);
        auto p09 = BE_read_uint32(ifile);
        auto p10 = BE_read_uint32(ifile);
        auto p11 = BE_read_uint32(ifile);
        auto p12 = BE_read_uint32(ifile);
        auto p13 = BE_read_uint32(ifile);
        auto p14 = BE_read_uint32(ifile);
        auto p15 = BE_read_uint32(ifile);
        auto p16 = BE_read_uint32(ifile);
        auto p17 = BE_read_uint32(ifile);
        auto p18 = BE_read_uint32(ifile);
        auto p19 = BE_read_uint32(ifile);
        auto p20 = BE_read_uint32(ifile);
        auto p21 = BE_read_uint32(ifile);
        auto p22 = BE_read_uint32(ifile);
        auto p23 = BE_read_uint32(ifile);
        auto p24 = BE_read_uint32(ifile);
        auto p25 = BE_read_uint32(ifile);
        auto p26 = BE_read_uint32(ifile);
        auto p27 = BE_read_uint32(ifile);
        auto p28 = BE_read_uint32(ifile);
        auto p29 = BE_read_uint32(ifile);
        auto p30 = BE_read_uint32(ifile);
        auto p31 = BE_read_uint32(ifile);
        auto p32 = BE_read_uint32(ifile);
        auto p33 = BE_read_uint32(ifile);
        if(debug) dfile << setw(3) << p01 << " ";
        if(debug) dfile << setw(3) << p02 << " ";
        if(debug) dfile << setw(3) << p03 << " ";
        if(debug) dfile << setw(3) << p04 << " ";
        if(debug) dfile << setw(3) << p05 << " ";
        if(debug) dfile << setw(3) << p06 << " ";
        if(debug) dfile << setw(3) << p07 << " ";
        if(debug) dfile << setw(3) << p08 << " ";
        if(debug) dfile << setw(3) << p09 << " ";
        if(debug) dfile << setw(3) << p10 << " ";
        if(debug) dfile << setw(3) << p11 << " ";
        if(debug) dfile << setw(3) << p12 << " ";
        if(debug) dfile << setw(3) << p13 << " ";
        if(debug) dfile << setw(3) << p14 << " ";
        if(debug) dfile << setw(3) << p15 << " ";
        if(debug) dfile << setw(3) << p16 << " ";
        if(debug) dfile << setw(3) << p17 << " ";
        if(debug) dfile << setw(3) << p18 << " ";
        if(debug) dfile << setw(3) << p19 << " ";
        if(debug) dfile << setw(3) << p20 << " ";
        if(debug) dfile << setw(3) << p21 << " ";
        if(debug) dfile << setw(3) << p22 << " ";
        if(debug) dfile << setw(3) << p23 << " ";
        if(debug) dfile << setw(3) << p24 << " ";
        if(debug) dfile << setw(3) << p25 << " ";
        if(debug) dfile << setw(3) << p26 << " ";
        if(debug) dfile << setw(3) << p27 << " ";
        if(debug) dfile << setw(3) << p28 << " ";
        if(debug) dfile << setw(3) << p29 << " ";
        if(debug) dfile << setw(3) << p30 << " ";
        if(debug) dfile << setw(3) << p31 << " ";
        if(debug) dfile << setw(3) << p32 << " ";
        if(debug) dfile << setw(3) << p33 << " ";
        if(debug) dfile << endl;
       }
    if(debug) dfile << endl;
   }

 //
 // PROCESS #06
 // READ MATERIALS
 //

 if(fileinfo[8] != 0)
   {
    // move to surface information
    if(debug) dfile << "MATERIALS" << endl;
    ifile.seekg(fileinfo[8]);
    if(ifile.fail()) return error("Seek failure.");
    
    // read data
    uint32 n_items = fileinfo[9];
    if(debug) dfile << "Number of items = " << n_items << endl;

    for(uint32 i = 0; i < n_items; i++)
       {
        // read material
        auto p01 = BE_read_uint32 (ifile); // 0x05
        auto p02 = BE_read_uint32 (ifile); // 0x00
        auto p03 = BE_read_uint32 (ifile); // base texture index
        auto p04 = BE_read_float32(ifile); // crap
        auto p05 = BE_read_float32(ifile); // crap
        auto p06 = BE_read_float32(ifile); // crap
        auto p07 = BE_read_float32(ifile); // crap
        auto p08 = BE_read_uint32 (ifile); // 0xFFFFFFFF
        auto p09 = BE_read_uint32 (ifile); // 0xFFFFFFFF
        auto p10 = BE_read_float32(ifile); // crap
        auto p11 = BE_read_float32(ifile); // crap
        auto p12 = BE_read_float32(ifile); // crap
        auto p13 = BE_read_float32(ifile); // crap
        auto p14 = BE_read_uint32 (ifile); // 0xFFFFFFFF
        auto p15 = BE_read_uint32 (ifile); // 0xFFFFFFFF
        auto p16 = BE_read_float32(ifile); // crap
        auto p17 = BE_read_float32(ifile); // crap
        auto p18 = BE_read_float32(ifile); // crap
        auto p19 = BE_read_float32(ifile); // end of 3 4xfloats
        auto p20 = BE_read_uint32 (ifile); // 0x03
        auto p21 = BE_read_uint32 (ifile); // normal map index
        auto p22 = BE_read_float32(ifile); // crap
        auto p23 = BE_read_float32(ifile); // crap
        auto p24 = BE_read_float32(ifile); // crap
        auto p25 = BE_read_float32(ifile); // crap
        auto p26 = BE_read_uint32 (ifile); // 0x04
        auto p27 = BE_read_uint32 (ifile); // specular map index
        auto p28 = BE_read_float32(ifile); // crap
        auto p29 = BE_read_float32(ifile); // crap
        auto p30 = BE_read_float32(ifile); // crap
        auto p31 = BE_read_float32(ifile); // crap
        auto p32 = BE_read_uint32 (ifile); // 0x01
        auto p33 = BE_read_uint32 (ifile); // 0x00
        auto p34 = BE_read_uint32 (ifile); // 0x01
        auto p35 = BE_read_uint32 (ifile); // 0x01
        auto p36 = BE_read_uint32 (ifile); // 0x01
        auto p37 = BE_read_float32(ifile); // crap
        auto p38 = BE_read_float32(ifile); // crap
        auto p39 = BE_read_float32(ifile); // crap
        auto p40 = BE_read_float32(ifile); // crap
        auto p41 = BE_read_float32(ifile); // crap
        auto p42 = BE_read_float32(ifile); // crap
        auto p43 = BE_read_float32(ifile); // crap
        auto p44 = BE_read_float32(ifile); // crap
        auto p45 = BE_read_float32(ifile); // crap
        auto p46 = BE_read_float32(ifile); // crap
        auto p47 = BE_read_float32(ifile); // crap
        auto p48 = BE_read_float32(ifile); // crap
        auto p49 = BE_read_float32(ifile); // crap
        auto p50 = BE_read_float32(ifile); // crap
        if(debug) dfile << p01 << " "; // << endl;
        if(debug) dfile << p02 << " "; // << endl;
        if(debug) dfile << p03 << " "; // << endl;
        if(debug) dfile << p08 << " "; // << endl;
        if(debug) dfile << p09 << " "; // << endl;
        if(debug) dfile << p14 << " "; // << endl;
        if(debug) dfile << p15 << " "; // << endl;
        if(debug) dfile << p20 << " "; // << endl;
        if(debug) dfile << p21 << " "; // << endl;
        if(debug) dfile << p26 << " "; // << endl;
        if(debug) dfile << p27 << " "; // << endl;
        if(debug) dfile << p32 << " "; // << endl;
        if(debug) dfile << p33 << " "; // << endl;
        if(debug) dfile << p34 << " "; // << endl;
        if(debug) dfile << p35 << " "; // << endl;
        if(debug) dfile << p36 << " "; // << endl;
        if(debug) dfile << endl;

        // build material name
        stringstream name;
        name << "material_" << setfill('0') << setw(3) << i;

        // save material
        MATERIAL m;
        m.id = name.str();
        m.basemap = p03;
        m.normmap = p21;
        m.specmap = p27;
        m.bumpmap = 0xFFFFFFFF;
        materials.push_back(m);
       }
    if(debug) dfile << endl;
   }

 //
 // PROCESS #07
 // READ TRIANGLE GROUP DATA
 //

 if(fileinfo[10])
   {
    // move to triangle information
    if(debug) dfile << "TRIANGLE GROUPS (TRIANGLES, POSITION)" << endl;
    ifile.seekg(fileinfo[10]);
    if(ifile.fail()) return error("Seek failure.");

    // number of groups
    uint32 n_groups = fileinfo[11];
    if(n_groups == 0) return error("Invalid number of triangle groups.");

    // read triangle information
    if(indextype == FACE_FORMAT_UINT_16) {
       for(uint32 i = 0; i < n_groups; i++) {
           uint32 p1 = BE_read_uint16(ifile); // number of triangles
           BE_read_uint16(ifile);             // always 0
           uint32 p2 = BE_read_uint32(ifile); // triangle offset
           TRILISTINFO info = { p1, p2 };
           trilist_data.push_back(info);
           if(debug) dfile << "g[" << i << "] = (" << p1 << ", " << p2 << ")" << endl;
          }
      }
    else if(indextype == FACE_FORMAT_UINT_32) {
       for(uint32 i = 0; i < n_groups; i++) {
           uint32 p1 = BE_read_uint32(ifile); // number of triangles
           uint32 p2 = BE_read_uint32(ifile); // triangle offset
           TRILISTINFO info = { p1, p2 };
           trilist_data.push_back(info);
           if(debug) dfile << "g[" << i << "] = (" << p1 << ", " << p2 << ")" << endl;
          }
      }
    if(debug) dfile << endl;
   }

 //
 // PROCESS #08
 // READ INDEX BUFFER
 //

 // move to index buffer
 if(debug) dfile << "INDEX BUFFER DATA" << endl;
 ifile.seekg(fileinfo[12]);
 if(ifile.fail()) return error("Seek failure.");

 // number of triangles
 uint32 n_triangles = fileinfo[13];
 if(n_triangles == 0) return error("Invalid number of triangles.");

 // process triangle groups
 for(size_t i = 0; i < trilist_data.size(); i++)
    {
     // get texture-material association data
     TEXTASSINFO ai = textass_data[i];
     if(!(ai.material_index < materials.size())) return error("Texture association data is invalid.");
     uint32 ib_material = ai.material_index;

     // surface name
     stringstream ss;
     ss << "surface_" << setfill('0') << setw(3) << i;

     // move to data offset
     uint32 ib_offset = 3*indexsize*trilist_data[i].offset;
     ifile.seekg(fileinfo[12] + ib_offset);
     if(ifile.fail()) return error("Seek failure.");

     // read index buffer
     uint32 ib_tris = trilist_data[i].triangles;
     uint32 ib_elem = ib_tris*3;
     uint32 ib_char = ib_elem*indexsize;
     boost::shared_array<char> ib_data(new char[ib_char]);
     if(indextype == FACE_FORMAT_UINT_16) BE_read_array(ifile, reinterpret_cast<uint16*>(ib_data.get()), ib_elem);
     else BE_read_array(ifile, reinterpret_cast<uint32*>(ib_data.get()), ib_elem);
     if(ifile.fail()) return error("Read failure.");

     // create index buffer
     IDX_BUFFER ib;
     ib.format = indextype;
     ib.type = FACE_TYPE_TRIANGLES;
     ib.name = ss.str();
     ib.elem = ib_elem;
     ib.data = ib_data;
     ib.material = ib_material;

     // save index buffer
     geometry.meshlist[0]->surfaces.push_back(ib);
    }
 if(debug) dfile << endl;

 //
 // PROCESS #09
 // READ VERTEX BUFFER
 //

 // move to vertex buffer
 if(debug) dfile << "VERTEX BUFFER DATA" << endl;
 ifile.seekg(fileinfo[15]);
 if(ifile.fail()) return error("Seek failure.");

 // number of vertices
 uint32 n_vertices = fileinfo[14];
 if(n_vertices == 0) return error("Invalid number of vertices.");

 // create vertex buffer
 geometry.meshlist[0]->vertices.flags = VERTEX_POSITION | VERTEX_UV;
 geometry.meshlist[0]->vertices.elem = n_vertices;
 geometry.meshlist[0]->vertices.data.reset(new VERTEX[n_vertices]);
 if(head02 == 0x1DB9A7) {
    geometry.meshlist[0]->vertices.flags |= VERTEX_WEIGHTS;
    geometry.meshlist[0]->vertices.skeleton = 0;
   }

 // read vertices
 uint32 max_bone_index = 0;
 for(size_t i = 0; i < n_vertices; i++)
    {
     // 0x1D02AF
     // 0x0C (12 bytes)
     // OK
     if(head02 == 0x1D01AF)
       {
        float v01 = BE_read_float32(ifile); //  4
        float v02 = BE_read_float32(ifile); //  8
        float v03 = BE_read_float32(ifile); // 12
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu = 0.0f;
        geometry.meshlist[0]->vertices.data[i].tv = 0.0f;
       }
     // 0x1D01BF
     // 0x0C (12 bytes)
     // OK
     if(head02 == 0x1D01BF)
       {
        float v01 = BE_read_float32(ifile); //  4
        float v02 = BE_read_float32(ifile); //  8
        float v03 = BE_read_float32(ifile); // 12
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu = 0.0f;
        geometry.meshlist[0]->vertices.data[i].tv = 0.0f;
       }
     // 0x1D02BF
     // 0x0C (12 bytes)
     // OK
     else if(head02 == 0x1D02BF)
       {
        float v01 = BE_read_float32(ifile); //  4
        float v02 = BE_read_float32(ifile); //  8
        float v03 = BE_read_float32(ifile); // 12
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu = 0.0f;
        geometry.meshlist[0]->vertices.data[i].tv = 0.0f;
       }
     // 0x1D80A7
     // 0x18 (24 bytes)
     // OK
     else if(head02 == 0x1D80A7)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1D80A7
     // 0x18 (24 bytes)
     // OK
     else if(head02 == 0x1D81A7)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz = -v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1D80AF
     // 0x1C (28 bytes)
     // OK
     else if(head02 == 0x1D80AF)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        auto v09 = BE_read_uint32(ifile);  // 28
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << v09 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1D81AF
     // 0x1C (28 bytes)
     // OK
     else if(head02 == 0x1D81AF)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        auto v09 = BE_read_uint32(ifile);  // 28
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << v09 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1D82A7
     // 0x18 (24 bytes)
     // OK
     else if(head02 == 0x1D82A7)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1D82AF
     // 0x1C (28 bytes)
     // OK
     else if(head02 == 0x1D82AF)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        auto v09 = BE_read_uint32(ifile);  // 28
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << v09 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1DB8A7
     // 0x20 (32 bytes)
     // OK
     else if(head02 == 0x1DB8A7)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint32(ifile);  // 24
        auto v09 = BE_read_uint16(ifile);  // 26
        auto v10 = BE_read_uint16(ifile);  // 28
        auto v11 = BE_read_uint16(ifile);  // 30
        auto v12 = BE_read_uint16(ifile);  // 32
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << v09 << endl;
        if(debug) dfile << v10 << endl;
        if(debug) dfile << v11 << endl;
        if(debug) dfile << v12 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1DB8AF
     // 0x24 (36 bytes)
     // OK
     else if(head02 == 0x1DB8AF)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_sint16(ifile);  // 14
        auto v05 = BE_read_sint16(ifile);  // 16
        auto v06 = BE_read_sint16(ifile);  // 18
        auto v07 = BE_read_sint16(ifile);  // 20
        auto v08 = BE_read_uint16(ifile);  // 22
        auto v09 = BE_read_uint16(ifile);  // 24
        auto v10 = BE_read_uint32(ifile);  // 28
        auto v11 = BE_read_uint32(ifile);  // 32
        auto v12 = BE_read_uint32(ifile);  // 36
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << v04 << endl;
        if(debug) dfile << v05 << endl;
        if(debug) dfile << v06 << endl;
        if(debug) dfile << v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << v09 << endl;
        if(debug) dfile << v10 << endl;
        if(debug) dfile << v11 << endl;
        if(debug) dfile << v12 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v06/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v07/1023.0f;
       }
     // 0x1DB9A7
     // 0x20 (32 bytes)
     // OK
     else if(head02 == 0x1DB9A7)
       {
        auto v01 = BE_read_float32(ifile); //  4
        auto v02 = BE_read_float32(ifile); //  8
        auto v03 = BE_read_float32(ifile); // 12
        auto v04 = BE_read_uint08(ifile);  // 14
        auto v05 = BE_read_uint08(ifile);  // 15
        auto v06 = BE_read_uint08(ifile);  // 16
        auto v07 = BE_read_uint08(ifile);  // 17
        auto v08 = BE_read_sint16(ifile);  // 18
        auto v09 = BE_read_sint16(ifile);  // 20
        auto v10 = BE_read_uint32(ifile);  // 24
        auto v11 = BE_read_uint08(ifile);  // 25
        auto v12 = BE_read_uint08(ifile);  // 26
        auto v13 = BE_read_uint08(ifile);  // 27
        auto v14 = BE_read_uint08(ifile);  // 28
        auto v15 = BE_read_uint08(ifile);  // 29
        auto v16 = BE_read_uint08(ifile);  // 30
        auto v17 = BE_read_uint08(ifile);  // 31
        auto v18 = BE_read_uint08(ifile);  // 32
        if(debug) dfile << "vertex " << i << endl;
        if(debug) dfile << v01 << endl;
        if(debug) dfile << v02 << endl;
        if(debug) dfile << v03 << endl;
        if(debug) dfile << (uint16)v04 << endl;
        if(debug) dfile << (uint16)v05 << endl;
        if(debug) dfile << (uint16)v06 << endl;
        if(debug) dfile << (uint16)v07 << endl;
        if(debug) dfile << v08 << endl;
        if(debug) dfile << v09 << endl;
        if(debug) dfile << v10 << endl;
        if(debug) dfile << (uint16)v11 << endl;
        if(debug) dfile << (uint16)v12 << endl;
        if(debug) dfile << (uint16)v13 << endl;
        if(debug) dfile << (uint16)v14 << endl;
        if(debug) dfile << (uint16)v15 << endl;
        if(debug) dfile << (uint16)v16 << endl;
        if(debug) dfile << (uint16)v17 << endl;
        if(debug) dfile << (uint16)v18 << endl;
        if(debug) dfile << endl;
        geometry.meshlist[0]->vertices.data[i].vx =  v01;
        geometry.meshlist[0]->vertices.data[i].vy =  v02;
        geometry.meshlist[0]->vertices.data[i].vz =  v03;
        geometry.meshlist[0]->vertices.data[i].tu =  v08/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v09/1023.0f;
        geometry.meshlist[0]->vertices.data[i].w1 = v11/255.0f;
        geometry.meshlist[0]->vertices.data[i].w2 = v12/255.0f;
        geometry.meshlist[0]->vertices.data[i].w3 = v13/255.0f;
        geometry.meshlist[0]->vertices.data[i].w4 = v14/255.0f;
        geometry.meshlist[0]->vertices.data[i].b1 = v15;
        geometry.meshlist[0]->vertices.data[i].b2 = v16;
        geometry.meshlist[0]->vertices.data[i].b3 = v17;
        geometry.meshlist[0]->vertices.data[i].b4 = v18;

        //if(v11 > max_bone_index) max_bone_index = v11; // max is 255
        //if(v12 > max_bone_index) max_bone_index = v12; // max is 127
        //if(v13 > max_bone_index) max_bone_index = v13; // max is 81
        //if(v14 > max_bone_index) max_bone_index = v14; // max is 9

        //if(v15 > max_bone_index) max_bone_index = v15; // max is 31
        //if(v16 > max_bone_index) max_bone_index = v16; // max is 31
        //if(v17 > max_bone_index) max_bone_index = v17; // max is 
        //if(v18 > max_bone_index) max_bone_index = v18; // max is 
       }
    }
 if(debug) dfile << endl;
 //cout << "Max bone index = " << max_bone_index << endl;

 //
 // PROCESS #10
 // READ SEPARATE UV DATA
 //

 // move to offset
 if(debug) dfile << "SEPARATE UV DATA" << endl;
 ifile.seekg(fileinfo[17]);
 if(ifile.fail()) return error("Seek failure.");
 if(head02 == 0x1D01AF || head02 == 0x1D01BF || head02 == 0x1D02BF) {
    for(size_t i = 0; i < n_vertices; i++) {
        auto v1 = BE_read_sint16(ifile);
        auto v2 = BE_read_sint16(ifile);
        if(debug) dfile << "v1 = " << v1 << endl;
        if(debug) dfile << "v2 = " << v2 << endl;
        geometry.meshlist[0]->vertices.data[i].tu =  v1/1023.0f;
        geometry.meshlist[0]->vertices.data[i].tv = -v2/1023.0f;
       }
   }
 if(debug) dfile << endl;

 //
 // PROCESS #11
 // READ BONE MATRICES
 //

 // process bones
 if(fileinfo[23])
   {
    // move to bone matrices
    if(debug) dfile << "BONE MATRICES" << endl;
    ifile.seekg(fileinfo[23]);
    if(ifile.fail()) return error("Seek failure.");

    // data to save
    deque<boost::shared_array<float>> mlist;
    JOINTTREE jtree;

    // read matrices
    uint32 n_bones = fileinfo[24];
    if(debug) dfile << "number of bones = " << n_bones << endl;
    for(size_t i = 0; i < n_bones; i++) {
        boost::shared_array<float> m(new float[16]);
        m[ 0] = BE_read_float32(ifile);
        m[ 1] = BE_read_float32(ifile);
        m[ 2] = BE_read_float32(ifile);
        m[ 3] = BE_read_float32(ifile);
        m[ 4] = BE_read_float32(ifile);
        m[ 5] = BE_read_float32(ifile);
        m[ 6] = BE_read_float32(ifile);
        m[ 7] = BE_read_float32(ifile);
        m[ 8] = BE_read_float32(ifile);
        m[ 9] = BE_read_float32(ifile);
        m[10] = BE_read_float32(ifile);
        m[11] = BE_read_float32(ifile);
        m[12] = BE_read_float32(ifile);
        m[13] = BE_read_float32(ifile);
        m[14] = BE_read_float32(ifile);
        m[15] = BE_read_float32(ifile);
        mlist.push_back(m);
        if(debug) dfile << m[ 0] << " " << m[ 1] << " " << m[ 2] << " " << m[ 3] << endl;
        if(debug) dfile << m[ 4] << " " << m[ 5] << " " << m[ 6] << " " << m[ 7] << endl;
        if(debug) dfile << m[ 8] << " " << m[ 9] << " " << m[10] << " " << m[11] << endl;
        if(debug) dfile << m[12] << " " << m[13] << " " << m[14] << " " << m[15] << endl;
        if(debug) dfile << endl;
       }

    // move to bone information
    if(debug) dfile << "BONES" << endl;
    ifile.seekg(fileinfo[25]);
    if(ifile.fail()) return error("Seek failure.");
    if(n_bones != fileinfo[26]) return error("Number of joints do not match.");

    // read bone information
    for(size_t i = 0; i < n_bones; i++)
       {
        // joint
        JOINT joint;
        uint32 index = (uint32)i;
   
        // joint name
        char name[5];
        ifile.read(&name[0], 4);
        std::reverse(&name[0], &name[4]);
        name[4] = '\0';
        joint.name = name;
   
        // joint matrix
        joint.matrix = mlist[i];

        // joint relative positions
        float x = BE_read_float32(ifile);
        float y = BE_read_float32(ifile);
        float z = BE_read_float32(ifile);
        joint.rel_x = x;
        joint.rel_y = y;
        joint.rel_z = z;

        // set parent
        uint32 parent = BE_read_uint32(ifile);
        uint32 xxxxxx = BE_read_uint32(ifile);
        joint.parent = parent;
   
        // insert joint
        jtree.InsertJoint(index, joint);

        if(debug) dfile << "index = " << index << endl;   
        if(debug) dfile << "name = " << name << endl;   
        if(debug) dfile << "x = " << x << endl;
        if(debug) dfile << "y = " << y << endl;
        if(debug) dfile << "z = " << z << endl;
        if(debug) dfile << "parent = " << parent << endl;
        if(debug) dfile << "xxxxxx = " << xxxxxx << endl;
        if(debug) dfile << endl;
       }

    // print joint tree in debug mode
    if(debug) {
       stringstream ss;
       jtree.PrintJointTree(ss);
       dfile << ss.str() << endl;
      }

    // insert skeleton
    SKELETON skel = { "skeleton", jtree };
    skeletons.push_back(skel);
   }

 //
 // PROCESS #12
 // READ TEXTURES
 //

 if(fileinfo[27])
   {
    // move to texture information
    if(debug) dfile << "TEXTURES" << endl;
    ifile.seekg(fileinfo[27]);
    if(ifile.fail()) return error("Seek failure.");
   
    // read texture information
    uint32 n_textures = fileinfo[28];
    for(uint32 i = 0; i < n_textures; i++)
       {
        uint32 param1 = BE_read_uint32(ifile); // index
        uint32 param2 = BE_read_uint32(ifile); // 0
        uint16 param3 = BE_read_uint16(ifile); // 0x0001
        uint16 param4 = BE_read_uint16(ifile); // 0x0200 | 0x0400
        uint16 param5 = BE_read_uint16(ifile); // 0x0200 | 0x0400
        uint16 param6 = BE_read_uint16(ifile); // 0x2000 | 0xFF00
        if(debug) dfile << "param1 = " << param1 << endl;
        if(debug) dfile << "param2 = " << param2 << endl;
        if(debug) dfile << "param3 = " << param3 << endl;
        if(debug) dfile << "param4 = " << param4 << endl;
        if(debug) dfile << "param5 = " << param5 << endl;
        if(debug) dfile << "param6 = " << param6 << endl;
        if(debug) dfile << endl;
   
        // texture id
        stringstream id;
        id << "texture_" << setfill('0') << setw(3) << param1;
   
        // texture filename
        stringstream fn;
        fn << setfill('0') << setw(3) << param1 << ".dds.tga";
   
        // add texture
        TEXTURE item;
        item.id = id.str();
        item.filename = fn.str();
        textures.push_back(item);
       }
    if(debug) dfile << endl;
   }

 // replace texture names
 string texfile = GetTEXFromMDL(filename);
 if(texfile.length()) {
    deque<string> filelist;
    GetFilenamesFromTEX(texfile, filelist);
    if(filelist.size() == textures.size()) {
       for(size_t i = 0; i < textures.size(); i++) {
           string shortname = GetShortFilename(filelist[i]);
           textures[i].filename = shortname;
           textures[i].filename += ".tga";
          }
      }
    else {
       stringstream ss;
       ss << "The number of textures do not match. ";
       ss << "Expecting " << textures.size() << " but obtained " << filelist.size() << "."; 
       error(ss.str().c_str());
      }
   }

 // save geometry
 return GeometryToDAE(fn_path.c_str(), fn_name.c_str(), geometry, materials, textures, skeletons);
}

bool extractor::processTMO(const char* filename)
{
 ifstream ifile(filename, ios::binary);
 if(!ifile) return error("");

 uint32 head01 = BE_read_uint32(ifile);
 uint32 head02 = BE_read_uint32(ifile);
 uint32 head03 = BE_read_uint32(ifile);
 uint32 head04 = BE_read_uint32(ifile);
 uint32 head05 = BE_read_uint32(ifile);
 uint32 head06 = BE_read_uint32(ifile); // number of bones
 uint32 head07 = BE_read_uint32(ifile);
 uint32 head08 = BE_read_uint32(ifile); // number of bones
 uint32 head09 = BE_read_uint32(ifile);
 uint32 head10 = BE_read_uint32(ifile);
 uint32 head11 = BE_read_uint32(ifile);
 uint32 head12 = BE_read_uint32(ifile);
 uint32 head13 = BE_read_uint32(ifile);
 uint32 head14 = BE_read_uint32(ifile);
 uint32 head15 = BE_read_uint32(ifile);
 uint32 head16 = BE_read_uint32(ifile);
 cout << head01 << endl;
 cout << head02 << endl;
 cout << head03 << endl;
 cout << head04 << endl;
 cout << head05 << endl;
 cout << head06 << endl;
 cout << head07 << endl;
 cout << head08 << endl;
 cout << head09 << endl;
 cout << head10 << endl;
 cout << head11 << endl;
 cout << head12 << endl;
 cout << head13 << endl;
 cout << head14 << endl;
 cout << head15 << endl;
 cout << head16 << endl;

 // read bone information
 uint32 n_bones = head06;
 for(size_t i = 0; i < n_bones; i++)
    {
     // joint name
     char name[5];
     ifile.read(&name[0], 4);
     std::reverse(&name[0], &name[4]);
     name[4] = '\0';

     // joint relative positions
     float x = BE_read_float32(ifile);
     float y = BE_read_float32(ifile);
     float z = BE_read_float32(ifile);

     // set parent
     uint32 parent = BE_read_uint32(ifile);
     uint32 xxxxxx = BE_read_uint32(ifile);

     cout << "index = " << i << endl;   
     cout << "name = " << name << endl;   
     cout << "x = " << x << endl;
     cout << "y = " << y << endl;
     cout << "z = " << z << endl;
     cout << "parent = " << parent << endl;
     cout << "xxxxxx = " << xxxxxx << endl;
     cout << endl;
    }

 ifile.seekg(head05);
 uint16 sect01 = BE_read_uint16(ifile);
 uint16 sect02 = BE_read_uint16(ifile);
 uint32 sect03 = BE_read_uint32(ifile);
 uint32 sect04 = BE_read_uint32(ifile);
 uint32 sect05 = BE_read_uint32(ifile);
 cout << "sect01 = " << sect01 << endl;
 cout << "sect02 = " << sect02 << endl;
 cout << "sect03 = " << sect03 << endl;
 cout << "sect04 = " << sect04 << endl;
 cout << "sect05 = " << sect05 << endl;

 for(size_t i = 0; i < sect02; i++)
    {
     cout << "i = " << i << endl;
     for(size_t j = 0; j < 27; j++) {
         uint32 temp = BE_read_uint32(ifile);
         cout << temp << endl;
        }
     cout << endl;
    }

 ifile.seekg(head14);
 uint32 param1 = BE_read_uint32(ifile);
 uint32 param2 = BE_read_uint32(ifile);
 cout << "param1 = " << param1 << endl;
 cout << "param2 = " << param2 << endl;

 for(size_t i = 0; i < sect01; i++)
    {
     cout << "i = " << i << endl;
     auto p1 = BE_read_uint16(ifile);
     auto p2 = BE_read_uint16(ifile);
     auto p3 = BE_read_uint16(ifile);
     auto p4 = BE_read_uint16(ifile);
     cout << "p1 = " << p1 << endl;
     cout << "p2 = " << p2 << endl;
     cout << "p3 = " << p3 << endl;
     cout << "p4 = " << p4 << endl;
     cout << endl;
    }

 float fp01 = BE_read_float32(ifile);
 float fp02 = BE_read_float32(ifile);
 float fp03 = BE_read_float32(ifile);
 float fp04 = BE_read_float32(ifile);
 float fp05 = BE_read_float32(ifile);
 float fp06 = BE_read_float32(ifile);
 float fp07 = BE_read_float32(ifile);
 float fp08 = BE_read_float32(ifile);
 float fp09 = BE_read_float32(ifile);
 float fp10 = BE_read_float32(ifile);
 float fp11 = BE_read_float32(ifile);
 float fp12 = BE_read_float32(ifile);
 cout << "fp01 = " << fp01 << endl;
 cout << "fp02 = " << fp02 << endl;
 cout << "fp03 = " << fp03 << endl;
 cout << "fp04 = " << fp04 << endl;
 cout << "fp05 = " << fp05 << endl;
 cout << "fp06 = " << fp06 << endl;
 cout << "fp07 = " << fp07 << endl;
 cout << "fp08 = " << fp08 << endl;
 cout << "fp09 = " << fp09 << endl;
 cout << "fp10 = " << fp10 << endl;
 cout << "fp11 = " << fp11 << endl;
 cout << "fp12 = " << fp12 << endl;
/*
 for(;;)
    {
     for(size_t i = 0; i < sect01; i++)
        {
         cout << "i = " << i << endl;
         auto p1 = BE_read_uint16(ifile);
         auto p2 = BE_read_uint16(ifile);
         auto p3 = BE_read_uint16(ifile);
         auto p4 = BE_read_uint16(ifile);
         cout << "p1 = " << p1 << endl;
         cout << "p2 = " << p2 << endl;
         cout << "p3 = " << p3 << endl;
         cout << "p4 = " << p4 << endl;
         cout << endl;
        }
    }
*/
 return true;
}

std::string GetTEXFromMDL(const std::string& filename)
{
 using namespace std;
 if(!filename.length()) return string();

 string path = GetPathnameFromFilename(filename);
 string name = GetShortFilenameWithoutExtension(filename);
 string name_with_ext = GetShortFilename(filename);

 // METHOD #1
 // SAME PATH - SAME NAME
 string test1 = path;
 test1 += name;
 test1 += ".tex";
 if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;

 // METHOD #2
 // SAME PATH - DIFFERENT NAME
 if(name == "000" || name == "001" || name == "002" || name == "003" || name == "004") {
    string test0 = path; test0 += "000.tex";
    string test1 = path; test1 += "001.tex";
    string test2 = path; test2 += "002.tex";
    string test3 = path; test3 += "003.tex";
    string test4 = path; test4 += "004.tex";
    if(GetFileAttributesA(test0.c_str()) != INVALID_FILE_ATTRIBUTES) return test0;
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
    if(GetFileAttributesA(test2.c_str()) != INVALID_FILE_ATTRIBUTES) return test2;
    if(GetFileAttributesA(test3.c_str()) != INVALID_FILE_ATTRIBUTES) return test3;
    if(GetFileAttributesA(test4.c_str()) != INVALID_FILE_ATTRIBUTES) return test4;
   } 

 // METHOD #3
 // REPLACE _mdl.mdl WITH _tex.tex
 size_t position = name_with_ext.find("_mdl.mdl");
 if(position != string::npos) {
    string test1 = path;
    string test2 = name_with_ext.replace(position, 8, "_tex.tex");
    test1 += test2;
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #4
 // REPLACE _mdl.mdl WITH _tex_color1.tex
 if(position != string::npos) {
    string test1 = path;
    string test2 = name_with_ext.substr(0, position);
    test1 += test2;
    test1 += "_tex_color1.tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #5
 // REPLACE _mdl.mdl WITH _tex_democolor.tex
 if(position != string::npos) {
    string test1 = path;
    string test2 = name_with_ext.substr(0, position);
    test1 += test2;
    test1 += "_tex_democolor.tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #6a
 // REPLACE EVERYTHING AFTER _tmd WITH _color1_xpr\\000\\xxx.tex
 position = path.find("_tmd\\000\\");
 if(position != string::npos) {
    string test1 = path.substr(0, position);
    test1 += "_color1_xpr\\000\\";
    if(name == "000") test1 += "000.tex";
    else if(name == "001") test1 += "001.tex";
    else if(name == "002") test1 += "002.tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #6b
 // REPLACE EVERYTHING AFTER _tmd WITH _color1_xpr\\001\\xxx.tex
 position = path.find("_tmd\\001\\");
 if(position != string::npos) {
    string test1 = path.substr(0, position);
    test1 += "_color1_xpr\\001\\";
    if(name == "000") test1 += "000.tex";
    else if(name == "001") test1 += "001.tex";
    else if(name == "002") test1 += "002.tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #6c
 // REPLACE EVERYTHING AFTER _tmd WITH _color1_xpr\\000\\xxx.tex
 position = path.find("_tmd\\");
 if(position != string::npos) {
    string test1 = path.substr(0, position);
    test1 += "_color1_xpr\\000\\";
    if(name == "000") test1 += "000.tex";
    else if(name == "001") test1 += "001.tex";
    else if(name == "002") test1 += "002.tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #6d
 // REPLACE EVERYTHING AFTER _tmd WITH _color1_xpr\\000\\000.tex
 position = path.find("_tmd\\");
 if(position != string::npos) {
    string test1 = path.substr(0, position);
    test1 += "_color1_xpr\\000\\000.tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #7
 // REPLACE SFX\\SFX\\XXX.MDL WITH SFX\\XXX.TEX
 position = path.find("sfx\\sfx\\");
 if(position != string::npos) {
    string test1 = path.substr(0, position);
    test1 += "sfx\\";
    test1 += name;
    test1 += ".tex";
    if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
   }

 // METHOD #8
 // REPLACE field\\fieldbg WITH field\\field_xbg
 position = path.find("field\\fieldbg");
 if(position != string::npos)
   {
    // build path part 1
    string test1 = path.substr(0, position);
    test1 += "field\\field_xbg";

    // build path part 2
    string test2 = path.substr(position + 13);
    size_t strip_position = test2.find("bf_fieldbg");
    if(strip_position != string::npos) {
       string test3 = test2.substr(0, strip_position);
       test1 += test3;
       if(name == "000") test1 += "000.tex";
       else if(name == "001") test1 += "001.tex";
       else if(name == "002") test1 += "002.tex";
       else if(name == "003") test1 += "003.tex";
       if(GetFileAttributesA(test1.c_str()) != INVALID_FILE_ATTRIBUTES) return test1;
      }
   }

 // METHOD #8
 // REPLACE field\\fieldbg WITH field\\field_xbg
 position = path.find("field\\fieldbg");
 if(position != string::npos)
   {
    // build path part 1
    string test1 = path.substr(0, position);
    test1 += "field\\field_xbg";

    // build path part 2
    string test2 = path.substr(position + 13);
    test1 += test2;

    // strip ending
    size_t strip_position = test1.find("_tmd\\");
    if(strip_position != string::npos) {
       string test3 = test1.substr(0, strip_position);
       test3 += "_xpr\\000.tex";
       if(GetFileAttributesA(test3.c_str()) != INVALID_FILE_ATTRIBUTES) return test3;
      }
   }

 return string();
}

bool GetFilenamesFromTEX(const std::string& texfile, std::deque<std::string>& filenames)
{
 // open texture file
 using namespace std;
 ifstream ifile(texfile, ios::binary);
 if(!ifile) return error("Failed to open TEX file; model will have to be manually textured.");

 // read number of textures
 ifile.seekg(0xC);
 if(ifile.fail()) return error("Seek failure; model will have to be manually textured.");

 // read number of textures
 uint32 n_textures = BE_read_uint32(ifile);
 if(ifile.fail()) return error("Read failure; model will have to be manually textured.");

 if(n_textures < 1 || n_textures > 1000)
    return error("Unexpected number of textures; model will have to be manually textured.");

 // move to filename offset
 ifile.seekg(16*n_textures + 20);
 if(ifile.fail()) return error("Seek failure; model will have to be manually textured.");

 // read filenames
 for(size_t i = 0; i < n_textures; i++) {
     char buffer[1024];
     read_string(ifile, buffer, 1024);
     if(ifile.fail()) return error("Read filename failure; model will have to be manually textured.");
     filenames.push_back(string(buffer));
    }

 if(filenames.size() == 0) return error("Filename search yielded no textures;  model will have to be manually textured.");
 return true;
}

};};

namespace XB360 { namespace Onechanbara2 {

bool extract(void)
{
 char pathname[MAX_PATH];
 GetModulePathname(pathname, MAX_PATH);
 return extract(pathname);
}

bool extract(const char* pathname)
{
 return extractor(pathname).extract();
}

};};