+ for (j = i - 1, seq_p = 0; j >= 0 && seq_p < seq_len; j--) {
+ if (ops[j].op != OP_POP || ops[j].operand[0].type != OPT_REG)
+ break;
+ if (ops[j].operand[0].reg != seq[seq_p])
+ break;
+ seq_p++;
+ }
+ found = seq_len = seq_p;
+ }
+ if (!found)
+ return;
+
+ for (i = 0; i < seq_len; i++)
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+
+ for (; i < opcnt && seq_len > 0; i++) {
+ if (!(ops[i].flags & OPF_TAIL))
+ continue;
+
+ for (j = i - 1, seq_p = 0; j >= 0 && seq_p < seq_len; j--) {
+ ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ seq_p++;
+ }
+ }
+
+ // unlike pushes after sub esp,
+ // IDA treats pushes like this as part of var area
+ *fsz += seq_len * 4;
+}
+
+static int scan_prologue_ecx(int i, int opcnt, int flags_set,
+ int limit, int *ecx_push_out)
+{
+ const struct parsed_proto *pp;
+ int ecx_push = 0, other_push = 0;
+ int ret;
+
+ while (limit > 0 && ops[i].op == OP_PUSH
+ && IS(opr_name(&ops[i], 0), "ecx"))
+ {
+ ops[i].flags |= flags_set;
+ ecx_push++;
+ i++;
+ limit--;
+ }
+
+ ret = i;
+ if (ecx_push == 0 || flags_set != 0)
+ goto out;
+
+ // check if some of the pushes aren't really call args
+ for (; i < opcnt; i++) {
+ if (i > 0 && g_labels[i] != NULL)
+ break;
+ if (ops[i].flags & (OPF_JMP|OPF_TAIL))
+ break;
+ if (ops[i].op == OP_PUSH)
+ other_push++;
+ }
+
+ if (ops[i].op != OP_CALL)
+ goto out;
+
+ pp = ops[i].pp;
+ if (pp == NULL && ops[i].operand[0].type == OPT_LABEL)
+ pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1);
+ if (pp == NULL)
+ goto out;
+
+ ferr_assert(&ops[i], ecx_push + other_push >= pp->argc_stack);
+ if (other_push < pp->argc_stack)
+ ecx_push -= pp->argc_stack - other_push;
+
+out:
+ if (ecx_push_out != NULL)
+ *ecx_push_out = ecx_push;
+ return ret;
+}
+
+static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub)
+{
+ const char *name;
+ int j, len, ret;
+ int ecx_tmp = 0;
+
+ for (; i < opcnt; i++)
+ if (!(ops[i].flags & OPF_DONE))
+ break;
+
+ ret = scan_prologue_ecx(i, opcnt, 0, 4, &ecx_tmp);
+ if (ecx_tmp > 0) {
+ scan_prologue_ecx(i, opcnt, OPF_RMD | OPF_DONE | OPF_NOREGS,
+ ecx_tmp, NULL);
+ g_stack_fsz += 4 * ecx_tmp;
+ *ecx_push += ecx_tmp;
+ i = ret;
+ }
+
+ for (; i < opcnt; i++) {
+ if (i > 0 && g_labels[i] != NULL)
+ break;
+ if (ops[i].flags & (OPF_JMP|OPF_TAIL))
+ break;
+ if (ops[i].flags & OPF_DONE)
+ continue;
+ if (ops[i].op == OP_PUSH)
+ break;
+ if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
+ && ops[i].operand[1].type == OPT_CONST)
+ {
+ g_stack_fsz += opr_const(&ops[i], 1);
+ ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+ i++;
+ *esp_sub = 1;
+ break;
+ }
+ if (ops[i].op == OP_LEA && ops[i].operand[0].reg == xSP
+ && ops[i].operand[1].type == OPT_REGMEM
+ && IS_START(ops[i].operand[1].name, "esp-"))
+ {
+ name = ops[i].operand[1].name;
+ ret = sscanf(name, "esp-%x%n", &j, &len);
+ ferr_assert(&ops[i], ret == 1 && len == strlen(name));
+ g_stack_fsz += j;