static int core_analysis_graph_construct_nodes(RzCore *core, RzAnalysisFunction *fcn, int opts, PJ *pj, Sdb *DB) {
	RzAnalysisBlock *bbi;
	RzListIter *iter;
	int is_keva = opts & RZ_CORE_ANALYSIS_KEYVALUE;
	int is_star = opts & RZ_CORE_ANALYSIS_STAR;
	int is_json = opts & RZ_CORE_ANALYSIS_JSON;
	int is_html = rz_cons_singleton()->is_html;
	int left = 300;
	int top = 0;

	int is_json_format_disasm = opts & RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM;
	char *pal_curr = palColorFor("graph.current");
	char *pal_traced = palColorFor("graph.traced");
	char *pal_box4 = palColorFor("graph.box4");
	const char *font = rz_config_get(core->config, "graph.font");
	bool color_current = rz_config_get_i(core->config, "graph.gv.current");
	char *str;
	int nodes = 0;
	rz_list_foreach (fcn->bbs, iter, bbi) {
		if (is_keva) {
			char key[128];
			sdb_array_push_num(DB, "bbs", bbi->addr, 0);
			snprintf(key, sizeof(key), "bb.0x%08" PFMT64x ".size", bbi->addr);
			sdb_num_set(DB, key, bbi->size, 0); // bb.<addr>.size=<num>
		} else if (is_json) {
			RzDebugTracepoint *t = rz_debug_trace_get(core->dbg, bbi->addr);
			ut8 *buf = malloc(bbi->size);
			pj_o(pj);
			pj_kn(pj, "offset", bbi->addr);
			pj_kn(pj, "size", bbi->size);
			if (bbi->jump != UT64_MAX) {
				pj_kn(pj, "jump", bbi->jump);
			}
			if (bbi->fail != -1) {
				pj_kn(pj, "fail", bbi->fail);
			}
			if (bbi->switch_op) {
				RzAnalysisSwitchOp *op = bbi->switch_op;
				pj_k(pj, "switchop");
				pj_o(pj);
				pj_kn(pj, "offset", op->addr);
				pj_kn(pj, "defval", op->def_val);
				pj_kn(pj, "maxval", op->max_val);
				pj_kn(pj, "minval", op->min_val);
				pj_k(pj, "cases");
				pj_a(pj);
				RzAnalysisCaseOp *case_op;
				RzListIter *case_iter;
				rz_list_foreach (op->cases, case_iter, case_op) {
					pj_o(pj);
					pj_kn(pj, "offset", case_op->addr);
					pj_kn(pj, "value", case_op->value);
					pj_kn(pj, "jump", case_op->jump);
					pj_end(pj);
				}
				pj_end(pj);
				pj_end(pj);
			}
			if (t) {
				pj_k(pj, "trace");
				pj_o(pj);
				pj_ki(pj, "count", t->count);
				pj_ki(pj, "times", t->times);
				pj_end(pj);
			}
			pj_kn(pj, "colorize", bbi->colorize);
			pj_k(pj, "ops");
			pj_a(pj);
			if (buf) {
				rz_io_read_at(core->io, bbi->addr, buf, bbi->size);
				if (is_json_format_disasm) {
					rz_core_print_disasm(core->print, core, bbi->addr, buf, bbi->size, bbi->size, 0, 1, true, pj, NULL);
				} else {
					rz_core_print_disasm_json(core, bbi->addr, buf, bbi->size, 0, pj);
				}
				free(buf);
			} else {
				eprintf("cannot allocate %" PFMT64u " byte(s)\n", bbi->size);
			}
			pj_end(pj);
			pj_end(pj);
			continue;
		}
		if ((str = core_analysis_graph_label(core, bbi, opts))) {
			if (opts & RZ_CORE_ANALYSIS_GRAPHDIFF) {
				const char *difftype = bbi->diff ? (
									   bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_MATCH ? "lightgray" : bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_UNMATCH ? "yellow"
																							   : "red")
								 : "orange";
				const char *diffname = bbi->diff ? (
									   bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_MATCH ? "match" : bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_UNMATCH ? "unmatch"
																						       : "new")
								 : "unk";
				if (is_keva) {
					sdb_set(DB, "diff", diffname, 0);
					sdb_set(DB, "label", str, 0);
				} else if (!is_json) {
					nodes++;
					RzConfigHold *hc = rz_config_hold_new(core->config);
					rz_config_hold_i(hc, "scr.color", "scr.utf8", "asm.offset", "asm.lines",
						"asm.cmt.right", "asm.lines.fcn", "asm.bytes", NULL);
					rz_config_set_i(core->config, "scr.utf8", 0);
					rz_config_set_i(core->config, "asm.offset", 0);
					rz_config_set_i(core->config, "asm.lines", 0);
					rz_config_set_i(core->config, "asm.cmt.right", 0);
					rz_config_set_i(core->config, "asm.lines.fcn", 0);
					rz_config_set_i(core->config, "asm.bytes", 0);
					if (!is_star) {
						rz_config_set_i(core->config, "scr.color", 0); // disable color for dot
					}

					if (bbi->diff && bbi->diff->type != RZ_ANALYSIS_DIFF_TYPE_MATCH && core->c2) {
						char dff_from[32], dff_to[32];

						RzCore *c = core->c2;
						RzConfig *oc = c->config;
						char *str = rz_core_cmd_strf(core, "pdb @ 0x%08" PFMT64x, bbi->addr);
						c->config = core->config;
						// XXX. the bbi->addr doesnt needs to be in the same address in core2
						char *str2 = rz_core_cmd_strf(c, "pdb @ 0x%08" PFMT64x, bbi->diff->addr);
						snprintf(dff_from, sizeof(dff_from), "0x%08" PFMT64x, bbi->addr);
						snprintf(dff_to, sizeof(dff_to), "0x%08" PFMT64x, bbi->diff->addr);

						RzDiff *dff = rz_diff_lines_new(str, str2, NULL);
						char *diffstr = rz_diff_unified_text(dff, dff_from, dff_to, false, false);
						rz_diff_free(dff);

						if (diffstr) {
							char *nl = strchr(diffstr, '\n');
							if (nl) {
								nl = strchr(nl + 1, '\n');
								if (nl) {
									nl = strchr(nl + 1, '\n');
									if (nl) {
										rz_str_cpy(diffstr, nl + 1);
									}
								}
							}
						}

						if (is_star) {
							char *title = get_title(bbi->addr);
							if (!title) {
								rz_config_hold_free(hc);
								return false;
							}
							char *body_b64 = rz_base64_encode_dyn((const ut8 *)diffstr, strlen(diffstr));
							if (!body_b64) {
								free(title);
								rz_config_hold_free(hc);
								return false;
							}
							body_b64 = rz_str_prepend(body_b64, "base64:");
							rz_cons_printf("agn %s %s %d\n", title, body_b64, bbi->diff->type);
							free(body_b64);
							free(title);
						} else {
							diffstr = rz_str_replace(diffstr, "\n", "\\l", 1);
							diffstr = rz_str_replace(diffstr, "\"", "'", 1);
							rz_cons_printf(" \"0x%08" PFMT64x "\" [fillcolor=\"%s\","
								       "color=\"black\", fontname=\"%s\","
								       " label=\"%s\", URL=\"%s/0x%08" PFMT64x "\"]\n",
								bbi->addr, difftype, diffstr, font, fcn->name,
								bbi->addr);
						}
						free(diffstr);
						c->config = oc;
					} else {
						if (is_star) {
							char *title = get_title(bbi->addr);
							if (!title) {
								rz_config_hold_free(hc);
								return false;
							}
							char *body_b64 = rz_base64_encode_dyn((const ut8 *)str, strlen(title));
							int color = (bbi && bbi->diff) ? bbi->diff->type : 0;
							if (!title || !body_b64) {
								free(body_b64);
								free(title);
								rz_config_hold_free(hc);
								return false;
							}
							body_b64 = rz_str_prepend(body_b64, "base64:");
							rz_cons_printf("agn %s %s %d\n", title, body_b64, color);
							free(body_b64);
							free(title);
						} else {
							rz_cons_printf(" \"0x%08" PFMT64x "\" [fillcolor=\"%s\","
								       "color=\"black\", fontname=\"%s\","
								       " label=\"%s\", URL=\"%s/0x%08" PFMT64x "\"]\n",
								bbi->addr, difftype, str, font, fcn->name, bbi->addr);
						}
					}
					rz_config_set_i(core->config, "scr.color", 1);
					rz_config_hold_free(hc);
				}
			} else {
				if (is_html) {
					nodes++;
					rz_cons_printf("<p class=\"block draggable\" style=\""
						       "top: %dpx; left: %dpx; width: 400px;\" id=\""
						       "_0x%08" PFMT64x "\">\n%s</p>\n",
						top, left, bbi->addr, str);
					left = left ? 0 : 600;
					if (!left) {
						top += 250;
					}
				} else if (!is_json && !is_keva) {
					bool current = rz_analysis_block_contains(bbi, core->offset);
					const char *label_color = bbi->traced
						? pal_traced
						: (current && color_current)
						? pal_curr
						: pal_box4;
					const char *fill_color = ((current && color_current) || label_color == pal_traced) ? pal_traced : "white";
					nodes++;
					if (is_star) {
						char *title = get_title(bbi->addr);
						char *body_b64 = rz_base64_encode_dyn((const ut8 *)str, strlen(str));
						int color = (bbi && bbi->diff) ? bbi->diff->type : 0;
						if (!title || !body_b64) {
							free(body_b64);
							free(title);
							return false;
						}
						body_b64 = rz_str_prepend(body_b64, "base64:");
						rz_cons_printf("agn %s %s %d\n", title, body_b64, color);
						free(body_b64);
						free(title);
					} else {
						rz_cons_printf("\t\"0x%08" PFMT64x "\" ["
							       "URL=\"%s/0x%08" PFMT64x "\", fillcolor=\"%s\","
							       "color=\"%s\", fontname=\"%s\","
							       "label=\"%s\"]\n",
							bbi->addr, fcn->name, bbi->addr,
							fill_color, label_color, font, str);
					}
				}
			}
			free(str);
		}
	}
	return nodes;
}
static int core_analysis_graph_construct_nodes(RzCore *core, RzAnalysisFunction *fcn, int opts, PJ *pj, Sdb *DB) {
	RzAnalysisBlock *bbi;
	RzListIter *iter;
	int is_keva = opts & RZ_CORE_ANALYSIS_KEYVALUE;
	int is_star = opts & RZ_CORE_ANALYSIS_STAR;
	int is_json = opts & RZ_CORE_ANALYSIS_JSON;
	int is_html = rz_cons_singleton()->is_html;
	int left = 300;
	int top = 0;

	int is_json_format_disasm = opts & RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM;
	char *pal_curr = palColorFor("graph.current");
	char *pal_traced = palColorFor("graph.traced");
	char *pal_box4 = palColorFor("graph.box4");
	const char *font = rz_config_get(core->config, "graph.font");
	bool color_current = rz_config_get_i(core->config, "graph.gv.current");
	char *str;
	int nodes = 0;
	rz_list_foreach (fcn->bbs, iter, bbi) {
		if (is_keva) {
			char key[128];
			sdb_array_push_num(DB, "bbs", bbi->addr, 0);
			snprintf(key, sizeof(key), "bb.0x%08" PFMT64x ".size", bbi->addr);
			sdb_num_set(DB, key, bbi->size, 0);  
		} else if (is_json) {
			RzDebugTracepoint *t = rz_debug_trace_get(core->dbg, bbi->addr);
			ut8 *buf = malloc(bbi->size);
			pj_o(pj);
			pj_kn(pj, "offset", bbi->addr);
			pj_kn(pj, "size", bbi->size);
			if (bbi->jump != UT64_MAX) {
				pj_kn(pj, "jump", bbi->jump);
			}
			if (bbi->fail != -1) {
				pj_kn(pj, "fail", bbi->fail);
			}
			if (bbi->switch_op) {
				RzAnalysisSwitchOp *op = bbi->switch_op;
				pj_k(pj, "switchop");
				pj_o(pj);
				pj_kn(pj, "offset", op->addr);
				pj_kn(pj, "defval", op->def_val);
				pj_kn(pj, "maxval", op->max_val);
				pj_kn(pj, "minval", op->min_val);
				pj_k(pj, "cases");
				pj_a(pj);
				RzAnalysisCaseOp *case_op;
				RzListIter *case_iter;
				rz_list_foreach (op->cases, case_iter, case_op) {
					pj_o(pj);
					pj_kn(pj, "offset", case_op->addr);
					pj_kn(pj, "value", case_op->value);
					pj_kn(pj, "jump", case_op->jump);
					pj_end(pj);
				}
				pj_end(pj);
				pj_end(pj);
			}
			if (t) {
				pj_k(pj, "trace");
				pj_o(pj);
				pj_ki(pj, "count", t->count);
				pj_ki(pj, "times", t->times);
				pj_end(pj);
			}
			pj_kn(pj, "colorize", bbi->colorize);
			pj_k(pj, "ops");
			pj_a(pj);
			if (buf) {
				rz_io_read_at(core->io, bbi->addr, buf, bbi->size);
				if (is_json_format_disasm) {
					rz_core_print_disasm(core->print, core, bbi->addr, buf, bbi->size, bbi->size, 0, 1, true, pj, NULL);
				} else {
					rz_core_print_disasm_json(core, bbi->addr, buf, bbi->size, 0, pj);
				}
				free(buf);
			} else {
				eprintf("cannot allocate %" PFMT64u " byte(s)\n", bbi->size);
			}
			pj_end(pj);
			pj_end(pj);
			continue;
		}
		if ((str = core_analysis_graph_label(core, bbi, opts))) {
			if (opts & RZ_CORE_ANALYSIS_GRAPHDIFF) {
				const char *difftype = bbi->diff ? (
									   bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_MATCH ? "lightgray" : bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_UNMATCH ? "yellow"
																							   : "red")
								 : "orange";
				const char *diffname = bbi->diff ? (
									   bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_MATCH ? "match" : bbi->diff->type == RZ_ANALYSIS_DIFF_TYPE_UNMATCH ? "unmatch"
																						       : "new")
								 : "unk";
				if (is_keva) {
					sdb_set(DB, "diff", diffname, 0);
					sdb_set(DB, "label", str, 0);
				} else if (!is_json) {
					nodes++;
					RzConfigHold *hc = rz_config_hold_new(core->config);
					rz_config_hold_i(hc, "scr.color", "scr.utf8", "asm.offset", "asm.lines",
						"asm.cmt.right", "asm.lines.fcn", "asm.bytes", NULL);
					rz_config_set_i(core->config, "scr.utf8", 0);
					rz_config_set_i(core->config, "asm.offset", 0);
					rz_config_set_i(core->config, "asm.lines", 0);
					rz_config_set_i(core->config, "asm.cmt.right", 0);
					rz_config_set_i(core->config, "asm.lines.fcn", 0);
					rz_config_set_i(core->config, "asm.bytes", 0);
					if (!is_star) {
						rz_config_set_i(core->config, "scr.color", 0);  
					}

					if (bbi->diff && bbi->diff->type != RZ_ANALYSIS_DIFF_TYPE_MATCH && core->c2) {
						char dff_from[32], dff_to[32];

						RzCore *c = core->c2;
						RzConfig *oc = c->config;
						char *str = rz_core_cmd_strf(core, "pdb @ 0x%08" PFMT64x, bbi->addr);
						c->config = core->config;
						 
						char *str2 = rz_core_cmd_strf(c, "pdb @ 0x%08" PFMT64x, bbi->diff->addr);
						snprintf(dff_from, sizeof(dff_from), "0x%08" PFMT64x, bbi->addr);
						snprintf(dff_to, sizeof(dff_to), "0x%08" PFMT64x, bbi->diff->addr);

						RzDiff *dff = rz_diff_lines_new(str, str2, NULL);
						char *diffstr = rz_diff_unified_text(dff, dff_from, dff_to, false, false);
						rz_diff_free(dff);

						if (diffstr) {
							char *nl = strchr(diffstr, '\n');
							if (nl) {
								nl = strchr(nl + 1, '\n');
								if (nl) {
									nl = strchr(nl + 1, '\n');
									if (nl) {
										rz_str_cpy(diffstr, nl + 1);
									}
								}
							}
						}

						if (is_star) {
							char *title = get_title(bbi->addr);
							if (!title) {
								rz_config_hold_free(hc);
								return false;
							}
							char *body_b64 = rz_base64_encode_dyn((const ut8 *)diffstr, strlen(diffstr));
							if (!body_b64) {
								free(title);
								rz_config_hold_free(hc);
								return false;
							}
							body_b64 = rz_str_prepend(body_b64, "base64:");
							rz_cons_printf("agn %s %s %d\n", title, body_b64, bbi->diff->type);
							free(body_b64);
							free(title);
						} else {
							diffstr = rz_str_replace(diffstr, "\n", "\\l", 1);
							diffstr = rz_str_replace(diffstr, "\"", "'", 1);
							rz_cons_printf(" \"0x%08" PFMT64x "\" [fillcolor=\"%s\","
								       "color=\"black\", fontname=\"%s\","
								       " label=\"%s\", URL=\"%s/0x%08" PFMT64x "\"]\n",
								bbi->addr, difftype, diffstr, font, fcn->name,
								bbi->addr);
						}
						free(diffstr);
						c->config = oc;
					} else {
						if (is_star) {
							char *title = get_title(bbi->addr);
							if (!title) {
								rz_config_hold_free(hc);
								return false;
							}
							char *body_b64 = rz_base64_encode_dyn((const ut8 *)str, strlen(title));
							int color = (bbi && bbi->diff) ? bbi->diff->type : 0;
							if (!title || !body_b64) {
								free(body_b64);
								free(title);
								rz_config_hold_free(hc);
								return false;
							}
							body_b64 = rz_str_prepend(body_b64, "base64:");
							rz_cons_printf("agn %s %s %d\n", title, body_b64, color);
							free(body_b64);
							free(title);
						} else {
							rz_cons_printf(" \"0x%08" PFMT64x "\" [fillcolor=\"%s\","
								       "color=\"black\", fontname=\"%s\","
								       " label=\"%s\", URL=\"%s/0x%08" PFMT64x "\"]\n",
								bbi->addr, difftype, str, font, fcn->name, bbi->addr);
						}
					}
					rz_config_set_i(core->config, "scr.color", 1);
					rz_config_hold_free(hc);
				}
			} else {
				if (is_html) {
					nodes++;
					rz_cons_printf("<p class=\"block draggable\" style=\""
						       "top: %dpx; left: %dpx; width: 400px;\" id=\""
						       "_0x%08" PFMT64x "\">\n%s</p>\n",
						top, left, bbi->addr, str);
					left = left ? 0 : 600;
					if (!left) {
						top += 250;
					}
				} else if (!is_json && !is_keva) {
					bool current = rz_analysis_block_contains(bbi, core->offset);
					const char *label_color = bbi->traced
						? pal_traced
						: (current && color_current)
						? pal_curr
						: pal_box4;
					const char *fill_color = ((current && color_current) || label_color == pal_traced) ? pal_traced : "white";
					nodes++;
					if (is_star) {
						char *title = get_title(bbi->addr);
						char *body_b64 = rz_base64_encode_dyn((const ut8 *)str, strlen(str));
						int color = (bbi && bbi->diff) ? bbi->diff->type : 0;
						if (!title || !body_b64) {
							free(body_b64);
							free(title);
							return false;
						}
						body_b64 = rz_str_prepend(body_b64, "base64:");
						rz_cons_printf("agn %s %s %d\n", title, body_b64, color);
						free(body_b64);
						free(title);
					} else {
						rz_cons_printf("\t\"0x%08" PFMT64x "\" ["
							       "URL=\"%s/0x%08" PFMT64x "\", fillcolor=\"%s\","
							       "color=\"%s\", fontname=\"%s\","
							       "label=\"%s\"]\n",
							bbi->addr, fcn->name, bbi->addr,
							fill_color, label_color, font, str);
					}
				}
			}
			free(str);
		}
	}
	return nodes;
}