/
win32_tsa.c
140 lines (132 loc) · 4.23 KB
/
win32_tsa.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* Touhou Community Reliant Automatic Patcher
* Team Shanghai Alice support plugin
*
* ----
*
* Touhou-specific wrappers around Win32 API functions.
*/
#include <thcrap.h>
#include "thcrap_tsa.h"
/// Window position hacks
/// ---------------------
/**
* Recent games call GetWindowRect() before closing to store the current
* position of the game window in their configuration file. The next time the
* game is started, this position is used as the parameter for CreateWindowEx().
*
* Now, if the game is closed while being minimized, GetWindowRect() reports
* bogus values (left and top = -32000). With these, CreateWindowEx() will
* spawn the window at some unreachable "pseudo-minimized" position.
* (Yeah, maybe it *is* possible to get it back *somehow*, but that way is
* certainly not user-friendly.)
*
* The best solution seems to be replacing GetWindowRect() with
* GetWindowPlacement(), which always reports the expected window coordinates,
* even when the window in question is minimized.
*/
BOOL WINAPI tsa_GetWindowRect(
__in HWND hWnd,
__out LPRECT lpRect
)
{
BOOL ret;
WINDOWPLACEMENT wp;
if(!lpRect) {
return FALSE;
}
ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
wp.length = sizeof(WINDOWPLACEMENT);
ret = GetWindowPlacement(hWnd, &wp);
memcpy(lpRect, &wp.rcNormalPosition, sizeof(RECT));
return ret;
}
/**
* A similar issue like the one with GetWindowRect() occurs when using
* multiple monitors. When the game window is moved to a secondary monitor,
* it will also spawn there on new sessions...
*
* ... even if the monitor in question is disconnected and the screen space is
* not available anymore, thus leaving the window in an unreachable position
* as well. (On Windows 7, you *can* get it back by hovering over its field in
* the taskbar, right-clicking on the thumbnail and selecting "Move".)
*
* The Win32 API provides the current bounding rectangle of the entire screen
* space through GetSystemMetrics(). We check the coordinates against that,
* and make sure that the window position comes as close as possible to what
* is actually requested.
*/
// Ensures the visibility of an imaginary line with length [w1] starting
// at [p1] inside a larger line with length [w2] starting at [p2].
// ... oh well, it's just a helper for tsa_CreateWindowExA().
int coord_clamp(int p1, int w1, int p2, int w2)
{
int e2 = p2 + w2;
if((p1 + w1) < p2) {
p1 = p2;
} else if(p1 > e2) {
p1 = e2 - w1;
}
return p1;
}
HWND WINAPI tsa_CreateWindowExA(
__in DWORD dwExStyle,
__in_opt LPCSTR lpClassName,
__in_opt LPCSTR lpWindowName,
__in DWORD dwStyle,
__in int X,
__in int Y,
__in int nWidth,
__in int nHeight,
__in_opt HWND hWndParent,
__in_opt HMENU hMenu,
__in_opt HINSTANCE hInstance,
__in_opt LPVOID lpParam
)
{
HWND ret;
const json_t *run_cfg = runconfig_get();
const char *game_id = json_object_get_string(run_cfg, "game");
const json_t *game_trans = strings_get(game_id);
const json_t *game_title =
game_trans ? game_trans : json_object_get(run_cfg, "title");
const json_t *game_build = json_object_get(run_cfg, "build");
size_t custom_title_len =
json_string_length(game_title) + 1 + json_string_length(game_build) + 1;
VLA(char, custom_title, custom_title_len);
const char *window_title = NULL;
if(X != CW_USEDEFAULT) {
X = coord_clamp(
X, nWidth, GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN)
);
}
if(Y != CW_USEDEFAULT) {
Y = coord_clamp(
Y, nHeight, GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN)
);
}
window_title = strings_lookup(lpWindowName, NULL);
if(window_title == lpWindowName && json_is_string(game_title)) {
sprintf(custom_title, "%s %s",
json_string_value(game_title),
json_is_string(game_build) ? json_string_value(game_build) : "");
window_title = custom_title;
}
ret = (HWND)detour_next(
"user32.dll", "CreateWindowExA", tsa_CreateWindowExA, 12,
dwExStyle, lpClassName, window_title, dwStyle, X, Y,
nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam
);
VLA_FREE(custom_title);
return ret;
}
/// ---------------------
void tsa_mod_detour(void)
{
detour_cache_add("user32.dll", 2,
"CreateWindowExA", tsa_CreateWindowExA,
"GetWindowRect", tsa_GetWindowRect
);
}