static GF_Err DumpProtos(GF_SceneDumper *sdump, GF_List *protoList)
{
#ifdef GPAC_DISABLE_VRML
	return GF_OK;
#else
	u32 i, j, count;
	GF_FieldInfo field;
	GF_Err e;
	GF_SceneGraph *prev_sg;
	GF_Proto *proto, *prev_proto;

	prev_proto = sdump->current_proto;

	i=0;
	while ((proto = (GF_Proto*)gf_list_enum(protoList, &i))) {
		sdump->current_proto = proto;

		DUMP_IND(sdump);
		if (!sdump->XMLDump) {
			gf_fprintf(sdump->trace, proto->ExternProto.count ? "EXTERNPROTO " : "PROTO ");
			gf_fprintf(sdump->trace, "%s [\n", proto->Name);
		} else {
			gf_fprintf(sdump->trace, "<ProtoDeclare name=\"%s\" protoID=\"%d\"", proto->Name, proto->ID);
			if (proto->ExternProto.count) {
				gf_fprintf(sdump->trace, " locations=\"");
				gf_dump_vrml_sffield(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0, NULL);
				gf_fprintf(sdump->trace, "\"");
			}
			gf_fprintf(sdump->trace, ">\n");
		}

		if (sdump->XMLDump && sdump->X3DDump) gf_fprintf(sdump->trace, "<ProtoInterface>");

		sdump->indent++;
		count = gf_list_count(proto->proto_fields);
		for (j=0; j<count; j++) {
			GF_ProtoFieldInterface *pf = (GF_ProtoFieldInterface *)gf_list_get(proto->proto_fields, j);
			field.fieldIndex = pf->ALL_index;
			field.eventType = pf->EventType;
			field.far_ptr = pf->def_value;
			field.fieldType = pf->FieldType;
			field.name = pf->FieldName;
			field.NDTtype = NDT_SFWorldNode;
			field.on_event_in = NULL;

			gf_dump_vrml_dyn_field(sdump, NULL, field, pf->QP_Type ? 1 : 0);

			if (!pf->QP_Type) continue;

			/*dump interface coding - BT/TXT extensions, not supported by any other tool*/
			sdump->indent++;
			DUMP_IND(sdump);
			if (sdump->XMLDump) {
				const char *quant_catname = "unknown";
#ifndef GPAC_DISABLE_BIFS
				switch (pf->QP_Type) {
				case QC_3DPOS: quant_catname = "position3D"; break;
				case QC_2DPOS: quant_catname = "position2D"; break;
				case QC_ORDER: quant_catname = "drawingOrder"; break;
				case QC_COLOR: quant_catname = "color"; break;
				case QC_TEXTURE_COORD: quant_catname = "textureCoordinate"; break;
				case QC_ANGLE: quant_catname = "angle"; break;
				case QC_SCALE: quant_catname = "scale"; break;
				case QC_INTERPOL_KEYS: quant_catname = "keys"; break;
				case QC_NORMALS: quant_catname = "normals"; break;
				case QC_ROTATION: quant_catname = "rotations"; break;
				case QC_SIZE_3D: quant_catname = "size3D"; break;
				case QC_SIZE_2D: quant_catname = "size2D"; break;
				case QC_LINEAR_SCALAR: quant_catname = "linear"; break;
				case QC_COORD_INDEX:quant_catname = "coordIndex"; break;
				}
#endif
				gf_fprintf(sdump->trace, "<InterfaceCodingParameters quantCategoy=\"%s\"", quant_catname);
			} else {
				gf_fprintf(sdump->trace, "{QP %d", pf->QP_Type);
			}
#ifndef GPAC_DISABLE_BIFS
			if (pf->QP_Type==QC_LINEAR_SCALAR) gf_fprintf(sdump->trace, sdump->XMLDump ? " nbBits=\"%d\"" : " nbBits %d", pf->NumBits);
			if (pf->hasMinMax) {
				switch (pf->QP_Type) {
				case QC_LINEAR_SCALAR:
				case QC_COORD_INDEX:
					if (sdump->XMLDump) {
						gf_fprintf(sdump->trace, " intMin=\"%d\" intMax=\"%d\"", *((SFInt32 *)pf->qp_min_value), *((SFInt32 *)pf->qp_max_value));
					} else {
						gf_fprintf(sdump->trace, " b {%d %d}", *((SFInt32 *)pf->qp_min_value), *((SFInt32 *)pf->qp_max_value));
					}
					break;
				default:
					if (sdump->XMLDump) {
						gf_fprintf(sdump->trace, " floatMin=\"%g\" floatMax=\"%g\"", FIX2FLT( *((SFFloat *)pf->qp_min_value) ), FIX2FLT( *((SFFloat *)pf->qp_max_value) ));
					} else {
						gf_fprintf(sdump->trace, " b {%g %g}", FIX2FLT( *((SFFloat *)pf->qp_min_value) ), FIX2FLT( *((SFFloat *)pf->qp_max_value) ) );
					}
					break;
				}
			}
#endif
			gf_fprintf(sdump->trace, sdump->XMLDump ? "/>\n" : "}\n");
			sdump->indent--;
			if (sdump->XMLDump) {
				DUMP_IND(sdump);
				gf_fprintf(sdump->trace, "</field>\n");
			}

		}

		sdump->indent--;
		DUMP_IND(sdump);
		if (!sdump->XMLDump) {
			gf_fprintf(sdump->trace, "]");
		} else if (sdump->X3DDump) gf_fprintf(sdump->trace, "</ProtoInterface>\n");

		if (proto->ExternProto.count) {
			if (!sdump->XMLDump) {
				gf_fprintf(sdump->trace, " \"");
				gf_dump_vrml_sffield(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0, NULL);
				gf_fprintf(sdump->trace, "\"\n\n");
			} else {
				gf_fprintf(sdump->trace, "</ProtoDeclare>\n");
			}
			continue;
		}
		if (!sdump->XMLDump) gf_fprintf(sdump->trace, " {\n");

		sdump->indent++;

		if (sdump->XMLDump && sdump->X3DDump) gf_fprintf(sdump->trace, "<ProtoBody>\n");

		e = DumpProtos(sdump, proto->sub_graph->protos);
		if (e) return e;

		/*set namespace to the proto one*/
		prev_sg = sdump->sg;
		sdump->sg = gf_sg_proto_get_graph(proto);

		count = gf_list_count(proto->node_code);
		for (j=0; j<count; j++) {
			GF_Node *n = (GF_Node*)gf_list_get(proto->node_code, j);
			gf_dump_vrml_node(sdump, n, 1, NULL);
		}
		count = gf_list_count(proto->sub_graph->Routes);
		for (j=0; j<count; j++) {
			GF_Route *r = (GF_Route *)gf_list_get(proto->sub_graph->Routes, j);
			if (r->IS_route) continue;
			gf_dump_vrml_route(sdump, r, 0);
		}

		if (sdump->XMLDump && sdump->X3DDump) gf_fprintf(sdump->trace, "</ProtoBody>\n");

		/*restore namespace*/
		sdump->sg = prev_sg;

		sdump->indent--;
		DUMP_IND(sdump);
		if (!sdump->XMLDump) {
			gf_fprintf(sdump->trace, "}\n");
		} else {
			gf_fprintf(sdump->trace, "</ProtoDeclare>\n");
		}
	}
	sdump->current_proto = prev_proto;
	return GF_OK;
#endif
}