+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
+
+ if (pp->arg[arg].reg != NULL) {
+ if (pp->arg[arg].type.is_retreg)
+ fprintf(fout, "&%s", pp->arg[arg].reg);
+ else
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
+
+ // stack arg
+ tmp_op = pp->arg[arg].datap;
+ if (tmp_op == NULL)
+ ferr(po, "parsed_op missing for arg%d\n", arg);
+
+ if (tmp_op->flags & OPF_VAPUSH) {
+ fprintf(fout, "ap");
+ }
+ else if (tmp_op->p_argpass != 0) {
+ fprintf(fout, "a%d", tmp_op->p_argpass);
+ }
+ else if (tmp_op->p_argnum != 0) {
+ fprintf(fout, "%s%s", cast,
+ saved_arg_name(buf1, sizeof(buf1),
+ tmp_op->p_arggrp, tmp_op->p_argnum));
+ }
+ else {
+ fprintf(fout, "%s",
+ out_src_opr(buf1, sizeof(buf1),
+ tmp_op, &tmp_op->operand[0], cast, 0));
+ }
+ }
+ }
+ fprintf(fout, ");");
+
+ if (strstr(pp->ret_type.name, "int64")) {
+ fprintf(fout, "\n");
+ fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
+ fprintf(fout, "%seax = tmp64;", buf3);
+ }
+
+ if (pp->is_unresolved) {
+ snprintf(buf2, sizeof(buf2), " unresolved %dreg",
+ pp->argc_reg);
+ strcat(g_comment, buf2);
+ }
+
+ if (po->flags & OPF_TAIL) {
+ ret = 0;
+ if (i == opcnt - 1 || pp->is_noreturn)
+ ret = 0;
+ else if (IS(pp->ret_type.name, "void"))
+ ret = 1;
+ else if (IS(g_func_pp->ret_type.name, "void"))
+ ret = 1;
+ // else already handled as 'return f()'
+
+ if (ret) {
+ if (!IS(g_func_pp->ret_type.name, "void")) {
+ ferr(po, "int func -> void func tailcall?\n");
+ }
+ else {
+ fprintf(fout, "\n%sreturn;", buf3);
+ strcat(g_comment, " ^ tailcall");
+ }
+ }
+ else
+ strcat(g_comment, " tailcall");
+ }
+ if (pp->is_noreturn)
+ strcat(g_comment, " noreturn");
+ if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
+ strcat(g_comment, " argframe");
+ if (po->flags & OPF_CC)
+ strcat(g_comment, " cond");
+
+ if (po->flags & OPF_CC)
+ fprintf(fout, "\n }");
+
+ delayed_flag_op = NULL;
+ last_arith_dst = NULL;
+ break;
+
+ case OP_RET:
+ if (g_func_pp->is_vararg)
+ fprintf(fout, " va_end(ap);\n");
+ if (g_func_pp->has_retreg) {
+ for (arg = 0; arg < g_func_pp->argc; arg++)
+ if (g_func_pp->arg[arg].type.is_retreg)
+ fprintf(fout, " *r_%s = %s;\n",
+ g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
+ }
+
+ if (IS(g_func_pp->ret_type.name, "void")) {
+ if (i != opcnt - 1 || label_pending)
+ fprintf(fout, " return;");
+ }
+ else if (g_func_pp->ret_type.is_ptr) {
+ fprintf(fout, " return (%s)eax;",
+ g_func_pp->ret_type.name);
+ }
+ else if (IS(g_func_pp->ret_type.name, "__int64"))
+ fprintf(fout, " return ((u64)edx << 32) | eax;");
+ else
+ fprintf(fout, " return eax;");
+
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_PUSH:
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->p_argnum != 0) {
+ // special case - saved func arg
+ fprintf(fout, " %s = %s;",
+ saved_arg_name(buf2, sizeof(buf2),
+ po->p_arggrp, po->p_argnum), buf1);
+ break;
+ }
+ else if (po->flags & OPF_RSAVE) {
+ fprintf(fout, " s_%s = %s;", buf1, buf1);
+ break;
+ }
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " *(--esp) = %s;", buf1);
+ break;
+ }
+ if (!(g_ida_func_attr & IDAFA_NORETURN))
+ ferr(po, "stray push encountered\n");
+ no_output = 1;
+ break;
+
+ case OP_POP:
+ if (po->flags & OPF_RSAVE) {
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = s_%s;", buf1, buf1);
+ break;
+ }
+ else if (po->datap != NULL) {
+ // push/pop pair
+ tmp_op = po->datap;
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2),
+ tmp_op, &tmp_op->operand[0],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ break;
+ }
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " %s = *esp++;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ break;
+ }
+ else
+ ferr(po, "stray pop encountered\n");
+ break;
+
+ case OP_NOP:
+ no_output = 1;
+ break;
+
+ // mmx
+ case OP_EMMS:
+ strcpy(g_comment, "(emms)");
+ break;
+
+ default:
+ no_output = 1;
+ ferr(po, "unhandled op type %d, flags %x\n",
+ po->op, po->flags);
+ break;
+ }
+
+ if (g_comment[0] != 0) {
+ char *p = g_comment;
+ while (my_isblank(*p))
+ p++;
+ fprintf(fout, " // %s", p);
+ g_comment[0] = 0;
+ no_output = 0;
+ }
+ if (!no_output)
+ fprintf(fout, "\n");
+
+ // some sanity checking
+ if (po->flags & OPF_REP) {
+ if (po->op != OP_STOS && po->op != OP_MOVS
+ && po->op != OP_CMPS && po->op != OP_SCAS)
+ ferr(po, "unexpected rep\n");
+ if (!(po->flags & (OPF_REPZ|OPF_REPNZ))
+ && (po->op == OP_CMPS || po->op == OP_SCAS))
+ ferr(po, "cmps/scas with plain rep\n");
+ }
+ if ((po->flags & (OPF_REPZ|OPF_REPNZ))
+ && po->op != OP_CMPS && po->op != OP_SCAS)
+ ferr(po, "unexpected repz/repnz\n");
+
+ if (pfomask != 0)
+ ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
+
+ // see is delayed flag stuff is still valid
+ if (delayed_flag_op != NULL && delayed_flag_op != po) {
+ if (is_any_opr_modified(delayed_flag_op, po, 0))
+ delayed_flag_op = NULL;
+ }
+
+ if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
+ if (is_opr_modified(last_arith_dst, po))
+ last_arith_dst = NULL;
+ }
+
+ label_pending = 0;
+ }
+
+ if (g_stack_fsz && !g_stack_frame_used)
+ fprintf(fout, " (void)sf;\n");
+
+ fprintf(fout, "}\n\n");
+
+ gen_x_cleanup(opcnt);
+}
+
+static void gen_x_cleanup(int opcnt)
+{
+ int i;
+
+ for (i = 0; i < opcnt; i++) {
+ struct label_ref *lr, *lr_del;
+
+ lr = g_label_refs[i].next;
+ while (lr != NULL) {
+ lr_del = lr;
+ lr = lr->next;
+ free(lr_del);
+ }
+ g_label_refs[i].i = -1;
+ g_label_refs[i].next = NULL;
+
+ if (ops[i].op == OP_CALL) {
+ if (ops[i].pp)
+ proto_release(ops[i].pp);
+ }
+ }
+ g_func_pp = NULL;
+}
+
+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;
+ const struct parsed_proto *pp; // seed pp, if any
+};
+
+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 scanned_var {
+ char name[NAMELEN];
+ enum opr_lenmod lmod;
+ unsigned int is_seeded:1;
+ unsigned int is_c_str:1;
+ const struct parsed_proto *pp; // seed pp, if any
+} *hg_vars;
+static int hg_var_cnt;
+
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count);
+
+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
+
+// recursive register dep pass
+// - track saved regs (part 2)
+// - try to figure out arg-regs
+// - calculate reg deps
+static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
+ struct func_prototype *fp, int regmask_save, int regmask_dst,
+ int *regmask_dep, int *has_ret)
+{
+ struct func_proto_dep *dep;
+ struct parsed_op *po;
+ int from_caller = 0;
+ int depth;
+ int j, l;
+ int reg;
+ int ret;
+
+ for (; i < opcnt; i++)
+ {
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ return;
+ cbits[i >> 3] |= (1 << (i & 7));
+
+ po = &ops[i];
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++) {
+ gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ return;
+ }
+
+ if (po->bt_i < 0) {
+ ferr(po, "dead branch\n");
+ return;
+ }
+
+ if (po->flags & OPF_CJMP) {
+ gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ else {
+ i = po->bt_i - 1;
+ }
+ continue;
+ }
+
+ if (po->flags & OPF_FARG)
+ /* (just calculate register deps) */;
+ else 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");
+
+ if (po->flags & OPF_RSAVE) {
+ regmask_save |= 1 << reg;
+ continue;
+ }
+ if (po->flags & OPF_DONE)
+ continue;
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 2, 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 * 3, 0, &depth, 1);
+ continue;
+ }
+ }
+ else if (po->flags & OPF_RMD)
+ 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 (fp->argc_stack >= 0
+ && fp->argc_stack != po->operand[0].val / 4)
+ ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
+ fp->argc_stack = po->operand[0].val / 4;
+ }
+ }
+
+ 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 * 4, &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 = regmask_save | regmask_dst;
+ if (g_bp_frame && !(po->flags & OPF_EBP_S))
+ l |= 1 << xBP;
+
+ l = po->regmask_src & ~l;
+#if 0
+ if (l)
+ fnote(po, "dep |= %04x, dst %04x, save %04x (f %x)\n",
+ l, regmask_dst, regmask_save, po->flags);
+#endif
+ *regmask_dep |= l;
+ regmask_dst |= po->regmask_dst;
+
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
+static void gen_hdr(const char *funcn, int opcnt)
+{
+ int save_arg_vars[MAX_ARG_GRP] = { 0, };
+ unsigned char cbits[MAX_OPS / 8];
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
+ struct func_prototype *fp;
+ struct parsed_data *pd;
+ struct parsed_op *po;
+ const char *tmpname;
+ int regmask_dummy = 0;
+ int regmask_dep;
+ int max_bp_offset = 0;
+ int has_ret;
+ int i, j, l, ret;
+
+ 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;
+ fp->argc_stack = -1;
+ hg_fp_cnt++;
+
+ // perhaps already in seed header?
+ fp->pp = proto_parse(g_fhdr, funcn, 1);
+ if (fp->pp != NULL) {
+ fp->argc_stack = fp->pp->argc_stack;
+ fp->is_stdcall = fp->pp->is_stdcall;
+ fp->regmask_dep = get_pp_arg_regmask(fp->pp);
+ fp->has_ret = !IS(fp->pp->ret_type.name, "void");
+ return;
+ }
+
+ g_bp_frame = g_sp_frame = g_stack_fsz = 0;
+ g_stack_frame_used = 0;
+
+ // pass1:
+ // - handle ebp/esp frame, remove ops related to it
+ scan_prologue_epilogue(opcnt);
+
+ // pass2:
+ // - 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|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL) {
+ tmpname = opr_name(po, 0);
+ pp = NULL;
+ if (po->operand[0].type == OPT_LABEL) {
+ hg_fp_add_dep(fp, tmpname);
+
+ // perhaps a call to already known func?
+ pp_c = proto_parse(g_fhdr, tmpname, 1);
+ if (pp_c != NULL)
+ pp = proto_clone(pp_c);
+ }
+ else if (po->datap != NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+
+ ret = parse_protostr(po->datap, pp);
+ if (ret < 0)
+ ferr(po, "bad protostr supplied: %s\n", (char *)po->datap);
+ free(po->datap);
+ po->datap = NULL;
+ }
+ if (pp != NULL && pp->is_noreturn)
+ po->flags |= OPF_TAIL;
+
+ po->pp = pp;
+ 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] != NULL
+ && 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
+ }
+
+ // pass3:
+ // - remove dead labels
+ // - handle push <const>/pop pairs
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
+ free(g_labels[i]);
+ g_labels[i] = NULL;
+ }
+
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
+ scan_for_pop_const(i, opcnt);
+ }
+
+ // pass4:
+ // - process trivial calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL)
+ {
+ pp = process_call_early(i, opcnt, &j);
+ if (pp != NULL) {
+ if (!(po->flags & OPF_ATAIL))
+ // since we know the args, try to collect them
+ if (collect_call_args_early(po, i, pp, ®mask_dummy) != 0)
+ pp = NULL;
+ }
+
+ if (pp != NULL) {
+ if (j >= 0) {
+ // commit esp adjust
+ ops[j].flags |= OPF_RMD;
+ if (ops[j].op != OP_POP)
+ patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+ else
+ ops[j].flags |= OPF_DONE;
+ }
+
+ po->flags |= OPF_DONE;
+ }
+ }
+ }
+
+ // pass5:
+ // - track saved regs (simple)
+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ // regmask_save |= 1 << po->operand[0].reg; // do it later
+ po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
+ }
+ }
+ else if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ {
+ pp = process_call(i, opcnt);
+
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ // since we know the args, collect them
+ ret = collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars,
+ i + opcnt * 1);
+ }
+ }
+ }
+
+ // pass6
+ memset(cbits, 0, sizeof(cbits));
+ regmask_dep = 0;
+ has_ret = -1;
+
+ gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, ®mask_dep, &has_ret);
+
+ // find unreachable code - must be fixed in IDA
+ for (i = 0; i < opcnt; i++)
+ {
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ continue;
+
+ if (ops[i].op != OP_NOP)
+ ferr(&ops[i], "unreachable code\n");
+ }
+
+ 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 (fp->argc_stack < 0) {
+ max_bp_offset = (max_bp_offset + 3) & ~3;
+ fp->argc_stack = max_bp_offset / 4;
+ if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0)
+ fp->argc_stack--;
+ }
+
+ fp->regmask_dep = regmask_dep & ~(1 << xSP);
+ fp->has_ret = has_ret;
+#if 0
+ printf("// has_ret %d, regmask_dep %x\n",
+ fp->has_ret, fp->regmask_dep);
+ output_hdr_fp(stdout, fp, 1);
+ if (IS(funcn, "sub_100073FD")) exit(1);
+#endif
+
+ gen_x_cleanup(opcnt);
+}
+
+static void hg_fp_resolve_deps(struct func_prototype *fp)
+{
+ struct func_prototype fp_s;
+ int dep;
+ 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);
+
+ dep = ~fp->dep_func[i].regmask_live
+ & fp->dep_func[i].proto->regmask_dep;
+ fp->regmask_dep |= dep;
+ // printf("dep %s %s |= %x\n", fp->name,
+ // fp->dep_func[i].name, dep);
+
+ if (fp->has_ret == -1)
+ fp->has_ret = fp->dep_func[i].proto->has_ret;
+ }
+ }
+}
+
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count)
+{
+ const struct parsed_proto *pp;
+ char *p, namebuf[NAMELEN];
+ const char *name;
+ int regmask_dep;
+ int argc_stack;
+ int j, arg;
+
+ for (; count > 0; count--, fp++) {
+ 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
+
+ p = strchr(fp->name, '@');
+ if (p != NULL) {
+ memcpy(namebuf, fp->name, p - fp->name);
+ namebuf[p - fp->name] = 0;
+ name = namebuf;
+ }
+ else
+ name = fp->name;
+ if (name[0] == '_')
+ name++;
+
+ pp = proto_parse(g_fhdr, name, 1);
+ if (pp != NULL && pp->is_include)
+ continue;
+
+ if (fp->pp != NULL) {
+ // part of seed, output later
+ continue;
+ }
+
+ regmask_dep = fp->regmask_dep;
+ argc_stack = fp->argc_stack;
+
+ fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name :
+ (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 (!(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*/ ");
+ }
+ else if (regmask_dep) {
+ fprintf(fout, "/*__userpurge*/ ");
+ }
+ else if (fp->is_stdcall)
+ fprintf(fout, " __stdcall ");
+ else
+ fprintf(fout, " __cdecl ");
+
+ fprintf(fout, "%s(", name);
+
+ arg = 0;
+ for (j = 0; j < xSP; j++) {
+ if (regmask_dep & (1 << j)) {
+ arg++;
+ if (arg != 1)
+ fprintf(fout, ", ");
+ if (fp->pp != NULL)
+ fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
+ else
+ fprintf(fout, "int");
+ fprintf(fout, " a%d/*<%s>*/", arg, regs_r32[j]);
+ }
+ }
+
+ for (j = 0; j < argc_stack; j++) {
+ arg++;
+ if (arg != 1)
+ fprintf(fout, ", ");
+ if (fp->pp != NULL) {
+ fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
+ if (!fp->pp->arg[arg - 1].type.is_ptr)
+ fprintf(fout, " ");
+ }
+ else
+ fprintf(fout, "int ");
+ fprintf(fout, "a%d", arg);
+ }
+
+ fprintf(fout, ");\n");
+ }
+}
+
+static void output_hdr(FILE *fout)
+{
+ static const char *lmod_c_names[] = {
+ [OPLM_UNSPEC] = "???",
+ [OPLM_BYTE] = "uint8_t",
+ [OPLM_WORD] = "uint16_t",
+ [OPLM_DWORD] = "uint32_t",
+ [OPLM_QWORD] = "uint64_t",
+ };
+ const struct scanned_var *var;
+ char line[256] = { 0, };
+ int i;
+
+ // 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);
+
+ // output variables
+ for (i = 0; i < hg_var_cnt; i++) {
+ var = &hg_vars[i];
+
+ if (var->pp != NULL)
+ // part of seed
+ continue;
+ else if (var->is_c_str)
+ fprintf(fout, "extern %-8s %s[];", "char", var->name);
+ else
+ fprintf(fout, "extern %-8s %s;",
+ lmod_c_names[var->lmod], var->name);
+
+ if (var->is_seeded)
+ fprintf(fout, " // seeded");
+ fprintf(fout, "\n");
+ }
+
+ fprintf(fout, "\n");
+
+ // output function prototypes
+ output_hdr_fp(fout, hg_fp, hg_fp_cnt);