| #include <stdexcept> | |
| #include <cmath> | |
| #include <Windows.h> | |
| #include <Psapi.h> | |
| #include <detours.h> | |
| #include <d3d9.h> | |
| #include <d3dx9.h> | |
| #define PI 3.14159F | |
| char **game_ptr; | |
| char **asw_engine; | |
| using cast_t = const void*(*)(const void*); | |
| cast_t cast_REDGameInfo_Battle; | |
| using is_active_t = bool(__thiscall*)(const void*); | |
| is_active_t is_active; | |
| void angle_vectors( | |
| const float p, const float y, const float r, | |
| float *forward, float *right, float *up) | |
| { | |
| const auto sp = sin(-p); | |
| const auto cp = cos(-p); | |
| const auto sy = sin(y); | |
| const auto cy = cos(y); | |
| const auto sr = sin(-r); | |
| const auto cr = cos(-r); | |
| if (forward != nullptr) | |
| { | |
| forward[0] = cp * cy; | |
| forward[1] = cp * sy; | |
| forward[2] = -sp; | |
| } | |
| if (right != nullptr) | |
| { | |
| right[0] = -1 * sr * sp * cy + -1 * cr * -sy; | |
| right[1] = -1 * sr * sp * sy + -1 * cr * cy; | |
| right[2] = -1 * sr * cp; | |
| } | |
| if (up != nullptr) | |
| { | |
| up[0] = cr * sp * cy + -sr * -sy; | |
| up[1] = cr * sp * sy + -sr * cy; | |
| up[2] = cr * cp; | |
| } | |
| } | |
| float vec_dot(float *a, float *b) | |
| { | |
| return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; | |
| } | |
| void world_to_screen(IDirect3DDevice9 *device, const D3DXVECTOR3 &in, D3DXVECTOR3 *out) | |
| { | |
| D3DVIEWPORT9 viewport; | |
| device->GetViewport(&viewport); | |
| const auto *game = *game_ptr; | |
| const auto *world = *(char**)(game + 0x50); | |
| const auto *world_info = **(char***)(world + 0x3C); | |
| const auto *game_info = *(char**)(world_info + 0x4A8); | |
| const auto *camera = *(char**)(game_info+0x428); | |
| D3DXVECTOR3 camera_pos; | |
| camera_pos.x = *(float*)(camera + 0x384); | |
| camera_pos.y = *(float*)(camera + 0x388); | |
| camera_pos.z = *(float*)(camera + 0x38C); | |
| D3DXVECTOR3 relative_pos; | |
| D3DXVec3Subtract(&relative_pos, &in, &camera_pos); | |
| const auto clipx = (float)(viewport.Width); | |
| const auto clipy = (float)(viewport.Height); | |
| const auto pitch = (float)(*(int*)(camera + 0x390)) / 32768.F * PI; | |
| const auto yaw = (float)(*(int*)(camera + 0x394)) / 32768.F * PI; | |
| const auto roll = (float)(*(int*)(camera + 0x398)) / 32768.F * PI; | |
| const auto fov = *(float*)(camera + 0x39C); | |
| float forward[3], right[3], up[3]; | |
| angle_vectors(pitch, yaw, roll, forward, right, up); | |
| out->x = vec_dot(relative_pos, right); | |
| out->y = vec_dot(relative_pos, up); | |
| out->z = vec_dot(relative_pos, forward); | |
| out->x = (clipx / 2.F) - out->x * ((clipx / 2.F) / tan(fov * PI / 360.F)) / out->z; | |
| out->y = (clipy / 2.F) - out->y * ((clipx / 2.F) / tan(fov * PI / 360.F)) / out->z; | |
| } | |
| void draw_rect( | |
| IDirect3DDevice9 *device, | |
| const D3DXVECTOR3 &p1, | |
| const D3DXVECTOR3 &p2, | |
| const D3DXVECTOR3 &p3, | |
| const D3DXVECTOR3 &p4, | |
| D3DCOLOR inner_color, | |
| D3DCOLOR outer_color) | |
| { | |
| struct vertex | |
| { | |
| float x, y, z, rhw; | |
| DWORD color; | |
| }; | |
| D3DXVECTOR3 sp1, sp2, sp3, sp4; | |
| world_to_screen(device, p1, &sp1); | |
| world_to_screen(device, p2, &sp2); | |
| world_to_screen(device, p3, &sp3); | |
| world_to_screen(device, p4, &sp4); | |
| device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); | |
| device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); | |
| device->SetPixelShader(nullptr); | |
| device->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE); | |
| device->SetTexture(0, nullptr); | |
| vertex vertices[] = | |
| { | |
| { sp1.x, sp1.y, 0.F, 0.F, inner_color }, | |
| { sp2.x, sp2.y, 0.F, 0.F, inner_color }, | |
| { sp3.x, sp3.y, 0.F, 0.F, inner_color }, | |
| { sp4.x, sp4.y, 0.F, 0.F, inner_color }, | |
| }; | |
| device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertices, sizeof(vertex)); | |
| vertex outline[] = | |
| { | |
| { sp1.x, sp1.y, 0.F, 0.F, outer_color }, | |
| { sp2.x, sp2.y, 0.F, 0.F, outer_color }, | |
| { sp4.x, sp4.y, 0.F, 0.F, outer_color }, | |
| { sp3.x, sp3.y, 0.F, 0.F, outer_color }, | |
| { sp1.x, sp1.y, 0.F, 0.F, outer_color }, | |
| }; | |
| device->DrawPrimitiveUP(D3DPT_LINESTRIP, 4, outline, sizeof(vertex)); | |
| } | |
| void draw_hitboxes(IDirect3DDevice9 *device, const char *asw_data, const bool active) | |
| { | |
| const auto posx = (float)(*(int*)(asw_data + 0x2B0)) / 1000.F * .43F; | |
| const auto posy = (float)(*(int*)(asw_data + 0x2B4)) / 1000.F * .43F; | |
| const auto flip = *(int*)(asw_data + 0x23C) == 1 ? 1.F : -1.F; | |
| const auto invuln_frames = *(int*)(asw_data + 0x964); | |
| const auto invuln_flags = *(char*)(asw_data + 0x230); | |
| const auto invuln = invuln_frames > 0 || (invuln_flags & 16); | |
| struct hitbox | |
| { | |
| int type; | |
| float offx; | |
| float offy; | |
| float sizex; | |
| float sizey; | |
| }; | |
| const auto *hitbox_data = *(hitbox**)(asw_data + 0x58); | |
| if (hitbox_data == nullptr) | |
| return; | |
| const auto hurtbox_count = *(int*)(asw_data + 0xA0); | |
| const auto hitbox_count = *(int*)(asw_data + 0xA4); | |
| const auto angle = (float)(*(int*)(asw_data + 0x24C)) / 1000.F; | |
| const auto ca = cos(angle * PI / 180.F); | |
| const auto sa = sin(angle * PI / 180.F); | |
| // Thanks to jedpossum on dustloop for these offsets | |
| const auto scale_x = (float)(*(int*)(asw_data + 0x258)) / 1000.F; | |
| const auto scale_y = (float)(*(int*)(asw_data + 0x25C)) / 1000.F; | |
| for (auto i = 0; i < hitbox_count + hurtbox_count; i++) | |
| { | |
| const auto type = *(int*)(hitbox_data); | |
| // Convert from Arcsys 2D engine coords to UE coords | |
| const auto box_x = (hitbox_data->offx * ca - hitbox_data->offy * sa) * scale_x * .43F; | |
| const auto box_y = (hitbox_data->offx * -sa - hitbox_data->offy * ca) * scale_y * .43F; | |
| const auto box_width = hitbox_data->sizex * scale_x * .43F; | |
| const auto box_height = hitbox_data->sizey * scale_y * .43F; | |
| const auto x1 = box_x; | |
| const auto y1 = box_y; | |
| const auto x2 = box_x - box_height * sa; | |
| const auto y2 = box_y - box_height * ca; | |
| const auto x3 = box_x + box_width * ca; | |
| const auto y3 = box_y - box_width * sa; | |
| const auto x4 = box_x + box_width * ca - box_height * sa; | |
| const auto y4 = box_y - box_width * sa - box_height * ca; | |
| const auto inner_color = | |
| type == 0 ? | |
| D3DCOLOR_ARGB(invuln ? 0 : 64, 0, 255, 0) : | |
| D3DCOLOR_ARGB(active ? 64 : 0, 255, 0, 0); | |
| const auto outer_color = | |
| type == 0 ? | |
| D3DCOLOR_ARGB(255, 0, 255, 0) : | |
| D3DCOLOR_ARGB(255, 255, 0, 0); | |
| draw_rect( | |
| device, | |
| D3DXVECTOR3(posx + x1 * flip, 0.F, posy + y1), | |
| D3DXVECTOR3(posx + x2 * flip, 0.F, posy + y2), | |
| D3DXVECTOR3(posx + x3 * flip, 0.F, posy + y3), | |
| D3DXVECTOR3(posx + x4 * flip, 0.F, posy + y4), | |
| inner_color, | |
| outer_color); | |
| hitbox_data++; | |
| } | |
| } | |
| using EndScene_t = HRESULT(__stdcall*)(IDirect3DDevice9*); | |
| EndScene_t orig_EndScene; | |
| HRESULT __stdcall hook_EndScene(IDirect3DDevice9 *device) | |
| { | |
| if (*game_ptr == nullptr) | |
| return orig_EndScene(device); | |
| const auto *world = *(char**)(*game_ptr + 0x50); | |
| if (world == nullptr) | |
| return orig_EndScene(device); | |
| const auto *world_info = **(char***)(world + 0x3C); | |
| if (world_info == nullptr) | |
| return orig_EndScene(device); | |
| const auto *game_info = *(char**)(world_info + 0x4A8); | |
| if (game_info == nullptr) | |
| return orig_EndScene(device); | |
| // Make sure it's a REDGameInfo_Battle | |
| if (cast_REDGameInfo_Battle(game_info) == nullptr) | |
| return orig_EndScene(device); | |
| if (*asw_engine == nullptr) | |
| return orig_EndScene(device); | |
| const auto ent_count = *(int*)(*asw_engine + 0xB0); | |
| const auto ent_list = (char**)(*asw_engine + 0x70C); | |
| for (auto i = 0; i < ent_count; i++) | |
| { | |
| const auto ent = ent_list[i]; | |
| const auto active = is_active(ent); | |
| draw_hitboxes(device, ent, active); | |
| // Attached entities like dusts | |
| const auto attached = *(char**)(ent + 0x200); | |
| if (attached != nullptr) | |
| draw_hitboxes(device, attached, active); | |
| } | |
| return orig_EndScene(device); | |
| } | |
| bool get_module_bounds(const char *name, uintptr_t *start, uintptr_t *end) | |
| { | |
| const auto module = GetModuleHandle(name); | |
| if(module == nullptr) | |
| return false; | |
| MODULEINFO info; | |
| GetModuleInformation(GetCurrentProcess(), module, &info, sizeof(info)); | |
| *start = (uintptr_t)(info.lpBaseOfDll); | |
| *end = *start + info.SizeOfImage; | |
| return true; | |
| } | |
| uintptr_t sigscan(const char *name, const char *sig, const char *mask) | |
| { | |
| uintptr_t start, end; | |
| if (!get_module_bounds(name, &start, &end)) | |
| throw std::runtime_error("Module not loaded"); | |
| const auto last_scan = end - strlen(mask) + 1; | |
| for (auto addr = start; addr < last_scan; addr++) { | |
| for (size_t i = 0;; i++) { | |
| if (mask[i] == '\0') | |
| return addr; | |
| if (mask[i] != '?' && sig[i] != *(char*)(addr + i)) | |
| break; | |
| } | |
| } | |
| throw std::runtime_error("Sigscan failed"); | |
| } | |
| BOOL WINAPI DllMain( | |
| _In_ HINSTANCE hinstDLL, | |
| _In_ DWORD fdwReason, | |
| _In_ LPVOID lpvReserved | |
| ) | |
| { | |
| if (fdwReason != DLL_PROCESS_ATTACH) | |
| return FALSE; | |
| game_ptr = *(char***)(sigscan( | |
| "GuiltyGearXrd.exe", | |
| "\x8B\x0D\x00\x00\x00\x00\x89\x5C\x24\x20\xE8", | |
| "xx????xxxxx") + 0x2); | |
| is_active = (is_active_t)(sigscan( | |
| "GuiltyGearXrd.exe", | |
| "\xA8\x03\x75\x28\xF7\x86", | |
| "xxxxxx") - 0x14); | |
| asw_engine = *(char***)(sigscan( | |
| "GuiltyGearXrd.exe", | |
| "\x85\xC0\x78\x74\x83\xF8\x01", | |
| "xxxxxxx") - 4); | |
| const auto cast_ref = sigscan( | |
| "GuiltyGearXrd.exe", | |
| "\x8B\x88\x00\x00\x00\x00\x51\xC7\x44\x24\x00\x00\x00\x00\x00\xE8", | |
| "xx????xxxx?????x") + 0xF; | |
| cast_REDGameInfo_Battle = (cast_t)(cast_ref + *(intptr_t*)(cast_ref + 1) + 5); | |
| const auto *dev_vtable = *(void***)(sigscan( | |
| "d3d9.dll", | |
| "\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", | |
| "xx????xx????xx") + 0x2); | |
| orig_EndScene = (EndScene_t)(DetourFunction( | |
| (byte*)(dev_vtable[42]), | |
| (byte*)(hook_EndScene))); | |
| return TRUE; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment