+struct func_proto_dep;
+
+struct func_prototype {
+ char name[NAMELEN];
+ int id;
+ int argc_stack;
+ int regmask_dep;
+ int has_ret:3; // -1, 0, 1: unresolved, no, yes
+ unsigned int dep_resolved:1;
+ unsigned int is_stdcall:1;
+ struct func_proto_dep *dep_func;
+ int dep_func_cnt;
+};
+
+struct func_proto_dep {
+ char *name;
+ struct func_prototype *proto;
+ int regmask_live; // .. at the time of call
+ unsigned int ret_dep:1; // return from this is caller's return
+};
+
+static struct func_prototype *hg_fp;
+static int hg_fp_cnt;
+
+static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; i < fp->dep_func_cnt; i++)
+ if (IS(fp->dep_func[i].name, name))
+ return &fp->dep_func[i];
+
+ return NULL;
+}
+
+static void hg_fp_add_dep(struct func_prototype *fp, const char *name)
+{
+ // is it a dupe?
+ if (hg_fp_find_dep(fp, name))
+ return;
+
+ if ((fp->dep_func_cnt & 0xff) == 0) {
+ fp->dep_func = realloc(fp->dep_func,
+ sizeof(fp->dep_func[0]) * (fp->dep_func_cnt + 0x100));
+ my_assert_not(fp->dep_func, NULL);
+ memset(&fp->dep_func[fp->dep_func_cnt], 0,
+ sizeof(fp->dep_func[0]) * 0x100);
+ }
+ fp->dep_func[fp->dep_func_cnt].name = strdup(name);
+ fp->dep_func_cnt++;
+}
+
+static int hg_fp_cmp_name(const void *p1_, const void *p2_)
+{
+ const struct func_prototype *p1 = p1_, *p2 = p2_;
+ return strcmp(p1->name, p2->name);
+}
+
+#if 0
+static int hg_fp_cmp_id(const void *p1_, const void *p2_)
+{
+ const struct func_prototype *p1 = p1_, *p2 = p2_;
+ return p1->id - p2->id;
+}
+#endif
+
+static void gen_hdr(const char *funcn, int opcnt)
+{
+ struct func_prototype *fp;
+ struct func_proto_dep *dep;
+ struct parsed_data *pd;
+ struct parsed_op *po;
+ int regmask_save = 0;
+ int regmask_dst = 0;
+ int regmask_dep = 0;
+ int max_bp_offset = 0;
+ int has_ret = -1;
+ int from_caller = 0;
+ int i, j, l, ret;
+ int depth, reg;
+
+ if ((hg_fp_cnt & 0xff) == 0) {
+ hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100));
+ my_assert_not(hg_fp, NULL);
+ memset(hg_fp + hg_fp_cnt, 0, sizeof(hg_fp[0]) * 0x100);
+ }
+
+ fp = &hg_fp[hg_fp_cnt];
+ snprintf(fp->name, sizeof(fp->name), "%s", funcn);
+ fp->id = hg_fp_cnt;
+ hg_fp_cnt++;
+
+ // pass1:
+ // - collect calls
+ // - resolve all branches
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ po->bt_i = -1;
+ po->btj = NULL;
+
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ if (po->operand[0].type == OPT_LABEL)
+ hg_fp_add_dep(fp, opr_name(po, 0));
+
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
+ continue;
+
+ if (po->operand[0].type == OPT_REGMEM) {
+ pd = try_resolve_jumptab(i, opcnt);
+ if (pd == NULL)
+ goto tailcall;
+
+ po->btj = pd;
+ continue;
+ }
+
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l][0] && IS(po->operand[0].name, g_labels[l])) {
+ add_label_ref(&g_label_refs[l], i);
+ po->bt_i = l;
+ break;
+ }
+ }
+
+ if (po->bt_i != -1 || (po->flags & OPF_RMD))
+ continue;
+
+ if (po->operand[0].type == OPT_LABEL)
+ // assume tail call
+ goto tailcall;
+
+ ferr(po, "unhandled branch\n");
+
+tailcall:
+ po->op = OP_CALL;
+ po->flags |= OPF_TAIL;
+ if (i > 0 && ops[i - 1].op == OP_POP)
+ po->flags |= OPF_ATAIL;
+ i--; // reprocess
+ }
+
+ // pass2:
+ // - remove dead labels
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i][0] != 0 && g_label_refs[i].i == -1)
+ g_labels[i][0] = 0;
+ }
+
+ // pass3:
+ // - track saved regs
+ // - try to figure out arg-regs
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ if (reg < 0)
+ ferr(po, "reg not set for push?\n");
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 1, 0, &depth, 0);
+ if (ret == 1) {
+ regmask_save |= 1 << reg;
+ po->flags |= OPF_RMD;
+ scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 2, 0, &depth, 1);
+ continue;
+ }
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ regmask_save |= 1 << reg;
+ po->flags |= OPF_RMD;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
+ continue;
+ }
+ }
+ else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) {
+ for (j = i + 1; j < opcnt; j++) {
+ if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
+ || ops[j].op == OP_PUSH || g_labels[i][0] != 0)
+ {
+ break;
+ }
+
+ if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
+ {
+ po->flags |= OPF_RMD;
+ ops[j].datap = po;
+ break;
+ }
+ }
+ continue;
+ }
+ else if (po->op == OP_CALL) {
+ po->regmask_dst |= 1 << xAX;
+
+ dep = hg_fp_find_dep(fp, po->operand[0].name);
+ if (dep != NULL)
+ dep->regmask_live = regmask_save | regmask_dst;
+ }
+ else if (po->op == OP_RET) {
+ if (po->operand_cnt > 0)
+ fp->is_stdcall = 1;
+ }
+
+ if (has_ret != 0 && (po->flags & OPF_TAIL)) {
+ if (po->op == OP_CALL) {
+ j = i;
+ ret = 1;
+ }
+ else {
+ struct parsed_opr opr = { 0, };
+ opr.type = OPT_REG;
+ opr.reg = xAX;
+ j = -1;
+ from_caller = 0;
+ ret = resolve_origin(i, &opr, i + opcnt * 3, &j, &from_caller);
+ }
+
+ if (ret == -1 && from_caller) {
+ // unresolved eax - probably void func
+ has_ret = 0;
+ }
+ else {
+ if (ops[j].op == OP_CALL) {
+ dep = hg_fp_find_dep(fp, po->operand[0].name);
+ if (dep != NULL)
+ dep->ret_dep = 1;
+ else
+ has_ret = 1;
+ }
+ else
+ has_ret = 1;
+ }
+ }
+
+ l = po->regmask_src & ~(regmask_save | regmask_dst);
+#if 0
+ if (l)
+ fnote(po, "dep |= %04x, dst %04x, save %04x\n", l,
+ regmask_dst, regmask_save);
+#endif
+ regmask_dep |= l;
+ regmask_dst |= po->regmask_dst;
+ }
+
+ if (has_ret == -1 && (regmask_dep & (1 << xAX)))
+ has_ret = 1;
+
+ for (i = 0; i < g_eqcnt; i++)
+ if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32)
+ max_bp_offset = g_eqs[i].offset;
+
+ if (max_bp_offset > 0) {
+ max_bp_offset = (max_bp_offset + 3) & ~3;
+ fp->argc_stack = max_bp_offset / 4 - 1;
+ if (!(g_ida_func_attr & IDAFA_BP_FRAME))
+ fp->argc_stack--;
+ }
+
+ fp->regmask_dep = regmask_dep & ~(1 << xSP);
+ fp->has_ret = has_ret;
+}
+
+static void hg_fp_resolve_deps(struct func_prototype *fp)
+{
+ struct func_prototype fp_s;
+ int i;
+
+ // this thing is recursive, so mark first..
+ fp->dep_resolved = 1;
+
+ for (i = 0; i < fp->dep_func_cnt; i++) {
+ strcpy(fp_s.name, fp->dep_func[i].name);
+ fp->dep_func[i].proto = bsearch(&fp_s, hg_fp, hg_fp_cnt,
+ sizeof(hg_fp[0]), hg_fp_cmp_name);
+ if (fp->dep_func[i].proto != NULL) {
+ if (!fp->dep_func[i].proto->dep_resolved)
+ hg_fp_resolve_deps(fp->dep_func[i].proto);
+
+ fp->regmask_dep |= ~fp->dep_func[i].regmask_live
+ & fp->dep_func[i].proto->regmask_dep;
+
+ if (fp->has_ret == -1)
+ fp->has_ret = fp->dep_func[i].proto->has_ret;
+ }
+ }
+}
+
+static void output_hdr(FILE *fout)
+{
+ struct func_prototype *fp;
+ int had_usercall = 0;
+ int regmask_dep;
+ int argc_stack;
+ int i, j, arg;
+
+ // resolve deps
+ qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name);
+ for (i = 0; i < hg_fp_cnt; i++)
+ hg_fp_resolve_deps(&hg_fp[i]);
+
+ // note: messes up .proto ptr, don't use
+ //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
+
+ for (i = 0; i < hg_fp_cnt; i++) {
+ fp = &hg_fp[i];
+ if (fp->has_ret == -1)
+ fprintf(fout, "// ret unresolved\n");
+#if 0
+ fprintf(fout, "// dep:");
+ for (j = 0; j < fp->dep_func_cnt; j++) {
+ fprintf(fout, " %s/", fp->dep_func[j].name);
+ if (fp->dep_func[j].proto != NULL)
+ fprintf(fout, "%04x/%d", fp->dep_func[j].proto->regmask_dep,
+ fp->dep_func[j].proto->has_ret);
+ }
+ fprintf(fout, "\n");
+#endif
+
+ regmask_dep = fp->regmask_dep;
+ argc_stack = fp->argc_stack;
+
+ fprintf(fout, fp->has_ret ? "int " : "void ");
+ if (regmask_dep && (fp->is_stdcall || argc_stack == 0)
+ && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0)
+ {
+ fprintf(fout, "__fastcall ");
+ if (had_usercall)
+ fprintf(fout, " "); // align
+ if (!(regmask_dep & (1 << xDX)) && argc_stack == 0)
+ argc_stack = 1;
+ else
+ argc_stack += 2;
+ regmask_dep = 0;
+ }
+ else if (regmask_dep && !fp->is_stdcall) {
+ fprintf(fout, "/*__usercall*/ ");
+ had_usercall = 1;
+ }
+ else if (regmask_dep) {
+ fprintf(fout, "/*__userpurge*/ ");
+ had_usercall = 1;
+ }
+ else if (fp->is_stdcall)
+ fprintf(fout, "__stdcall ");
+ else
+ fprintf(fout, "__cdecl ");
+
+ fprintf(fout, "%s(", fp->name);
+
+ arg = 0;
+ for (j = 0; j < xSP; j++) {
+ if (regmask_dep & (1 << j)) {
+ arg++;
+ if (arg != 1)
+ fprintf(fout, ", ");
+ fprintf(fout, "int a%d/*<%s>*/", arg, regs_r32[j]);
+ }
+ }
+
+ for (j = 0; j < argc_stack; j++) {
+ arg++;
+ if (arg != 1)
+ fprintf(fout, ", ");
+ fprintf(fout, "int a%d", arg);
+ }
+
+ fprintf(fout, ");\n");
+ }
+}
+