Skip to content

Instantly share code, notes, and snippets.

@JakobBruenker
Last active January 7, 2018 17:56
Show Gist options
  • Save JakobBruenker/a2a8c1e9d7afb136e1963a92bca51507 to your computer and use it in GitHub Desktop.
Save JakobBruenker/a2a8c1e9d7afb136e1963a92bca51507 to your computer and use it in GitHub Desktop.
Mirror's Edge TAS
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
// Take care to call the Bindings as extended Key Press if the keys have an
// extended scan code
const WORD const BINDINGS[21] = {
0x11, // Move Forward
0x1F, // Move Backward
0x1E, // Strafe Left
0x20, // Strafe Right
0x39, // Up Action
0x2A, // Down Action
0x1D, // Walk
0x10, // Turn 90/180
0x49, // Attack
0x12, // Interact
0x13, // Reaction Time
0x51, // Pick Up/Drop
0x21, // Weapon Zoom
0x0F, // Objectives/Time Trial Reset
0x32, // Hint
0x01, // Escape
0x48, // Up Arrow
0x50, // Down Arrow
0x4B, // Left Arrow
0x4D, // Right Arrow
0x1C, // Enter
};
INPUT MakeKeyboardInput(WORD wScan) {
INPUT kip;
kip.type = INPUT_KEYBOARD;
kip.ki.time = 0;
kip.ki.wVk = 0;
kip.ki.dwExtraInfo = 0;
// use hardware scan code instead of virtual key code
kip.ki.dwFlags = KEYEVENTF_SCANCODE;
kip.ki.wScan = wScan;
return kip;
}
void SendKey(WORD wScan, int state) {
INPUT kip = MakeKeyboardInput(wScan);
switch (state) {
case 1:
kip.ki.dwFlags |= KEYEVENTF_KEYUP;
break;
case 0:
// fall through
default:
break;
}
SendInput(1, &kip, sizeof (INPUT));
}
void SendKeyDown(WORD wScan) {
SendKey(wScan, 0);
}
void SendKeyUp(WORD wScan) {
SendKey(wScan, 1);
}
void SendKeyPress(WORD wScan) {
INPUT kip = MakeKeyboardInput(wScan);
kip.ki.dwFlags &= ~KEYEVENTF_KEYUP;
SendInput(1, &kip, sizeof (INPUT));
kip.ki.dwFlags |= KEYEVENTF_KEYUP;
SendInput(1, &kip, sizeof (INPUT));
}
void SendExtendedKeyPress(WORD wScan) {
INPUT kip = MakeKeyboardInput(wScan);
kip.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
kip.ki.dwFlags &= ~KEYEVENTF_KEYUP;
SendInput(1, &kip, sizeof (INPUT));
kip.ki.dwFlags |= KEYEVENTF_KEYUP;
SendInput(1, &kip, sizeof (INPUT));
}
// Try to avoid using this as it can interfere with key presses and mouse
// movement
void SendClick(int button) {
INPUT mip;
mip.type = INPUT_MOUSE;
mip.mi.dx = 0L;
mip.mi.dy = 0L;
mip.mi.time = 0;
mip.mi.mouseData = 0;
mip.mi.dwExtraInfo = 0;
WORD dwFlagsDown;
WORD dwFlagsUp;
switch (button) {
case 1:
dwFlagsDown = MOUSEEVENTF_RIGHTDOWN;
dwFlagsUp = MOUSEEVENTF_RIGHTUP;
break;
case 0:
// fall through
default:
dwFlagsDown = MOUSEEVENTF_LEFTDOWN;
dwFlagsUp = MOUSEEVENTF_LEFTUP;
break;
}
mip.mi.dwFlags = dwFlagsDown;
SendInput(1, &mip, sizeof(INPUT));
mip.mi.dwFlags = dwFlagsUp;
SendInput(1, &mip, sizeof(INPUT));
}
// Try to avoid using this as it can interfere with key presses and mouse
// movement
void SendLeftClick() {
SendClick(0);
}
// Try to avoid using this as it can interfere with key presses and mouse
// movement
void SendRightClick() {
SendClick(1);
}
void SendMouseMove(int dx, int dy) {
INPUT mip;
mip.type = INPUT_MOUSE;
mip.mi.dx = (LONG)dx;
mip.mi.dy = (LONG)dy;
mip.mi.time = 0;
mip.mi.mouseData = 0;
mip.mi.dwExtraInfo = 0;
mip.mi.dwFlags = MOUSEEVENTF_MOVE;
SendInput(1, &mip, sizeof(INPUT));
}
void StartForward() {
SendKeyDown(BINDINGS[0]);
}
void StopForward() {
SendKeyUp(BINDINGS[0]);
}
void StartBackward() {
SendKeyDown(BINDINGS[1]);
}
void StopBackward() {
SendKeyUp(BINDINGS[1]);
}
void StartLeft() {
SendKeyDown(BINDINGS[2]);
}
void StopLeft() {
SendKeyUp(BINDINGS[2]);
}
void StartRight() {
SendKeyDown(BINDINGS[3]);
}
void StopRight() {
SendKeyUp(BINDINGS[3]);
}
void Jump() {
SendKeyPress(BINDINGS[4]);
}
void StartDown() {
SendKeyDown(BINDINGS[5]);
}
void StopDown() {
SendKeyUp(BINDINGS[5]);
}
void StartWalk() {
SendKeyDown(BINDINGS[6]);
}
void StopWalk() {
SendKeyUp(BINDINGS[6]);
}
void Turn() {
SendKeyPress(BINDINGS[7]);
}
void Attack() {
SendExtendedKeyPress(BINDINGS[8]);
}
void Interact() {
SendKeyPress(BINDINGS[9]);
}
void Reaction() {
SendKeyPress(BINDINGS[10]);
}
void PickUp() {
SendExtendedKeyPress(BINDINGS[11]);
}
void Zoom() {
SendKeyPress(BINDINGS[12]);
}
void Objectives() {
SendKeyPress(BINDINGS[13]);
}
void StartHint() {
SendKeyDown(BINDINGS[14]);
}
void StopHint() {
SendKeyUp(BINDINGS[14]);
}
void Esc() {
SendKeyPress(BINDINGS[15]);
}
void UpArrow() {
SendExtendedKeyPress(BINDINGS[16]);
}
void DownArrow() {
SendExtendedKeyPress(BINDINGS[17]);
}
void LeftArrow() {
SendExtendedKeyPress(BINDINGS[18]);
}
void RightArrow() {
SendExtendedKeyPress(BINDINGS[19]);
}
void Enter() {
SendKeyPress(BINDINGS[20]);
}
void Action(char* action) {
if (!strncmp("fd", action, 2)) {
StartForward();
puts("fd");
} else if (!strncmp("fu", action, 2)) {
StopForward();
puts("fu");
} else if (!strncmp("bd", action, 2)) {
StartBackward();
puts("bd");
} else if (!strncmp("bu", action, 2)) {
StopBackward();
puts("bu");
} else if (!strncmp("ld", action, 2)) {
StartLeft();
puts("ld");
} else if (!strncmp("lu", action, 2)) {
StopLeft();
puts("lu");
} else if (!strncmp("rd", action, 2)) {
StartRight();
puts("rd");
} else if (!strncmp("ru", action, 2)) {
StopRight();
puts("ru");
} else if (!strncmp("jp", action, 2)) {
Jump();
puts("jp");
} else if (!strncmp("dd", action, 2)) {
StartDown();
puts("dd");
} else if (!strncmp("du", action, 2)) {
StopDown();
puts("du");
} else if (!strncmp("wd", action, 2)) {
StartWalk();
puts("wd");
} else if (!strncmp("wu", action, 2)) {
StopWalk();
puts("wu");
} else if (!strncmp("tp", action, 2)) {
Turn();
puts("tp");
} else if (!strncmp("ap", action, 2)) {
Attack();
puts("ap");
} else if (!strncmp("ip", action, 2)) {
Interact();
puts("ip");
} else if (!strncmp("rp", action, 2)) {
Reaction();
puts("rp");
} else if (!strncmp("pp", action, 2)) {
PickUp();
puts("pp");
} else if (!strncmp("zp", action, 2)) {
Zoom();
puts("zp");
} else if (!strncmp("op", action, 2)) {
Objectives();
puts("op");
} else if (!strncmp("hd", action, 2)) {
StartHint();
puts("hp");
} else if (!strncmp("hu", action, 2)) {
StopHint();
puts("hu");
} else if (!strncmp("ep", action, 2)) {
Esc();
puts("ep");
} else if (!strncmp("ua", action, 2)) {
UpArrow();
puts("ua");
} else if (!strncmp("da", action, 2)) {
DownArrow();
puts("da");
} else if (!strncmp("la", action, 2)) {
LeftArrow();
puts("la");
} else if (!strncmp("ra", action, 2)) {
RightArrow();
puts("ra");
} else if (!strncmp("en", action, 2)) {
Enter();
puts("en");
} else if (!strncmp("wt", action, 2)) {
// wait
} else if (!strncmp("#", action, 1)) {
// comment
} else if (!strncmp("--", action, 2)) {
// comment
} else if (!strncmp("//", action, 2)) {
// comment
} else if (!strncmp("/*", action, 2)) {
// comment
} else {
printf("Warning: unrecognized action \"%s\"\n", action);
}
}
// tries to wait 100us less than msWait specifies, just to leave some time for
// overhead
void Wait(int msWait, LARGE_INTEGER freq) {
LARGE_INTEGER startTime, endTime, tmsElapsed;
QueryPerformanceCounter(&startTime);
if (msWait > 20) {
Sleep(msWait - 20);
}
int tmsWait = 10 * msWait;
do {
QueryPerformanceCounter(&endTime);
tmsElapsed.QuadPart = endTime.QuadPart - startTime.QuadPart;
tmsElapsed.QuadPart *= 10000;
tmsElapsed.QuadPart /= freq.QuadPart;
} while (tmsElapsed.QuadPart < tmsWait - 1);
}
int main(int argc, char* argv[]) {
const char* fileName;
if (argc > 1) {
fileName = argv[1];
} else {
puts("Usage");
exit(EXIT_FAILURE);
}
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LPCSTR windowTitle = "Mirror's Edge™";
HWND window = FindWindow(NULL, windowTitle);
SetForegroundWindow(window);
WINDOWPLACEMENT windowPlacement;
windowPlacement.length = sizeof (WINDOWPLACEMENT);
GetWindowPlacement(window, &windowPlacement);
windowPlacement.showCmd = SW_RESTORE;
SetWindowPlacement(window, &windowPlacement);
Sleep(100); // Mouse movements might not work without this Sleep
FILE* fp;
char buffer[100];
int wait;
char action[2];
int dx;
int dy;
int ret;
fp = fopen(fileName, "r");
while (fgets(buffer, 100, fp)) {
if (buffer[0] == '>') {
printf("%s", buffer);
} else if (buffer[1] != '\0') {
ret = sscanf(buffer, "%d %s %d %d", &wait, action, &dx, &dy);
switch (ret) {
case 2:
Wait(wait, freq);
Action(action);
break;
case 4:
Wait(wait, freq);
SendMouseMove(dx, dy);
break;
default:
ret = sscanf(buffer, "%s %d %d", action, &dx, &dy);
switch (ret) {
case 1:
Action(action);
break;
case 3:
SendMouseMove(dx, dy);
break;
default:
// Everything that doesn't look right will be skipped
// Except for comments
if (strncmp("#", action, 1) &&
strncmp("--", action, 2) &&
strncmp("//", action, 2) &&
strncmp("/*", action, 2)) {
printf("Warning: skipped line %s", buffer);
}
continue;
}
break;
}
}
}
fclose(fp);
exit(EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment