#include "bod_bob_parser.h"
#include "../common/strutils.h"
//---------------------------------------------------------------------------------
bool bod_bob_parser::compile(token_stream& is, bob_dom_obinaryfilestream& os, bool bEmbedded)
{
	bob_dom_bob *bob=new bob_dom_bob(m_settings);
	bob=loadBOB(is, bEmbedded);
	if(bob)
		bob->toFile(os);
	delete bob;
	return bob!=NULL;
}
//---------------------------------------------------------------------------------
#define ISMATERIAL(text) ((strcmp(text, "MATERIAL6")==0 || strcmp(text, "MATERIAL5")==0 || \
                          strcmp(text, "MATERIAL3")==0 || strcmp(text, "MATERIAL")==0))

bob_dom_bob * bod_bob_parser::loadBOB(token_stream& is, bool bEmbedded)
{
	token *t;
	bob_dom_bob *bob=new bob_dom_bob(m_settings);
	bool abort=false;
	
	while((t=is.tok()) && t->type==token::t_hdrinfo){
		if(bob->info.text()!=0)
			error(is.tok(), S_Warning, "Header info already defined, last value will be used");
		bob->info.text(t->text);
		++is;
	}
	
	// first load material definitions, break on first numeric token
	while((t=is.tok()) && t->type!=token::t_closeBracket){
		if(t->type!=token::t_text){
			error(t, S_Error, "Unexpected here: '%s'", t->getText());
			abort=true; break;
		}
		if(isinteger(t->text)) break;
		
		//if(strcmp(t->text, "MATERIAL5")==0 || strcmp(t->text, "MATERIAL3")==0 || strcmp(t->text, "MATERIAL")==0){
		if(ISMATERIAL(t->text)){
			if(!loadMaterial(bob, is)){
				abort=true; break;
			}
		}
		else{
			error(t, S_Error, "Unexpected here '%s'", t->getText());
			abort=true; break;
		}
	}
	
	// now load the body definition: body size, points, parts
	if(!abort){
		m_materials=&(bob->materials);
		do{
			if(abort=(loadBody(bob, is)==false)) break;
			
			if(bEmbedded){
				if(is.tok()==NULL){
					error(is.previous(), S_Error, "Unexpected end of document found while loading embedded BOB - do you miss the '}'?");
					abort=true; break;
				}
				else if(is.tok()->type==token::t_closeBracket)
					break;
			}
			else if(is.eof())
				break;
		}
		while(1); // while what? while '}' if embedded or while eof if standalone
	}
	
	if(abort){
		delete bob;
		bob=NULL;
	}
	
	++is; // advance past the close bracket (if embedded)
	
	// set the bob type (setting from Settings takes precedence)
	/*if(bob){
		switch(m_settings->X3BOB()){
			case Settings::on:
				bob->bobType=bobX3;
				break;
			case Settings::off:
				bob->bobType=bobX2;
				break;
			default:
				if(bob->materials.size() && bob->materials.front()->type==bob_dom_material::mat6)
					bob->bobType=bobX3;
				else
					bob->bobType=bobX2;
		}	
	}*/
	
	return bob;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMatRGB(token_stream& is, bob_dom_material5::rgb& rgb)
{
	int vals[3];
	token *t;
	for(int i=0; i < 3; i++){
		t=loadValue(is);
		if(t==NULL) return false;
		if(!isinteger(t->text)){
			error(t, S_Error, "Expected integer value at position %d in RBG", i);
			return false;
		}
		vals[i]=atoi(t->text);
	}
	rgb.r=vals[0];
	rgb.g=vals[1];
	rgb.b=vals[2];
	return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMatPair(token_stream& is, bob_dom_material5::pair& pair)
{
	int vals[2];
	
	for(int i=0; i < 2; i++){
		if(!loadIntValue(is, vals + i))
			return false;
		
	}
	pair.value=vals[0];
	pair.strength=vals[1];
	return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMatPair(token_stream& is, bob_dom_material6::Small::pair& pair)
{
	int i;
	if((pair.texture=loadString(is, "texture name"))==0) return false;
	if(strcmp(pair.texture, "NULL")==0){
		delete pair.texture; pair.texture=0;
	}
	if(loadIntValue(is, &i, "texture strength")==false) return false;
	pair.strength=i;
	return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::checkImmediatePlacement(const bod_bob_parser::token *token1, const bod_bob_parser::token *token2)
{
	if(token1->line!=token2->line || (token1->col + strlen(token1->getText())!=token2->col)){
		error(token2, S_Error, "'%s' must be placed directly after '%s'", token2->getText(), token1->getText());
		return false;
	}
	else
		return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMat(bob_dom_material5 *mat, token_stream &is)
{
	do{
		int i; 
		bob_dom_material5::rgb rgb;
		bob_dom_material5::pair pair;
		
		if(!loadIntValue(is, &i)) break;
		mat->index=i;
		if(!loadIntValue(is, &i)) break;
		mat->textureID=i;
		if(!loadMatRGB(is, rgb)) break;
		mat->ambient=rgb;
		if(!loadMatRGB(is, rgb)) break;
		mat->diffuse=rgb;
		if(!loadMatRGB(is, rgb)) break;
		mat->specular=rgb;
		if(mat->type!=bob_dom_material::mat1) {
			if(!loadIntValue(is, &i)) break;
			mat->transparency=i;
			if(!loadIntValue(is, &i)) break;
			mat->selfIllumination=i;
			if(!loadMatPair(is, pair)) break;
			mat->shininess=pair;
			if(!loadIntValue(is, &i)) break;
			mat->destinationBlend=i!=0;
			if(!loadIntValue(is, &i)) break;
			mat->twoSided=i!=0;
			if(!loadIntValue(is, &i)) break;
			mat->wireframe=i!=0;
			if(!loadIntValue(is, &i)) break;
			mat->textureValue=i;
			if(!loadMatPair(is, pair)) break;
			mat->enviromentMap=pair;
			if(!loadMatPair(is, pair)) break;
			mat->bumpMap=pair;
			
			if(mat->type==bob_dom_material::mat5){
				if(!loadMatPair(is, pair)) break;
				mat->lightMap=pair;
			}
		}
		return true;
	}
	while(false);
	return false;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMat(bob_dom_material6 *mat, token_stream &is)
{
	int i; bool bRes=false;
	do{
		if(!loadIntValue(is, &i)) break;
			mat->index=i;
		
		if(!loadIntValue(is, &i)) break;
			mat->flags=i;
			
		if(mat->flags==bob_dom_material6::Big::flag)
			bRes=loadMaterial6Big(mat, is);
		else
			bRes=loadMaterial6Small(mat, is);
	}
	while(false);
	
	return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMaterial6Small(bob_dom_material6 *mat, token_stream &is)
{
	int i;
	bob_dom_material1::rgb rgb;
	bob_dom_material1::pair int_pair;
	bob_dom_material6::Small::pair pair;
	
	mat->small=new bob_dom_material6::Small();
	do{
		if((mat->small->textureFile=loadString(is, "texture name: "))==0) break;
		if(strcmp(mat->small->textureFile, "NULL")==0) 
			{ delete[] mat->small->textureFile; mat->small->textureFile=0; }
		if(!loadMatRGB(is, rgb)) break;
		mat->small->ambient=rgb;
		if(!loadMatRGB(is, rgb)) break;
		mat->small->diffuse=rgb;
		if(!loadMatRGB(is, rgb)) break;
		mat->small->specular=rgb;
		if(!loadIntValue(is, &i)) break;
		mat->small->transparency=i;
		if(!loadIntValue(is, &i)) break;
		mat->small->selfIllumination=i;
		if(!loadMatPair(is, int_pair)) break;
		mat->small->shininess=int_pair;
		if(!loadIntValue(is, &i)) break;
		mat->small->destinationBlend=i!=0;
		if(!loadIntValue(is, &i)) break;
		mat->small->twoSided=i!=0;
		if(!loadIntValue(is, &i)) break;
		mat->small->wireframe=i!=0;
		if(!loadIntValue(is, &i)) break;
		mat->small->textureValue=i;
		if(!loadMatPair(is, pair)) break;
		mat->small->enviromentMap=pair;
		if(!loadMatPair(is, pair)) break;
		mat->small->bumpMap=pair;

		if(!loadMatPair(is, pair)) break;
		mat->small->lightMap=pair;

		if(!loadMatPair(is, pair)) break;
		mat->small->map4=pair;
		if(!loadMatPair(is, pair)) break;
		mat->small->map5=pair;
		
		short flags=0;
		if(mat->small->destinationBlend) flags&=0x2;
		if(mat->small->twoSided) flags&=0x10;
		if(mat->small->wireframe) flags&=0x8;
		mat->flags=flags;
	
		return true;
	}
	while(false);
	return false;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMaterial6Big(bob_dom_material6 *mat, token_stream &is)
{
	int count,i;
	
	bob_dom_material6::Big *big=new bob_dom_material6::Big();
	mat->big=big;
	
	if(!loadIntValue(is, &i, "Technique idx: ")) return false;
	big->technique=i;
	
	if((big->effect=loadString(is, "Effect name: "))==0) return false;
	
	material6_value *val;
	if(!loadIntValue(is, &count, "Value count: ")) return false;
	for(i=0; i < count; i++){
		if((val=loadMat6Value(is))==0) return false;
		big->values.push_back(val);
	}
	
	return true;
}
//---------------------------------------------------------------------------------
material6_value * bod_bob_parser::loadMat6Value(token_stream& is)
{
	bool bRes=false;
	// name, type, value
	char *name, *strtype;
	
	material6_value *val=new material6_value();
	
	token_stream::token *typepos;
	
	do{
		if((name=loadString(is, "Property name: "))==0) break;
		if(name[0]==0) {
			error(is.tok(), S_Error, "Empty property name");
			return false;
		}
		if((strtype=loadString(is, "Property type: "))==0) break;
		typepos=is.tok();
		
		int type=-1;
		
		for(int i=0; i < material6_value::typeNameCount(); i++){
			if(strcmp(strtype, material6_value::typeName(i))==0) {
				if(strtype[0]!=0)
					type=i;
				break;
			}
		}
		
		if(type==-1){
			error(typepos, S_Error, "Unknown property type: \"%s\"", strtype);
			break;
		}
		val->type=(material6_value::Type)type;
		val->name=name; 
		name=0;
		
		double d;
		switch(val->type){
			case material6_value::typeBool: // nobreak
			case material6_value::typeLong:
				bRes=loadIntValue(is, &val->val.i, "Property value: ");
				break;
			case material6_value::typeString:
				bRes=((val->val.psz=loadString(is, "Property value: "))!=0);
				break;
			case material6_value::typeFloat:
				bRes=loadDoubleValue(is, &d, "Property value: ");
				val->val.f=(float)d;
				break;
			case material6_value::typeFloat4:
				for(i=0; i < 4; i++){
					if((bRes=loadDoubleValue(is, &d, "Property value"))==false) break;
					val->val.f4.f[i]=(float)d;
				}
				break;
			default:
				error(0, S_Error, "Don't know how to output type \"%s\"", strtype);
				bRes=false;
		}
		if(bRes==false)
			break;
			
		bRes=true;
	}
	while(0);
	
	delete[] name; delete[] strtype;
	
	if(bRes==false) {
		delete val;
		val=0;
	}
	
	return val;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadMaterial(bob_dom_bob *bob, token_stream& is)
{
	bob_dom_material5::materialType type;
	
	token *t=is.tok();
	
	if(t){
		if(strcmp(t->getText(), "MATERIAL6")==0)
			type=bob_dom_material::mat6;
		else if(strcmp(t->getText(), "MATERIAL5")==0)
			type=bob_dom_material::mat5;
		else if(strcmp(t->getText(), "MATERIAL3")==0)
			type=bob_dom_material::mat3;
		else if(strcmp(t->getText(), "MATERIAL")==0)
			type=bob_dom_material5::mat1;
	}
	
	if(t==NULL || t->type!=token::t_text){
		error(t ? t : is.previous(), S_Error, "Expected material header (MATERIAL, MATERIAL3, MATERIAL5 or MATERIAL6)");
		return NULL;
	}
	t=(++is).tok();
	if(t==NULL || t->type!=token::t_colon){
		error(t ? t : is.previous(), S_Error, "Expected ':' after '%s'", is.previous()->getText());
		return NULL;
	}
	if(!checkImmediatePlacement(is.previous(), is.tok()))
		return NULL;
	
	++is;
	
	bool bRes;
	bob_dom_material *mat;
	switch(type) {
		case bob_dom_material::mat6:
			mat=new bob_dom_material6();
			bRes=loadMat((bob_dom_material6*)mat, is);
			break;
		default: // always mat5 because lower mats are not supported in binary form
			mat=new bob_dom_material5();
			mat->type=type;
			bRes=loadMat((bob_dom_material5*)mat, is);
			break;
	}
	
	if(bRes==false)
		delete mat;
	else
		bob->materials.push_back(mat);
		
	return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadBody(bob_dom_bob *bob, token_stream& is)
{
	static const char *context="Body: ";
	bob_dom_body *body=bob->bodies.createChild();
	PointList list;
	
	deleteTempData();
	
	loadIntValue(is, &(body->bodySize), context);
	
	if(!loadBones(body, is)){
		bob->bodies.removeChild(--bob->bodies.end());
		return false;
	}
	if(!loadPoints(body, is, list)) {
		bob->bodies.removeChild(--bob->bodies.end());
		return false;
	}
	
	if(!loadWeights(body, is)){
		bob->bodies.removeChild(--bob->bodies.end());
		return false;
	}
	
	if(m_weights.size() && m_weights.size()!=list.size()){
		error(0, S_Error, "Number of Weights (%d) does not match number of Points (%d)", m_weights.size(), list.size());
		bob->bodies.removeChild(--bob->bodies.end());
		return false;
	}
	
	// move the points to array so we can access them with index
	m_points.resize(list.size());
	int i=0;
	for(PointList::iterator &it=list.begin(); it!=list.end(); ++it, ++i){
		m_points[i]=*it;
	}
	list.clear();
	
	int endval;
	do{
		if(!loadPart(body, is)) {
			bob->bodies.removeChild(--bob->bodies.end());
			return false;
		}
		
		loadIntValue(is, &endval);
		if(endval!=-99)
			ungetValue(is);
	}
	while(is.good() && endval!=-99); // -99 means end of body
	
	if(endval!=-99){
		error(is.previous(), S_Error, "Unexpected end of document found while loading Body. Do you miss the '-99'?");
		bob->bodies.removeChild(--bob->bodies.end());
		return false;
	}
	if(!flagsFromString(is, body->bodyFlags)){
		bob->bodies.removeChild(--bob->bodies.end());
		return false;
	}
	
	return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadPoints(bob_dom_body *body, token_stream& is, PointList& points)
{
	bool bEnd;
	int x, y, z;
	static const char *context="Point: ";
	
	do{
		loadIntValue(is, &x, context);
		loadIntValue(is, &y, context);
		if(!loadIntValue(is, &z, context)) return false;
		
		if(x==-1 && y==-1 && z==-1) { 
			bEnd=true; 
			break; 
		}
		
		__asm {
			fild x
			fdiv bob_dom_point::multiplier;
			fistp x
			
			fild y
			fdiv bob_dom_point::multiplier;
			fistp y
			
			fild z
			fdiv bob_dom_point::multiplier;
			fistp z
		}
		points.push_back(new point_t(x, y, z));
	}
	while(is.good());
	
	if(!bEnd){
		error(is.previous(), S_Error, "Unexpected end of document found while loading Points. Do you miss the '-1;-1;-1;'?");
		return false;
	}
	
	return true;
}
//---------------------------------------------------------------------------------
// load part - bunch of faces/uv's
bool bod_bob_parser::loadPart(bob_dom_body *body, token_stream& is)
{
	int matIdx;
	bool bRes=true;
	static const char *context="Parts: ";
	bool secUV=false;
	
	bob_dom_part *part=new bob_dom_part();
	
	loadIntValue(is, &matIdx, context);
	do{
		if(matIdx < 0)
			{ error(is.previous(), S_Error, "%sMaterial index must be >= 0", context); bRes=false; break; }
		if(matIdx!=0 && ((size_t)matIdx > m_materials->size() - 1))
			error(is.previous(), S_Warning, "%sMaterial index out of range: %d", context, matIdx);
			
		if((bRes=loadFace(matIdx, *body, *part, is))==false)
			break;
		
		if(is.tok() && is.tok()->type==token::t_openInstrBlock) {
			secUV=true;
			
			if((bRes=loadX3PartValues(++is, *part))==false)
				break;
			
			if((bRes=loadSecondaryUVs(is, *part))==false) 
				break;
		}
		
		loadIntValue(is, &matIdx, context);
	}
	while(matIdx!=-99 && is.good()); // -99 means end of part
	
	if(bRes && matIdx!=-99){
		error(is.previous(), S_Error, "Unexpected end of document found while loading Part. Do you miss the '-99'?");
		bRes=false;
	}
	
	if(bRes)
		bRes=flagsFromString(is, part->flags);
		
	if(secUV)
		part->flags|=bob_dom_part::x3flag;
		
	if(bRes)
		body->parts.push_back(part);
	else
		delete part;
		
	return bRes;
};
//---------------------------------------------------------------------------------
bool bod_bob_parser::flagsFromString(token_stream& is, int& flags)
{
	bool bRes;
	token *t=loadValue(is);
	
	flags=0;
	if(bRes=(t!=NULL)){
		int len=(int)strlen(t->text);
		int val=1;
		for(int i=len-1; i >= 0; i--, val<<=1 ){
			switch(t->text[i]){
				case '0':
					break;
				case '1':
					flags|=val;
					break;
				default:
					error(is.previous(), S_Error, "Invalid flags definition. Expected binary number");
					bRes=false;
					break;
			}
			if(bRes==false) break;
		}
	}
	return bRes;
}
//---------------------------------------------------------------------------------
// load face - actual face and its uv's
bool bod_bob_parser::loadFace(int matIdx, bob_dom_body& body, bob_dom_part& part, token_stream& is)
{
	int indexes[3];
	int magic;
	int lastPntVal;
	double dtexX, dtexY;
	int texX, texY;
	static const char *context="Part: ";
	
	for(int i=0; i < 3; i++){
		if(loadIntValue(is, indexes + i, context)){
			if(indexes[i] < 0 || (size_t)indexes[i] >= m_points.size()){
				error(is.previous(), S_Error, "Point index out of range: point %d, index %d", i, indexes[i]);
				return false;
			}
		}
		else
			return false;
	}
	
	bool bRes=true;
	
	// magic: -1 means no smoothing and no uv, -9 means no smoothing and uv
	//        -17 means smoothing and no uv, -25 means smoothing and uv
	bRes&=loadIntValue(is, &magic, context);
	if(magic!=-9 && magic!=-1)
		bRes&=loadIntValue(is, &lastPntVal, context);
	else
		lastPntVal=0;
	
	if(bRes==false) return false;
	
	bob_dom_point *point;
	bob_dom_face *face=new bob_dom_face();
	
	bob_dom_point *points_ar[3];
	int  magic_ar[3];
	
	for(int i=0; i < 3; i++){
		if(magic!=-17 && magic!=-1){
			loadDoubleValue(is, &dtexX, context);
			bRes&=loadDoubleValue(is, &dtexY, context);
			
			// this is like texX=(int)(dtexX / bob_dom_part::multiplier) with rounding 'to nearest'
			__asm {
				fld dtexX
				fdiv bob_dom_part::multiplier
				fistp texX
				
				fld dtexY
				fdiv bob_dom_part::multiplier
				fistp texY
			}
		}
		else{
			texX=0;
			texY=0;
		}
		
		texture_coords_t *coords;
		texture_coord_t *coord=0;
		
		coords=m_points[indexes[i]]->findTextureCoords(lastPntVal);
		
		// I can't remember why -9 is duplicated - it is because it does not have smoothing?
		// should -1 be also here?
		if(magic!=-9 /*&& magic!=-1 ?? */){
			coord=coords->findCoords(texX, texY);
		}
		if(coord==NULL){
			coord=new texture_coord_t();
			coord->x=texX;
			coord->y=texY;
			coords->push_back(coord);
			
			point=new bob_dom_point();
			point->x=m_points[indexes[i]]->x;
			point->y=m_points[indexes[i]]->y;
			point->z=m_points[indexes[i]]->z;
			point->textureCoords.left=texX;
			point->textureCoords.right=texY;
			
			point->values.push_back(lastPntVal);
			
			coord->index=(int)body.points.new_points.size();
			body.points.new_points.push_back(point);
			
			points_ar[i]=point; // this will be used to fetch values found in intruction block
			
			// now insert the appropriate weight
			if(m_weights.size())
				body.weights.new_weights.push_back(new bob_dom_weight(*m_weights[indexes[i]]));
		}
		else
			points_ar[i]=NULL;
		
		magic_ar[i]=magic;
		face->values[i]=coord->index;
	}
	face->values[3]=1;
	part.facelist(matIdx)->push_back(face);
	
	
	return loadSpecialValues(points_ar, magic_ar, is);
}
//---------------------------------------------------------------------------------
// load special values which may be "hidden" in instruction block after standard part definition
// example: /! { 1;2;3; } { 4;5;6; } { 7;8;9; } !/
bool bod_bob_parser::loadSpecialValues(bob_dom_point *points[3], int magic[3], token_stream& is)
{
	// we must now fill the special coordinates to the points
	// they will be either specified in instruction block or we will use default values
	
	bob_dom_point dummy_pnt;
	dummy_pnt.values.push_back(1);
	
	token *t=is.tok();
	
	if(t && t->type==token::t_openInstrBlock){
		++is;
		for(int i=0; i < 3; i++){
			// if thte point was already loaded it is null, but we must still consume the values
			// and check errors so I load them to dummy point
			if(points[i]!=NULL){
				if(!loadSpecialPointValues(is, points[i]))
					return false;
			}
			else{
				if(!loadSpecialPointValues(is, &dummy_pnt))
					return false;
				dummy_pnt.values.clear();
				// the "last value" must be there otherwise loadSpecialPointValues would fail
				dummy_pnt.values.push_back(1); 
			}
		}
		t=is.tok();
		if(t==NULL || t->type!=token::t_closeInstrBlock){
			error(t ? t : is.previous(), S_Error, "Expected end of instruction block '!/' after '%s'", is.previous());
			return false;
		}
		++is;
	}
	// use default values
	else{
		int count;
		for(int i=0; i < 3; i++){
			if(points[i]==NULL) continue;
			bob_dom_point::iterator last=--points[i]->values.end();
			
			// megic ==-17 means we are not using mateial so we can use the smallest point type 0x19
			if(magic[i]==-17){
				points[i]->type=0x19;
				count=1;
			}
			else{
				points[i]->type=0x1B;
				count=3;
			}
			for(int j=0; j < count; j++){
				points[i]->values.insert(last, 0);
			}
		}
	}
	return true;
}
//---------------------------------------------------------------------------------
// load one block from extra info in instruction block 
// example: { 1; 2; 3; }
bool bod_bob_parser::loadSpecialPointValues(token_stream& is, bob_dom_point *point)
{
	double d;
	int v;
	static const char *context="Part - Extra point info: ";
	
	// point already has the "last point value" pushed into list, so we must
	// push new values before it
	bob_dom_point::iterator &last=--point->values.end();
	
	token *t=is.tok();
	if(t==NULL || t->type!=token::t_openBracket){
		error(t ? t : is.previous(), S_Error, "Expected beginning of block after '%s'", is.previous()->getText());
		return false;
	}
	++is;
	while((t=is.tok())!=NULL && !(t->type==token::t_closeBracket || t->type==token::t_closeInstrBlock)){
		if(!loadDoubleValue(is, &d, context))
			return false;
		
		__asm{
			fld d
			fdiv bob_dom_part::multiplier
			fistp v
		}
		
		point->values.insert(last, v);
	}
	if(t==NULL || t->type!=token::t_closeBracket){
		if(t==NULL)
			error(is.previous(), S_Error, "Unexpected end of document found while loading extra point info. Do you miss the '}'?");
		else
			error(t, S_Error, "Unexpected '%s' found while loading extra point info", t->getText());
		return false;
	}
	
	switch(point->values.size()){
		case 2:
			point->type=0x19;
			break;
		case 4:
			point->type=0x1B;
			break;
		case 6:
			point->type=0x1F;
			break;
		default:
			error(t, S_Error, "Invalid number of values in extra point info block (%d) must be 1, 3 or 5", point->values.size() - 1);
			return false;
	}
	++is;
	
	return true;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadBones(bob_dom_body *body, token_stream& is)
{
	const token *t=is.tok();
	static const char *context2="Bones: ";
	static const char *context="Bone: ";
	
	if(!(t && t->type==token::t_text && strcmp(t->text, "BONES")==0))
		return true;
		
	t=(++is).tok();
	if(t==NULL || t->type!=token::t_colon){
		error(t ? t : is.previous(), S_Error, "Expected ':' after '%s'", is.previous()->getText());
		return false;
	}
	if(!checkImmediatePlacement(is.previous(), t))
		return false;
	
	int count;
	if(!loadIntValue(++is, &count, context2))
		return false;
	
	char *name;
	for(int i=0; i < count; i++){
		name=loadString(is, context);
		if(name==NULL) return false;
		body->bones.push_back(name);
	}
	
	return !is.fail();
}
//---------------------------------------------------------------------------------
// load the weights to temporary array. Weights from this array will then be inflated and added to body
bool bod_bob_parser::loadWeights(bob_dom_body *body, token_stream& is)
{
	const token *t=is.tok();
	static const char *context="Weight: ";
	static const char *context2="Weights: ";
	
	if(!(t && t->type==token::t_text && strcmp(t->text, "WEIGHTS")==0))
		return true;
		
	int count;
	if(!loadIntValue(++is, &count, context2))
		return false;
	
	bool bRes=true;
	int v; double d;
	bob_dom_weight *w;
	
	m_weights.resize(count, 0);
	
	for(int i=0; i < count; i++){
		w=new bob_dom_weight();
		do{
			if(!loadIntValue(is, &v, context)) 
				{ bRes=false; break; }
			if(v==-1) 
				break;
				
			if(v < 0 || (size_t)v >= body->bones.size()){
				error(is.previous(), S_Error, "Weight: Bone index out of range (%d)", v);
				{ bRes=false; break; }
			}
			
			if(!loadDoubleValue(is, &d, context)) 
				{ bRes=false; break; }
			
			bob_dom_weight::value val;
			val.boneIdx=v;
			
			__asm{
				// val.boneCoefficient = bob_dom_weight::value::multiplier * d
				fild bob_dom_weight::value::multiplier
				fmul d
				fistp val.boneCoefficient
			}
			w->values.push_back(val);
		}
		while(!is.fail());
		if(is.fail()){
			error(is.previous(), S_Error, "Unexpected end of document found while loading Weights. Do you miss the '-1'?");
			bRes=false;
		}
		if(bRes==false){
			delete w;
			break;
		}
		m_weights[i]=w;
	}
	if(bRes){
		if(!loadIntValue(is, &v, context2)) 
			bRes=false;
		else if(v!=-1){
			error(is.previous(), S_Error, "Expected '-1' after end of Weights");
			bRes=false;
		}
	}
	return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadSecondaryUVs(token_stream& is, bob_dom_part& part)
{
	bool bRes;
	do{
		if((bRes=loadSecondaryUVRecord(is, part))==false) break;
	}
	while(is.tok() && is.tok()->type!=token::t_closeInstrBlock);
	
	if(bRes){
		bRes=(is.tok() && is.tok()->type==token::t_closeInstrBlock);
		if(bRes==false)
			error(is.tok() ? is.tok() : is.previous(), S_Error, "Unexpected end of document found while loading secondary UV's. Do you miss the '!/'?");
		else
			++is;
	}
	return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadSecondaryUVRecord(token_stream& is, bob_dom_part& part)
{
	int matIdx, pointIdx;
	bool bRes=false;
	double d;
	bob_face_list *faces;
	bob_x3uv *uv=new bob_x3uv();
	
	do{
		if(!loadIntValue(is, &matIdx, "")) break;
		if(!loadIntValue(is, &pointIdx, "")) break;
		for(int i=0; i < 6; i++){
			if((bRes=loadDoubleValue(is, &d, ""))==false) break;
			uv->values[i]=(float) d;
		}
		uv->idx=pointIdx;
		
		faces=part[matIdx];
		if(faces==NULL){
			error(is.tok(), S_Error, "Secondary UVs: Face list with material idx %d not found", matIdx);
			break;
		}
		faces->x3uvlist.push_back(uv);
		
		bRes=true;
	}
	while(0);
	if(bRes==false)
		delete uv;
		
	return bRes;
}
//---------------------------------------------------------------------------------
bool bod_bob_parser::loadX3PartValues(token_stream& is, bob_dom_part& part)
{
	static char *context="X3 part values: ";
	static char *hdr="PART_VALUES_RAW";
	
	if(is.tok()==NULL || is.tok()->type!=token::t_text || strcmp(is.tok()->text, "PART_VALUES_RAW")!=0) {
		error(is.tok() ? is.tok() : is.previous(), S_Error, "Expected '%s' here", hdr);
		return false;
	}
	if((++is).tok()==NULL || is.tok()->type!=token::t_colon){
		error(is.tok() ? is.tok() : is.previous(), S_Error, "Expected ':' after '%s'", hdr);
		return false;
	}
	
	++is;
	
	int v;
	bool bRes;
	
	for(int i=0; i < 10; i++){
		if((bRes=loadIntValue(is, &v, context))==false) 
			break;
		part.values[i]=v;
	}
	return bRes;
}
//---------------------------------------------------------------------------------