14 static HANDLE proc_thread
;
18 static int input_swapped
;
19 static int input_replay
;
21 static int pausemenu_active
= 0;
24 static int16_t p1_magic_max
;
25 static int16_t p2_magic_max
;
27 static int infodlg_on
;
29 /* recording history stuff */
35 static struct input_history
{
36 struct input
*ih_vals
;
47 hist
.ih_vals
= realloc(hist
.ih_vals
,
48 sizeof(hist
.ih_vals
[0]) * hist
.ih_alloc
);
54 void *vals_save
= hist
.ih_vals
;
55 memset(&hist
, 0, sizeof(hist
));
57 hist
.ih_vals
= vals_save
;
63 recording_rec(int val
)
65 if (!hist
.ih_cur
&& val
== 0) {
66 /* don't actually start recording until we get a real input */
70 if (!hist
.ih_cur
|| hist
.ih_cur
->val
!= val
) {
71 if (hist
.ih_size
>= hist
.ih_alloc
) {
72 hist
.ih_alloc
+= 1024;
75 hist
.ih_cur
= &hist
.ih_vals
[hist
.ih_size
++];
76 hist
.ih_cur
->val
= val
;
77 hist
.ih_cur
->count
= 1;
84 recording_rewind(void)
86 if (hist
.ih_cur
&& hist
.ih_cur
->val
== 0) {
87 /* if we ended with a neutral input, trim it from the end */
92 hist
.ih_cur
= &hist
.ih_vals
[0];
105 if (hist
.ih_curcount
>= hist
.ih_cur
->count
) {
107 hist
.ih_curcount
= 0;
108 if (hist
.ih_pos
>= hist
.ih_size
) {
109 /* reset back to the beginning, but give ourselves 1 frame of neutral */
111 hist
.ih_cur
= &hist
.ih_vals
[0];
114 hist
.ih_cur
= &hist
.ih_vals
[hist
.ih_pos
];
117 return hist
.ih_cur
->val
;
123 if ((val
& (CTRL_BACK
| CTRL_FWD
))) {
124 val
^= (CTRL_BACK
| CTRL_FWD
);
129 static const char *ctrl_state
= "CONTROLLING P1";
131 switch_state(const char *str
)
142 static int ignore_ud
= CTRL_UP
;
143 static int ignore_bf
= CTRL_FWD
;
149 switch (val
& (CTRL_BACK
| CTRL_FWD
)) {
150 case CTRL_BACK
: ignore_bf
= CTRL_FWD
; break;
151 case CTRL_FWD
: ignore_bf
= CTRL_BACK
; break;
153 switch (val
& (CTRL_UP
| CTRL_DOWN
)) {
154 case CTRL_UP
: ignore_ud
= CTRL_DOWN
; break;
155 case CTRL_DOWN
: ignore_ud
= CTRL_UP
; break;
158 if ((val
& (CTRL_BACK
| CTRL_FWD
)) == (CTRL_BACK
| CTRL_FWD
)) {
161 if ((val
& (CTRL_UP
| CTRL_DOWN
)) == (CTRL_UP
| CTRL_DOWN
)) {
165 if (val
== last_val
|| !val
) {
169 if (!PostThreadMessage(mainthread
, CTU_INPUT
, 0, (LPARAM
)val
)) {
170 ctud("PostThreadMessage (CTU_INPUT) %d\n", (int)GetLastError());
175 static unsigned char p1_reverse
;
180 void *addr_meter_partial
;
181 void *addr_meter_partial_max
;
185 unsigned char start_meter
[2];
193 static struct comboinfo comboinfo_p1
= {
194 .addr_health
= P2_HP_VADDR
,
195 .addr_meter
= P1_SUPER_BARS_VADDR
,
196 .addr_meter_partial
= P1_SUPER_PARTIAL_VADDR
,
197 .addr_meter_partial_max
= P1_SUPER_PARTIAL_MAX_VADDR
,
198 .addr_magic
= P1_MAGIC_VADDR
,
200 static struct comboinfo comboinfo_p2
= {
201 .addr_health
= P1_HP_VADDR
,
202 .addr_meter
= P2_SUPER_BARS_VADDR
,
203 .addr_meter_partial
= P2_SUPER_PARTIAL_VADDR
,
204 .addr_meter_partial_max
= P2_SUPER_PARTIAL_MAX_VADDR
,
205 .addr_magic
= P2_MAGIC_VADDR
,
209 combo_update(struct comboinfo
*cinfo
)
212 unsigned char cur_meter
[2];
213 int16_t cur_magic
, start_magic
;
214 unsigned char max_partial_meter
;
216 ReadProcessMemory(proc
, cinfo
->addr_health
, &cur_health
,
217 sizeof(cur_health
), NULL
);
218 if (cur_health
>= cinfo
->start_health
) {
219 cinfo
->combo_health
= 0;
222 ReadProcessMemory(proc
, cinfo
->addr_meter
, &cur_meter
[0],
223 sizeof(cur_meter
[0]), NULL
);
224 ReadProcessMemory(proc
, cinfo
->addr_meter_partial
, &cur_meter
[1],
225 sizeof(cur_meter
[1]), NULL
);
226 ReadProcessMemory(proc
, cinfo
->addr_magic
, &cur_magic
,
227 sizeof(cur_magic
), NULL
);
228 ReadProcessMemory(proc
, cinfo
->addr_meter_partial_max
, &max_partial_meter
,
229 sizeof(max_partial_meter
), NULL
);
231 cinfo
->combo_health
= cinfo
->start_health
- cur_health
;
232 cinfo
->combo_meter
= (cur_meter
[0] +
233 ((float)cur_meter
[1])/((float)max_partial_meter
)) -
234 (cinfo
->start_meter
[0] +
235 ((float)cinfo
->start_meter
[1])/((float)max_partial_meter
));
237 start_magic
= cinfo
->start_magic
;
241 if (start_magic
< 0) {
244 cinfo
->combo_magic
= cur_magic
- start_magic
;
248 combo_reset(struct comboinfo
*cinfo
)
250 ReadProcessMemory(proc
, cinfo
->addr_health
, &cinfo
->start_health
,
251 sizeof(cinfo
->start_health
), NULL
);
252 ReadProcessMemory(proc
, cinfo
->addr_meter
, &cinfo
->start_meter
[0],
253 sizeof(cinfo
->start_meter
[0]), NULL
);
254 ReadProcessMemory(proc
, cinfo
->addr_meter_partial
, &cinfo
->start_meter
[1],
255 sizeof(cinfo
->start_meter
[1]), NULL
);
256 ReadProcessMemory(proc
, cinfo
->addr_magic
, &cinfo
->start_magic
,
257 sizeof(cinfo
->start_magic
), NULL
);
263 /* the FiM window should now exist */
264 if (!PostThreadMessage(mainthread
, CTU_SETTITLE
, 0, 0)) {
265 ctud("PostThreadMessage (FIMWINDOW) %d\n", (int)GetLastError());
270 activate_pausemenu(void)
272 ReadProcessMemory(proc
, P1_REVERSE_VADDR
, &p1_reverse
, sizeof(p1_reverse
), NULL
);
274 if (!PostThreadMessage(mainthread
, CTU_MENUON
, 0, 0)) {
275 ctud("PostThreadMessage (MENUON) %d\n", (int)GetLastError());
278 pausemenu_active
= 1;
285 if (last_val
== val
) {
292 if (!PostThreadMessage(mainthread
, CTU_MENUINPUT
, (WPARAM
)val
, 0)) {
293 ctud("PostThreadMessage (MENUINPUT) %d\n", (int)GetLastError());
297 static int refill_health
= 1;
298 static int refill_meter
= 0;
299 static int refill_magic
= 0;
300 static int deplete_magic
= 0;
305 return dlg
[index
].val_arr
[dlg
[index
].cur
].val
;
309 deactivate_infodlg(void)
315 if (!PostThreadMessage(mainthread
, CTU_INFOOFF
, 0, 0)) {
316 ctud("PostThreadMessage (INFOOFF) %d\n", (int)GetLastError());
320 activate_infodlg(void)
326 if (!PostThreadMessage(mainthread
, CTU_INFOON
, 0, 0)) {
327 ctud("PostThreadMessage (INFOON) %d\n", (int)GetLastError());
332 deactivate_pausemenu(void)
334 if (!PostThreadMessage(mainthread
, CTU_MENUOFF
, 0, 0)) {
335 ctud("PostThreadMessage (MENUOFF) %d\n", (int)GetLastError());
338 pausemenu_active
= 0;
340 input_swapped
= input_replay
= recording
= p2_block
= 0;
343 switch (dlgval(CTU_IDX_ACTION
)) {
344 case CTU_DLGITEM_Control
:
345 ctud("**INPUT SWAPPED\n");
348 case CTU_DLGITEM_Record
:
349 ctud("**RECORDING P2\n");
354 case CTU_DLGITEM_Replay
:
355 ctud("**REPLAYING RECORDING\n");
358 case CTU_DLGITEM_Recover
:
361 ctud("**CONTROLLING P1\n");
366 switch (dlgval(CTU_IDX_HEALTH
)) {
367 case CTU_DLGITEM_Refill
:
374 switch (dlgval(CTU_IDX_METER
)) {
375 case CTU_DLGITEM_0_Bars
:
378 case CTU_DLGITEM_1_Bar
:
381 case CTU_DLGITEM_2_Bars
:
384 case CTU_DLGITEM_3_Bars
:
389 refill_magic
= deplete_magic
= 0;
391 switch (dlgval(CTU_IDX_MAGIC
)) {
392 case CTU_DLGITEM_Refill
:
395 case CTU_DLGITEM_Deplete
:
400 switch (dlgval(CTU_IDX_INFO
)) {
401 case CTU_DLGITEM_Off
:
402 deactivate_infodlg();
410 static int round_no
= -1;
415 return round_no
>= 0;
419 should_refill_health(int state
)
421 switch (state
& STATE_STUN_MASK
) {
429 is_attacking(int state
)
431 switch (state
& STATE_STUN_MASK
) {
432 /* note that this is the same as STATE_STUN_GBOUNCE, but for our usages,
433 * it shouldn't matter if we mistake one for the other */
434 case STATE_STUN_ATTACK
:
443 static int p1_last_state
, p2_last_state
;
444 int p1_state
, p2_state
;
445 int p1_refill_ok
= 0, p2_refill_ok
= 0;
447 if (!refill_health
&& !refill_meter
&& !refill_magic
&& !infodlg_on
) {
451 ReadProcessMemory(proc
, P1_STATE_VADDR
, &p1_state
, 4, NULL
);
452 ReadProcessMemory(proc
, P2_STATE_VADDR
, &p2_state
, 4, NULL
);
453 if (p1_state
!= p1_last_state
) {
454 ctud("p1 state: 0x%x\n", (unsigned)p1_state
);
456 if (p2_state
!= p2_last_state
) {
457 ctud(" p2 state: 0x%x\n", (unsigned)p2_state
);
459 p2_last_state
= p2_state
;
460 p1_last_state
= p1_state
;
463 /* only refill meter/magic if we haven't attacked recently, so we don't
464 * refill instantly when someone starts a combo with meter or magic */
465 static const int cooldown_timer
= 20;
466 static int p1_refill_cooldown
, p2_refill_cooldown
;
468 if (is_attacking(p1_state
)) {
469 p1_refill_cooldown
= cooldown_timer
;
471 if (p1_refill_cooldown
< 1) {
474 p1_refill_cooldown
--;
478 if (is_attacking(p2_state
)) {
479 p2_refill_cooldown
= cooldown_timer
;
481 if (p2_refill_cooldown
< 1) {
484 p2_refill_cooldown
--;
490 combo_update(&comboinfo_p1
);
491 combo_update(&comboinfo_p2
);
494 if (should_refill_health(p1_state
)) {
498 ReadProcessMemory(proc
, P1_MAX_HP_VADDR
, &max
, sizeof(max
), NULL
);
499 ReadProcessMemory(proc
, P1_HP_VADDR
, &cur
, sizeof(max
), NULL
);
501 ctud("p2 combo damage: %d/%d (%.1f%%)\n", (max
- cur
), max
,
502 (100.0*(float)(max
-cur
))/((float)max
));
503 WriteProcessMemory(proc
, P1_HP_VADDR
, &max
, sizeof(max
), NULL
);
506 /* remember, fill the _opponent_ super/magic meter when we are recovered */
507 if (refill_meter
&& p2_refill_ok
) {
510 ReadProcessMemory(proc
, P2_SUPER_BARS_VADDR
, &bars
, sizeof(bars
), NULL
);
511 ReadProcessMemory(proc
, P2_SUPER_PARTIAL_VADDR
, &part
, sizeof(part
), NULL
);
512 if (bars
!= (refill_meter
-1) || part
!= 0) {
513 bars
= refill_meter
- 1;
515 WriteProcessMemory(proc
, P2_SUPER_BARS_VADDR
, &bars
, sizeof(bars
), NULL
);
516 WriteProcessMemory(proc
, P2_SUPER_PARTIAL_VADDR
, &part
, sizeof(part
), NULL
);
519 if (refill_magic
&& p2_refill_ok
) {
521 ReadProcessMemory(proc
, P2_MAGIC_VADDR
, &magic
, sizeof(magic
), NULL
);
522 if (magic
!= p2_magic_max
) {
523 WriteProcessMemory(proc
, P2_MAGIC_VADDR
, &p2_magic_max
, sizeof(p2_magic_max
), NULL
);
526 if (deplete_magic
&& p2_refill_ok
) {
528 ReadProcessMemory(proc
, P2_MAGIC_VADDR
, &magic
, sizeof(magic
), NULL
);
531 WriteProcessMemory(proc
, P2_MAGIC_VADDR
, &magic
, sizeof(magic
), NULL
);
534 combo_reset(&comboinfo_p2
);
536 if (should_refill_health(p2_state
)) {
540 ReadProcessMemory(proc
, P2_MAX_HP_VADDR
, &max
, sizeof(max
), NULL
);
541 ReadProcessMemory(proc
, P2_HP_VADDR
, &cur
, sizeof(max
), NULL
);
543 ctud("p1 combo damage: %d/%d (%.1f%%)\n", (max
- cur
), max
,
544 (100.0*(float)(max
-cur
))/((float)max
));
545 WriteProcessMemory(proc
, P2_HP_VADDR
, &max
, sizeof(max
), NULL
);
548 /* remember, fill the _opponent_ super/magic meter when we are recovered */
549 if (refill_meter
&& p1_refill_ok
) {
552 ReadProcessMemory(proc
, P1_SUPER_BARS_VADDR
, &bars
, sizeof(bars
), NULL
);
553 ReadProcessMemory(proc
, P1_SUPER_PARTIAL_VADDR
, &part
, sizeof(part
), NULL
);
554 if (bars
!= (refill_meter
-1) || part
!= 0) {
555 bars
= refill_meter
- 1;
557 WriteProcessMemory(proc
, P1_SUPER_BARS_VADDR
, &bars
, sizeof(bars
), NULL
);
558 WriteProcessMemory(proc
, P1_SUPER_PARTIAL_VADDR
, &part
, sizeof(part
), NULL
);
561 if (refill_magic
&& p1_refill_ok
) {
563 ReadProcessMemory(proc
, P1_MAGIC_VADDR
, &magic
, sizeof(magic
), NULL
);
564 if (magic
!= p1_magic_max
) {
565 WriteProcessMemory(proc
, P1_MAGIC_VADDR
, &p1_magic_max
, sizeof(p1_magic_max
), NULL
);
568 if (deplete_magic
&& p1_refill_ok
) {
570 ReadProcessMemory(proc
, P1_MAGIC_VADDR
, &magic
, sizeof(magic
), NULL
);
573 WriteProcessMemory(proc
, P1_MAGIC_VADDR
, &magic
, sizeof(magic
), NULL
);
576 combo_reset(&comboinfo_p1
);
581 handle_p1_input(int val
)
584 static int just_swapped
;
585 int mylastval
= last_val
;
592 if (!input_swapped
) {
593 static int choosing_char
;
595 ReadProcessMemory(proc
, P1_CHAR_COLOR_VADDR
, &color
, sizeof(color
), NULL
);
596 if (!choosing_char
&& color
< 0) {
599 if (choosing_char
&& color
>= 0) {
602 last_val
= mylastval
= val
;
608 if ((val
& CTRL_PAUSE
) && !(mylastval
& CTRL_PAUSE
)) {
609 if (pausemenu_active
) {
610 deactivate_pausemenu();
612 activate_pausemenu();
615 ctud("we are fighting; sending pause to fm2k\n");
618 ctud("not fighting; swallowed pause\n");
623 if ((val
& CTRL_PAUSE
)) {
627 if (pausemenu_active
) {
629 val
= translate(val
);
637 if (just_swapped
&& val
== mylastval
) {
642 p2_input
= translate(val
);
643 output_ctrl(p2_input
);
653 block_input(int state
, int *atech
)
659 switch ((state
& STATE_STUN_MASK
)) {
661 /* we are in hitstun; hold back to try to block */
666 case STATE_STUN_BLOCK
:
667 /* hitstun; try to block */
671 case STATE_STUN_GBOUNCE
:
672 /* try to ground tech */
673 val
= CTRL_BACK
| CTRL_DOWN
;
680 handle_p2_input(int val
)
688 } else if (input_replay
) {
689 val
= recording_play();
691 } else if (p2_block
) {
692 static int p2_last_state
, block_cooldown
, block_cooldown_val
;
695 unsigned int p2_height
= HEIGHT_GROUNDED
;
697 ReadProcessMemory(proc
, P2_STATE_VADDR
, &p2_state
, 4, NULL
);
699 val
= block_input(p2_state
, &tech
);
701 /* if we are in the air, hold 'up' to air tech if we can */
702 ReadProcessMemory(proc
, P2_HEIGHT_VADDR
, &p2_height
, sizeof(p2_height
), NULL
);
703 if (p2_height
!= HEIGHT_GROUNDED
) {
709 if (block_cooldown
) {
711 if (!block_cooldown
) {
712 ctud(" == cooldown ending\n");
714 val
= block_cooldown_val
;
717 val
= block_input(p2_last_state
, &tech
);
720 block_cooldown_val
= val
;
725 p2_last_state
= p2_state
;
733 ctud("\nround_end\n");
734 switch_state("CONTROLLING P1");
737 recording
= input_swapped
= 0;
748 SystemTimeToFileTime(&st
, &ft
);
750 srand(ft
.dwLowDateTime
);
754 /* just always return rarity's stage for now, to ensure we minimize
755 * performance issues */
761 mainthread_update_input(void)
763 if (!PostThreadMessage(mainthread
, CTU_THREADUPDATE
, 0, 0)) {
764 ctud("PostThreadMessage (threadupdate) %d\n", (int)GetLastError());
768 static int stats_dirty
;
771 stat_health(void *haddr
, void *maxhaddr
, struct info_val
*val
)
774 ReadProcessMemory(proc
, haddr
, &health
, sizeof(health
), NULL
);
775 if (val
->val
[0] != health
) {
776 val
->val
[0] = health
;
782 ReadProcessMemory(proc
, maxhaddr
, &val
->val
[1], sizeof(val
->val
[1]), NULL
);
789 stat_meter(void *meter_addr
, void *partial_addr
, void *maxp_addr
,
790 struct info_val
*val
)
793 unsigned char partial
;
797 ReadProcessMemory(proc
, meter_addr
, &bars
, sizeof(bars
), NULL
);
798 ReadProcessMemory(proc
, partial_addr
, &partial
, sizeof(partial
), NULL
);
800 ReadProcessMemory(proc
, maxp_addr
, &max
, sizeof(max
), NULL
);
806 fval
= bars
+ ((float)partial
/(float)val
->val
[0]);
808 if (fval
!= val
->fval
) {
816 stat_magic(void *magic_addr
, int max
, struct info_val
*val
)
820 ReadProcessMemory(proc
, magic_addr
, &magic
, sizeof(magic
), NULL
);
828 if (magic
!= val
->val
[0]) {
833 if (max
!= val
->val
[1]) {
841 stat_combo(struct comboinfo
*cinfo
)
843 if (info_curc_dmg
.val
[0] != cinfo
->combo_health
) {
844 info_curc_dmg
.val
[0] = cinfo
->combo_health
;
845 info_curc_dmg
.dirty
= 1;
849 if (info_curc_meter
.fval
!= cinfo
->combo_meter
) {
850 info_curc_meter
.fval
= cinfo
->combo_meter
;
851 info_curc_meter
.dirty
= 1;
855 if (info_curc_magic
.val
[0] != cinfo
->combo_magic
) {
856 info_curc_magic
.val
[0] = cinfo
->combo_magic
;
857 info_curc_magic
.dirty
= 1;
861 if (info_curc_dmg
.val
[0] > info_maxc_dmg
.val
[0]) {
862 info_maxc_dmg
.val
[0] = info_curc_dmg
.val
[0];
863 info_maxc_dmg
.dirty
= 1;
866 if (info_maxc_meter
.fval
!= info_curc_meter
.fval
) {
867 info_maxc_meter
.fval
= info_curc_meter
.fval
;
868 info_maxc_meter
.dirty
= 1;
870 if (info_maxc_magic
.val
[0] != info_curc_magic
.val
[0]) {
871 info_maxc_magic
.val
[0] = info_curc_magic
.val
[0];
872 info_maxc_magic
.dirty
= 1;
886 stat_health(P1_HP_VADDR
, P1_MAX_HP_VADDR
, &info_p1_health
);
887 stat_health(P2_HP_VADDR
, P2_MAX_HP_VADDR
, &info_p2_health
);
889 stat_meter(P1_SUPER_BARS_VADDR
, P1_SUPER_PARTIAL_VADDR
, P1_SUPER_PARTIAL_MAX_VADDR
, &info_p1_meter
);
890 stat_meter(P2_SUPER_BARS_VADDR
, P2_SUPER_PARTIAL_VADDR
, P2_SUPER_PARTIAL_MAX_VADDR
, &info_p2_meter
);
892 stat_magic(P1_MAGIC_VADDR
, p1_magic_max
, &info_p1_magic
);
893 stat_magic(P2_MAGIC_VADDR
, p2_magic_max
, &info_p2_magic
);
895 if (comboinfo_p1
.combo_health
> 0 || comboinfo_p2
.combo_health
> 0) {
896 if (comboinfo_p1
.combo_health
>= comboinfo_p2
.combo_health
) {
897 stat_combo(&comboinfo_p1
);
899 stat_combo(&comboinfo_p2
);
904 if (!PostThreadMessage(mainthread
, CTU_INFOUPDATE
, 0, 0)) {
905 ctud("PostThreadMessage (INFOUPDATE) error %d\n", (int)GetLastError());
911 character_max_magic(int charid
)
914 case FIM_CHAR_TWILIGHT
:
915 case FIM_CHAR_APPLEJACK
:
918 case FIM_CHAR_RARITY
:
919 case FIM_CHAR_PINKIE
:
923 ctud("warning got weird character id 0x%x\n", (unsigned)charid
);
928 static int bg_volume
;
929 static int se_volume
;
932 handle_exc(DEBUG_EVENT
*de
)
934 if (de
->u
.Exception
.ExceptionRecord
.ExceptionCode
== EXCEPTION_ACCESS_VIOLATION
) {
935 ctud("Access Violation %08x at %08x: %s@%08x\n",
936 (unsigned)de
->u
.Exception
.ExceptionRecord
.ExceptionCode
,
937 (unsigned)de
->u
.Exception
.ExceptionRecord
.ExceptionAddress
,
938 de
->u
.Exception
.ExceptionRecord
.ExceptionInformation
[0] ? "read" : "write",
939 (unsigned)de
->u
.Exception
.ExceptionRecord
.ExceptionInformation
[1]);
940 return DBG_EXCEPTION_NOT_HANDLED
;
943 if (de
->u
.Exception
.ExceptionRecord
.ExceptionCode
== EXCEPTION_BREAKPOINT
) {
944 unsigned long address
;
947 if (de
->dwThreadId
!= proc_thread_id
) {
948 ctud("bad thread id: %d != %d\n", (int)de
->dwThreadId
, (int)proc_thread_id
);
949 proc_thread_id
= de
->dwThreadId
;
950 proc_thread
= OpenThread(THREAD_ALL_ACCESS
, FALSE
, proc_thread_id
);
951 mainthread_update_input();
953 ctud("OpenThread returned error %d\n", (int)GetLastError());
958 address
= (unsigned long)de
->u
.Exception
.ExceptionRecord
.ExceptionAddress
;
961 case STAGE_SELECT_BREAK
:
962 c
.ContextFlags
= CONTEXT_INTEGER
;
963 GetThreadContext(proc_thread
, &c
);
964 c
.Eax
= select_stage();
965 SetThreadContext(proc_thread
, &c
);
968 case ROUND_START_BREAK
:
975 ReadProcessMemory(proc
, P1_CHAR_VADDR
, &charid
, sizeof(charid
), NULL
);
976 p1_magic_max
= character_max_magic(charid
);
978 ReadProcessMemory(proc
, P2_CHAR_VADDR
, &charid
, sizeof(charid
), NULL
);
979 p2_magic_max
= character_max_magic(charid
);
983 case ROUND_END_BREAK
: {
986 ReadProcessMemory(proc
, ROUND_ESI_VADDR
, &mem
, 4, NULL
);
988 c
.ContextFlags
= CONTEXT_INTEGER
;
989 GetThreadContext(proc_thread
, &c
);
993 if (c
.Eax
== c
.Edx
) {
995 } else if (c
.Eax
== c
.Esi
) {
999 SetThreadContext(proc_thread
, &c
);
1005 case VS_P1_KEY_BREAK
:
1006 c
.ContextFlags
= CONTEXT_INTEGER
;
1007 GetThreadContext(proc_thread
, &c
);
1009 c
.Eax
= handle_p1_input(c
.Eax
);
1011 WriteProcessMemory(proc
, P1_INPUT_VADDR
, &c
.Eax
, 4, NULL
);
1014 if (last
!= c
.Eax
) {
1015 /*ctud(" === p1 input %x\n", (unsigned)c.Eax);*/
1019 SetThreadContext(proc_thread
, &c
);
1022 case VS_P2_KEY_BREAK
:
1023 c
.ContextFlags
= CONTEXT_INTEGER
;
1024 GetThreadContext(proc_thread
, &c
);
1026 c
.Eax
= handle_p2_input(c
.Eax
);
1028 WriteProcessMemory(proc
, P2_INPUT_VADDR
, &c
.Eax
, 4, NULL
);
1031 if (last
!= c
.Eax
) {
1032 /*ctud(" === p2 input %x\n", (unsigned)c.Eax);*/
1036 SetThreadContext(proc_thread
, &c
);
1039 case SE_VOLUME_BREAK
:
1040 c
.ContextFlags
= CONTEXT_INTEGER
;
1041 GetThreadContext(proc_thread
, &c
);
1043 SetThreadContext(proc_thread
, &c
);
1045 case BGM_VOLUME_BREAK
:
1046 c
.ContextFlags
= CONTEXT_INTEGER
;
1047 GetThreadContext(proc_thread
, &c
);
1049 SetThreadContext(proc_thread
, &c
);
1053 static int frame_counter
;
1055 c
.ContextFlags
= CONTEXT_INTEGER
;
1056 GetThreadContext(proc_thread
, &c
);
1058 SetThreadContext(proc_thread
, &c
);
1060 if (frame_counter
== 10) {
1064 if (frame_counter
> 10) {
1065 if (round_no
>= 0) {
1075 ctud("unhandled breakpoint: 0x%lx\n", address
);
1078 unsigned char nop
= 0x90;
1081 c
.ContextFlags
= CONTEXT_FULL
;
1082 GetThreadContext(proc_thread
, &c
);
1084 WriteProcessMemory(proc
, (void *)c
.Eip
, &nop
, 1, NULL
);
1085 FlushInstructionCache(proc
, NULL
, 0);
1090 return DBG_CONTINUE
;
1096 /* frame draw callback */
1097 WriteProcessMemory(proc
, FRAME_CADDR
, FRAME_CODE
, sizeof(FRAME_CODE
), NULL
);
1100 WriteProcessMemory(proc
, STAGE_SELECT_CADDR
, STAGE_SELECT_CODE
, sizeof(STAGE_SELECT_CODE
), NULL
);
1102 /* record and alter player inputs */
1103 WriteProcessMemory(proc
, VS_P1_KEY_CADDR
, VS_P1_KEY_CODE
, sizeof(VS_P1_KEY_CODE
), NULL
);
1104 WriteProcessMemory(proc
, VS_P2_KEY_CADDR
, VS_P2_KEY_CODE
, sizeof(VS_P2_KEY_CODE
), NULL
);
1106 /* notice when a round starts */
1107 WriteProcessMemory(proc
, ROUND_START_CADDR
, ROUND_START_CODE
,
1108 sizeof(ROUND_START_CODE
), NULL
);
1109 /* notice when a new round starts */
1110 WriteProcessMemory(proc
, ROUND_END_CADDR
, ROUND_END_CODE
,
1111 sizeof(ROUND_END_CODE
), NULL
);
1113 if (bg_volume
|| se_volume
) {
1114 WriteProcessMemory(proc
, VOLUME_SET_1_CADDR
, VOLUME_SET_1_CODE
, sizeof(VOLUME_SET_1_CODE
), NULL
);
1115 WriteProcessMemory(proc
, VOLUME_SET_2_CADDR
, VOLUME_SET_2_CODE
, sizeof(VOLUME_SET_2_CODE
), NULL
);
1120 runproc(LPVOID lpParam
)
1122 char path
[] = "FightingIsMagic.exe";
1123 /*char path[] = "K:\\games\\mlp\\FightingIsMagic\\FightingIsMagic.exe";*/
1125 PROCESS_INFORMATION pi
;
1128 ZeroMemory(&si
, sizeof(si
));
1130 ZeroMemory(&pi
, sizeof(pi
));
1132 if (!CreateProcess(NULL
, path
/* command line */,
1133 NULL
/* process security attrs */,
1134 NULL
/* thread security attrs */,
1135 FALSE
/* child inherits handles? */,
1136 DEBUG_PROCESS
/* flags */,
1137 NULL
/* environment */,
1140 ctud("Could not CreateProcess, error %d\n", (int)GetLastError());
1144 proc
= OpenProcess(PROCESS_ALL_ACCESS
, FALSE
/* inherit handles? */,
1147 ctud("Could not OpenProcess, error %d\n", (int)GetLastError());
1151 while (WaitForDebugEvent(&de
, INFINITE
)) {
1152 unsigned int state
= DBG_EXCEPTION_NOT_HANDLED
;
1153 switch (de
.dwDebugEventCode
) {
1154 case EXCEPTION_DEBUG_EVENT
:
1155 state
= handle_exc(&de
);
1157 case CREATE_THREAD_DEBUG_EVENT
:
1160 case CREATE_PROCESS_DEBUG_EVENT
:
1161 proc_thread
= de
.u
.CreateProcessInfo
.hThread
;
1162 proc_thread_id
= de
.dwThreadId
;
1163 mainthread_update_input();
1167 case EXIT_THREAD_DEBUG_EVENT
:
1170 case EXIT_PROCESS_DEBUG_EVENT
:
1173 case LOAD_DLL_DEBUG_EVENT
:
1176 case UNLOAD_DLL_DEBUG_EVENT
:
1179 case OUTPUT_DEBUG_STRING_EVENT
: {
1180 char *str
= malloc(de
.u
.DebugString
.nDebugStringLength
);
1181 ReadProcessMemory(proc
, de
.u
.DebugString
.lpDebugStringData
, str
,
1182 de
.u
.DebugString
.nDebugStringLength
, NULL
);
1183 ctud(" = OUTPUT_DEBUG_STRING_EVENT(%s)\n", str
);
1189 ctud(" = RIP_EVENT\n");
1193 ctud(" = unhandled de.dwDebugEventCode %d\n", (int)de
.dwDebugEventCode
);
1196 ContinueDebugEvent(de
.dwProcessId
, de
.dwThreadId
, state
);
1200 PostThreadMessage(mainthread
, WM_QUIT
, 0, 0);
1206 WinMain(HINSTANCE hinstance
, HINSTANCE hPrevInstance
,
1207 LPSTR lpCmdLine
, int nCmdShow
)
1211 argv
= CommandLineToArgvW(GetCommandLineW(), &argc
);
1212 for (i
= 1; i
< argc
; i
++) {
1213 if (StrCmpW(argv
[i
], L
"-stdout") == 0) {
1216 ctud("-stdout takes an argument\n");
1219 if (!_wfreopen(argv
[i
], L
"w", stdout
)) {
1224 if (StrCmpW(argv
[i
], L
"-stderr") == 0) {
1227 ctud("-stderr takes an argument\n");
1230 if (!_wfreopen(argv
[i
], L
"w", stderr
)) {
1235 if (StrCmpW(argv
[i
], L
"-bgvol") == 0) {
1238 ctud("-bgvol takes an argument\n");
1241 if (!StrToIntExW(argv
[i
], 0, &bg_volume
)) {
1242 ctud("Invalid -bgvol integer specified (error %d)\n", (int)GetLastError());
1247 if (StrCmpW(argv
[i
], L
"-sevol") == 0) {
1250 ctud("-bgvol takes an argument\n");
1253 if (!StrToIntExW(argv
[i
], 0, &se_volume
)) {
1254 ctud("Invalid -sevol integer specified (error %d)\n", (int)GetLastError());
1259 if (StrCmpW(argv
[i
], L
"-vol") == 0) {
1262 ctud("-vol takes an argument\n");
1265 if (!StrToIntExW(argv
[i
], 0, &bg_volume
)) {
1266 ctud("Invalid -vol integer specified (error %d)\n", (int)GetLastError());
1269 se_volume
= bg_volume
;
1272 if (StrCmpW(argv
[i
], L
"-mutebg") == 0) {
1276 if (StrCmpW(argv
[i
], L
"-mutese") == 0) {
1280 if (StrCmpW(argv
[i
], L
"-mute") == 0) {
1281 bg_volume
= se_volume
= -10000;
1284 ctud("Invalid option '%S' given\n", argv
[i
]);
1289 mainthread
= GetCurrentThreadId();
1291 if (!CreateThread(NULL
/* attrs*/,
1296 NULL
/* thread id */)) {
1297 ctud("CreateThread error %d\n", (int)GetLastError());