Add external borderless helper
Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
@@ -21,6 +21,10 @@ add_library(version SHARED
|
||||
deps/minhook/src/hde/hde64.c
|
||||
)
|
||||
|
||||
add_executable(cgss-borderless-helper WIN32
|
||||
src/helper_main.cpp
|
||||
)
|
||||
|
||||
target_include_directories(version PRIVATE
|
||||
src
|
||||
deps/minhook/include
|
||||
@@ -29,6 +33,10 @@ target_include_directories(version PRIVATE
|
||||
deps/rapidjson/include
|
||||
)
|
||||
|
||||
target_include_directories(cgss-borderless-helper PRIVATE
|
||||
src
|
||||
)
|
||||
|
||||
target_compile_definitions(version PRIVATE
|
||||
NOMINMAX
|
||||
WIN32_LEAN_AND_MEAN
|
||||
@@ -39,18 +47,36 @@ target_link_libraries(version PRIVATE
|
||||
kernel32
|
||||
)
|
||||
|
||||
target_link_libraries(cgss-borderless-helper PRIVATE
|
||||
user32
|
||||
kernel32
|
||||
shell32
|
||||
)
|
||||
|
||||
set_target_properties(version PROPERTIES
|
||||
OUTPUT_NAME "version"
|
||||
PREFIX ""
|
||||
)
|
||||
|
||||
set_target_properties(cgss-borderless-helper PROPERTIES
|
||||
OUTPUT_NAME "cgss-borderless-helper"
|
||||
)
|
||||
|
||||
if(MINGW)
|
||||
target_compile_options(version PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>"
|
||||
)
|
||||
target_compile_options(cgss-borderless-helper PRIVATE
|
||||
-fno-exceptions
|
||||
-fno-rtti
|
||||
)
|
||||
target_link_options(version PRIVATE
|
||||
"-static-libgcc"
|
||||
"-static-libstdc++"
|
||||
)
|
||||
target_link_options(cgss-borderless-helper PRIVATE
|
||||
"-static-libgcc"
|
||||
"-static-libstdc++"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
TARGET := build/version.dll
|
||||
HELPER_TARGET := build/cgss-borderless-helper.exe
|
||||
BUILD_DIR := build
|
||||
OBJ_DIR := $(BUILD_DIR)/obj
|
||||
BUILD ?= release
|
||||
@@ -14,6 +15,8 @@ CFLAGS := -std=gnu11 $(DEFINES) $(INCLUDES)
|
||||
CXXFLAGS := -std=gnu++20 -fno-exceptions -fno-rtti $(DEFINES) $(INCLUDES)
|
||||
LDFLAGS := -shared -static-libgcc -static-libstdc++
|
||||
LDLIBS := -luser32 -lkernel32
|
||||
HELPER_LDFLAGS := -static-libgcc -static-libstdc++ -mwindows
|
||||
HELPER_LDLIBS := -luser32 -lkernel32 -lshell32
|
||||
|
||||
ifeq ($(BUILD),debug)
|
||||
DEFINES += -D_DEBUG
|
||||
@@ -38,18 +41,22 @@ CXX_SRCS := \
|
||||
src/config.cpp \
|
||||
src/il2cpp_symbols.cpp
|
||||
|
||||
HELPER_CXX_SRCS := \
|
||||
src/helper_main.cpp
|
||||
|
||||
RC_SRCS := src/version.rc
|
||||
DEF_FILE := src/version.def
|
||||
|
||||
C_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(C_SRCS))
|
||||
CXX_OBJS := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(CXX_SRCS))
|
||||
HELPER_CXX_OBJS := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(HELPER_CXX_SRCS))
|
||||
RC_OBJS := $(patsubst %.rc,$(OBJ_DIR)/%.res,$(RC_SRCS))
|
||||
OBJS := $(C_OBJS) $(CXX_OBJS) $(RC_OBJS)
|
||||
|
||||
.PHONY: all release debug clean
|
||||
|
||||
all: $(TARGET)
|
||||
release: $(TARGET)
|
||||
all: $(TARGET) $(HELPER_TARGET)
|
||||
release: $(TARGET) $(HELPER_TARGET)
|
||||
debug:
|
||||
$(MAKE) BUILD=debug
|
||||
|
||||
@@ -57,6 +64,10 @@ $(TARGET): $(OBJS) $(DEF_FILE)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(DEF_FILE) $(LDLIBS)
|
||||
|
||||
$(HELPER_TARGET): $(HELPER_CXX_OBJS)
|
||||
@mkdir -p $(dir $@)
|
||||
$(CXX) $(HELPER_LDFLAGS) -o $@ $(HELPER_CXX_OBJS) $(HELPER_LDLIBS)
|
||||
|
||||
$(OBJ_DIR)/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
- 等待 IL2CPP 运行时初始化稳定
|
||||
- 可选地 hook `Stage.NetworkUtil.GetSchemeType()`,在需要时强制切到 `http`
|
||||
- 支持通过 `config.json` 覆盖 `api_url` 和 `asset_url`
|
||||
- 自动拉起同目录下的 `cgss-borderless-helper.exe`,用外部 helper 处理 `F11` 无边框全屏
|
||||
|
||||
**目录结构**
|
||||
- `src/`:代理、日志、IL2CPP 符号解析、hook 逻辑
|
||||
@@ -28,15 +29,22 @@ cmake -S . -B build -G Ninja -DCMAKE_SYSTEM_NAME=Windows \
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
或直接使用:
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
**输出**
|
||||
- `build/version.dll`
|
||||
- `build/cgss-borderless-helper.exe`
|
||||
|
||||
**config.json**
|
||||
```json
|
||||
{
|
||||
"force_http": true,
|
||||
"api_url": "apis.game.starlight-stage.jp/",
|
||||
"asset_url": "asset-starlight-stage.akamaized.net/"
|
||||
"asset_url": "asset-starlight-stage.akamaized.net/",
|
||||
"launch_borderless_helper": true
|
||||
}
|
||||
```
|
||||
|
||||
@@ -44,12 +52,20 @@ cmake --build build
|
||||
- `force_http`:是否强制将 scheme 改成 `http`。默认 `true`
|
||||
- `api_url`:API 主机名,按原始字段格式填写,不带 scheme
|
||||
- `asset_url`:资源主机名,按原始字段格式填写,不带 scheme
|
||||
- `launch_borderless_helper`:是否自动启动 `cgss-borderless-helper.exe`。默认 `true`
|
||||
|
||||
**使用方法**
|
||||
1. 将 `build/version.dll` 复制到 `imascgstage.exe` 同目录
|
||||
1. 将 `build/version.dll` 和 `build/cgss-borderless-helper.exe` 一起复制到 `imascgstage.exe` 同目录
|
||||
2. 如需覆盖地址,在同目录放置 `config.json`
|
||||
3. 启动游戏
|
||||
4. 如果 hook 未生效,查看 `cgss-dmm-hook.log`
|
||||
4. 进入游戏后按 `F11` 切换无边框全屏
|
||||
5. 如果 hook 未生效,查看 `cgss-dmm-hook.log` 和 `cgss-borderless-helper.log`
|
||||
|
||||
说明:
|
||||
- `cgss-borderless-helper.exe` 现在只负责外部窗口 borderless 行为,不再修改 Unity 内部 `Screen.SetResolution`。
|
||||
- 这样更接近 Borderless Gaming 的工作方式,也能减少退出时的干扰。
|
||||
- `version.dll` 只在 `imascgstage.exe` 主进程中继续启动 hook 和 helper;被其他进程间接加载时会跳过。
|
||||
|
||||
**致谢**
|
||||
- 本项目的 `version.dll` 代理注入思路与运行时 IL2CPP hook 方向,受 `gkms-localify-dmm` 项目启发。
|
||||
- `cgss-borderless-helper.exe` 的外部无边框全屏处理思路,参考了 Borderless Gaming 项目;本项目同样按 GPL 开源。
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"force_http": true,
|
||||
"api_url": "apis.game.starlight-stage.jp/",
|
||||
"asset_url": "asset-starlight-stage.akamaized.net/"
|
||||
"asset_url": "asset-starlight-stage.akamaized.net/",
|
||||
"launch_borderless_helper": true
|
||||
}
|
||||
|
||||
+7
-1
@@ -59,7 +59,8 @@ namespace config {
|
||||
"{\r\n"
|
||||
" \"force_http\": false,\r\n"
|
||||
" \"api_url\": \"apis.game.starlight-stage.jp/\",\r\n"
|
||||
" \"asset_url\": \"asset-starlight-stage.akamaized.net/\"\r\n"
|
||||
" \"asset_url\": \"asset-starlight-stage.akamaized.net/\",\r\n"
|
||||
" \"launch_borderless_helper\": true\r\n"
|
||||
"}\r\n";
|
||||
}
|
||||
|
||||
@@ -114,8 +115,13 @@ namespace config {
|
||||
read_bool(document, "force_http", g_urls.force_http);
|
||||
read_string(document, "api_url", g_urls.api_url);
|
||||
read_string(document, "asset_url", g_urls.asset_url);
|
||||
read_bool(document, "launch_borderless_helper", g_urls.launch_borderless_helper);
|
||||
|
||||
hook_logf("[cgss-dmm-hook] force_http=%s", g_urls.force_http ? "true" : "false");
|
||||
hook_logf(
|
||||
"[cgss-dmm-hook] launch_borderless_helper=%s",
|
||||
g_urls.launch_borderless_helper ? "true" : "false"
|
||||
);
|
||||
|
||||
if (!g_urls.api_url.empty()) {
|
||||
hook_logf("[cgss-dmm-hook] normalized api_url=%s", g_urls.api_url.c_str());
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace config {
|
||||
bool force_http = true;
|
||||
std::string api_url;
|
||||
std::string asset_url;
|
||||
bool launch_borderless_helper = true;
|
||||
};
|
||||
|
||||
void load();
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
constexpr DWORD kWindowPollIntervalMs = 500;
|
||||
constexpr int kWindowPollMaxAttempts = 240;
|
||||
constexpr DWORD kHotkeyPollIntervalMs = 50;
|
||||
constexpr DWORD kMaintainIntervalMs = 500;
|
||||
constexpr DWORD kDelayedStyleApplyMs = 4;
|
||||
constexpr int kToggleKey = VK_F11;
|
||||
|
||||
HWND g_main_window = nullptr;
|
||||
bool g_borderless_active = false;
|
||||
bool g_saved_state_valid = false;
|
||||
RECT g_saved_window_rect = {};
|
||||
LONG_PTR g_saved_style = 0;
|
||||
LONG_PTR g_saved_exstyle = 0;
|
||||
DWORD g_target_process_id = 0;
|
||||
|
||||
std::wstring get_window_class_name(HWND hwnd) {
|
||||
wchar_t buffer[256] = {};
|
||||
int len = GetClassNameW(hwnd, buffer, static_cast<int>(std::size(buffer)));
|
||||
if (len <= 0) {
|
||||
return {};
|
||||
}
|
||||
return std::wstring(buffer, buffer + len);
|
||||
}
|
||||
|
||||
void helper_logf(const char* format, ...) {
|
||||
char exe_path[MAX_PATH] = {};
|
||||
DWORD len = GetModuleFileNameA(nullptr, exe_path, MAX_PATH);
|
||||
if (len == 0 || len >= MAX_PATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* last_sep = exe_path;
|
||||
for (char* p = exe_path; *p != '\0'; ++p) {
|
||||
if (*p == '\\' || *p == '/') {
|
||||
last_sep = p;
|
||||
}
|
||||
}
|
||||
*last_sep = '\0';
|
||||
lstrcatA(exe_path, "\\cgss-borderless-helper.log");
|
||||
|
||||
char message[1024] = {};
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_vsnprintf_s(message, sizeof(message), _TRUNCATE, format, args);
|
||||
va_end(args);
|
||||
|
||||
std::ofstream stream(exe_path, std::ios::app | std::ios::binary);
|
||||
if (!stream.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SYSTEMTIME st = {};
|
||||
GetLocalTime(&st);
|
||||
char line[1200] = {};
|
||||
_snprintf_s(
|
||||
line,
|
||||
sizeof(line),
|
||||
_TRUNCATE,
|
||||
"[%04u-%02u-%02u %02u:%02u:%02u] %s\r\n",
|
||||
static_cast<unsigned>(st.wYear),
|
||||
static_cast<unsigned>(st.wMonth),
|
||||
static_cast<unsigned>(st.wDay),
|
||||
static_cast<unsigned>(st.wHour),
|
||||
static_cast<unsigned>(st.wMinute),
|
||||
static_cast<unsigned>(st.wSecond),
|
||||
message
|
||||
);
|
||||
stream << line;
|
||||
}
|
||||
|
||||
void set_dpi_awareness() {
|
||||
using SetProcessDpiAwarenessContextFn = BOOL(WINAPI*)(HANDLE);
|
||||
auto user32 = GetModuleHandleW(L"user32.dll");
|
||||
if (!user32) {
|
||||
user32 = LoadLibraryW(L"user32.dll");
|
||||
}
|
||||
|
||||
if (user32) {
|
||||
auto set_context = reinterpret_cast<SetProcessDpiAwarenessContextFn>(
|
||||
GetProcAddress(user32, "SetProcessDpiAwarenessContext")
|
||||
);
|
||||
if (set_context && set_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
|
||||
helper_logf("[cgss-borderless-helper] enabled per-monitor-v2 DPI awareness");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetProcessDPIAware();
|
||||
helper_logf("[cgss-borderless-helper] enabled legacy DPI awareness");
|
||||
}
|
||||
|
||||
bool parse_arguments() {
|
||||
LPWSTR* argv = nullptr;
|
||||
int argc = 0;
|
||||
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
if (!argv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
for (int i = 1; i + 1 < argc; ++i) {
|
||||
if (lstrcmpiW(argv[i], L"--pid") == 0) {
|
||||
g_target_process_id = wcstoul(argv[i + 1], nullptr, 10);
|
||||
ok = g_target_process_id != 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LocalFree(argv);
|
||||
return ok;
|
||||
}
|
||||
|
||||
struct WindowSearchContext {
|
||||
DWORD process_id = 0;
|
||||
HWND hwnd = nullptr;
|
||||
};
|
||||
|
||||
BOOL CALLBACK enum_windows_proc(HWND hwnd, LPARAM lparam) {
|
||||
auto* context = reinterpret_cast<WindowSearchContext*>(lparam);
|
||||
if (!context) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD process_id = 0;
|
||||
GetWindowThreadProcessId(hwnd, &process_id);
|
||||
if (process_id != context->process_id) {
|
||||
return TRUE;
|
||||
}
|
||||
if (GetWindow(hwnd, GW_OWNER) != nullptr) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
|
||||
if ((style & WS_CHILD) != 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
context->hwnd = hwnd;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HWND find_main_window(DWORD process_id) {
|
||||
WindowSearchContext context;
|
||||
context.process_id = process_id;
|
||||
EnumWindows(&enum_windows_proc, reinterpret_cast<LPARAM>(&context));
|
||||
return context.hwnd;
|
||||
}
|
||||
|
||||
bool get_monitor_rect(HWND hwnd, RECT& rect, MONITORINFOEXW* out_monitor_info = nullptr) {
|
||||
MONITORINFOEXW monitor_info = {};
|
||||
monitor_info.cbSize = sizeof(monitor_info);
|
||||
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
if (!monitor || !GetMonitorInfoW(monitor, &monitor_info)) {
|
||||
return false;
|
||||
}
|
||||
rect = monitor_info.rcMonitor;
|
||||
if (out_monitor_info) {
|
||||
*out_monitor_info = monitor_info;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void save_window_state(HWND hwnd) {
|
||||
if (g_saved_state_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
GetWindowRect(hwnd, &g_saved_window_rect);
|
||||
g_saved_style = GetWindowLongPtrW(hwnd, GWL_STYLE);
|
||||
g_saved_exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
|
||||
g_saved_state_valid = true;
|
||||
helper_logf(
|
||||
"[cgss-borderless-helper] saved window rect=(%ld,%ld)-(%ld,%ld)",
|
||||
static_cast<long>(g_saved_window_rect.left),
|
||||
static_cast<long>(g_saved_window_rect.top),
|
||||
static_cast<long>(g_saved_window_rect.right),
|
||||
static_cast<long>(g_saved_window_rect.bottom)
|
||||
);
|
||||
}
|
||||
|
||||
void apply_borderless_once(HWND hwnd) {
|
||||
RECT monitor_rect = {};
|
||||
MONITORINFOEXW monitor_info = {};
|
||||
if (!get_monitor_rect(hwnd, monitor_rect, &monitor_info)) {
|
||||
helper_logf("[cgss-borderless-helper] failed to query monitor rect");
|
||||
return;
|
||||
}
|
||||
|
||||
save_window_state(hwnd);
|
||||
|
||||
LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
|
||||
LONG_PTR exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
|
||||
LONG_PTR new_style = style & ~(WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
|
||||
LONG_PTR new_exstyle = exstyle &
|
||||
~(WS_EX_DLGMODALFRAME | WS_EX_COMPOSITED | WS_EX_WINDOWEDGE |
|
||||
WS_EX_CLIENTEDGE | WS_EX_LAYERED | WS_EX_STATICEDGE |
|
||||
WS_EX_TOOLWINDOW | WS_EX_APPWINDOW);
|
||||
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
nullptr,
|
||||
monitor_rect.left,
|
||||
monitor_rect.top,
|
||||
monitor_rect.right - monitor_rect.left,
|
||||
monitor_rect.bottom - monitor_rect.top,
|
||||
SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING
|
||||
);
|
||||
|
||||
Sleep(kDelayedStyleApplyMs);
|
||||
SetWindowLongPtrW(hwnd, GWL_STYLE, new_style);
|
||||
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, new_exstyle);
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
nullptr,
|
||||
monitor_rect.left,
|
||||
monitor_rect.top,
|
||||
monitor_rect.right - monitor_rect.left,
|
||||
monitor_rect.bottom - monitor_rect.top,
|
||||
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING
|
||||
);
|
||||
|
||||
RECT actual_rect = {};
|
||||
GetWindowRect(hwnd, &actual_rect);
|
||||
helper_logf(
|
||||
"[cgss-borderless-helper] applied borderless class=%ls monitor=%ls target=(%ld,%ld)-(%ld,%ld) actual=(%ld,%ld)-(%ld,%ld)",
|
||||
get_window_class_name(hwnd).c_str(),
|
||||
monitor_info.szDevice,
|
||||
static_cast<long>(monitor_rect.left),
|
||||
static_cast<long>(monitor_rect.top),
|
||||
static_cast<long>(monitor_rect.right),
|
||||
static_cast<long>(monitor_rect.bottom),
|
||||
static_cast<long>(actual_rect.left),
|
||||
static_cast<long>(actual_rect.top),
|
||||
static_cast<long>(actual_rect.right),
|
||||
static_cast<long>(actual_rect.bottom)
|
||||
);
|
||||
}
|
||||
|
||||
bool needs_borderless_reapply(HWND hwnd) {
|
||||
RECT expected_rect = {};
|
||||
if (!get_monitor_rect(hwnd, expected_rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT current_rect = {};
|
||||
if (!GetWindowRect(hwnd, ¤t_rect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LONG_PTR style = GetWindowLongPtrW(hwnd, GWL_STYLE);
|
||||
const bool has_border = (style & (WS_CAPTION | WS_THICKFRAME | WS_SYSMENU)) != 0;
|
||||
const bool rect_mismatch = current_rect.left != expected_rect.left ||
|
||||
current_rect.top != expected_rect.top ||
|
||||
current_rect.right != expected_rect.right ||
|
||||
current_rect.bottom != expected_rect.bottom;
|
||||
return has_border || rect_mismatch;
|
||||
}
|
||||
|
||||
void restore_window(HWND hwnd) {
|
||||
if (!g_saved_state_valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetWindowLongPtrW(hwnd, GWL_STYLE, g_saved_style);
|
||||
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, g_saved_exstyle);
|
||||
SetWindowPos(
|
||||
hwnd,
|
||||
nullptr,
|
||||
g_saved_window_rect.left,
|
||||
g_saved_window_rect.top,
|
||||
g_saved_window_rect.right - g_saved_window_rect.left,
|
||||
g_saved_window_rect.bottom - g_saved_window_rect.top,
|
||||
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING
|
||||
);
|
||||
helper_logf("[cgss-borderless-helper] restored original window style and rect");
|
||||
}
|
||||
|
||||
void toggle_borderless(HWND hwnd) {
|
||||
if (!hwnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
RECT monitor_rect = {};
|
||||
MONITORINFOEXW monitor_info = {};
|
||||
if (!get_monitor_rect(hwnd, monitor_rect, &monitor_info)) {
|
||||
helper_logf("[cgss-borderless-helper] failed to resolve monitor rect for toggle");
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_borderless_active) {
|
||||
g_borderless_active = false;
|
||||
restore_window(hwnd);
|
||||
return;
|
||||
}
|
||||
|
||||
g_borderless_active = true;
|
||||
helper_logf(
|
||||
"[cgss-borderless-helper] borderless enabled monitor=%ls rect=(%ld,%ld)-(%ld,%ld)",
|
||||
monitor_info.szDevice,
|
||||
static_cast<long>(monitor_rect.left),
|
||||
static_cast<long>(monitor_rect.top),
|
||||
static_cast<long>(monitor_rect.right),
|
||||
static_cast<long>(monitor_rect.bottom)
|
||||
);
|
||||
apply_borderless_once(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
|
||||
set_dpi_awareness();
|
||||
|
||||
if (!parse_arguments()) {
|
||||
helper_logf("[cgss-borderless-helper] missing or invalid --pid argument");
|
||||
return 1;
|
||||
}
|
||||
|
||||
HANDLE target_process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, g_target_process_id);
|
||||
if (!target_process) {
|
||||
helper_logf("[cgss-borderless-helper] failed to open target process");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < kWindowPollMaxAttempts; ++i) {
|
||||
g_main_window = find_main_window(g_target_process_id);
|
||||
if (g_main_window) {
|
||||
helper_logf("[cgss-borderless-helper] found main window hwnd=0x%p", g_main_window);
|
||||
break;
|
||||
}
|
||||
if (WaitForSingleObject(target_process, 0) == WAIT_OBJECT_0) {
|
||||
CloseHandle(target_process);
|
||||
helper_logf("[cgss-borderless-helper] target process exited before main window appeared");
|
||||
return 0;
|
||||
}
|
||||
Sleep(kWindowPollIntervalMs);
|
||||
}
|
||||
|
||||
if (!g_main_window) {
|
||||
CloseHandle(target_process);
|
||||
helper_logf("[cgss-borderless-helper] timed out waiting for main window");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SHORT previous_hotkey_state = 0;
|
||||
DWORD last_maintain_tick = GetTickCount();
|
||||
|
||||
while (WaitForSingleObject(target_process, 0) != WAIT_OBJECT_0) {
|
||||
if (!IsWindow(g_main_window)) {
|
||||
helper_logf("[cgss-borderless-helper] target window no longer valid");
|
||||
break;
|
||||
}
|
||||
|
||||
SHORT current_hotkey_state = GetAsyncKeyState(kToggleKey);
|
||||
bool is_pressed = (current_hotkey_state & 0x8000) != 0;
|
||||
bool was_pressed = (previous_hotkey_state & 0x8000) != 0;
|
||||
if (is_pressed && !was_pressed) {
|
||||
helper_logf("[cgss-borderless-helper] F11 pressed");
|
||||
toggle_borderless(g_main_window);
|
||||
}
|
||||
previous_hotkey_state = current_hotkey_state;
|
||||
|
||||
DWORD now = GetTickCount();
|
||||
if (g_borderless_active && now - last_maintain_tick >= kMaintainIntervalMs) {
|
||||
if (needs_borderless_reapply(g_main_window)) {
|
||||
helper_logf("[cgss-borderless-helper] detected borderless drift, reapplying");
|
||||
apply_borderless_once(g_main_window);
|
||||
}
|
||||
last_maintain_tick = now;
|
||||
}
|
||||
|
||||
Sleep(kHotkeyPollIntervalMs);
|
||||
}
|
||||
|
||||
CloseHandle(target_process);
|
||||
helper_logf("[cgss-borderless-helper] target process exited, helper shutting down");
|
||||
return 0;
|
||||
}
|
||||
+67
-155
@@ -1,6 +1,7 @@
|
||||
#include "stdinclude.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "hook.hpp"
|
||||
|
||||
namespace {
|
||||
using GetSchemeTypeFn = int (*)(const MethodInfo*);
|
||||
@@ -400,187 +401,98 @@ namespace {
|
||||
hook_log("[cgss-dmm-hook] force_http disabled, keeping original scheme");
|
||||
}
|
||||
|
||||
if (get_application_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(get_application_server_url_addr),
|
||||
auto hook_method = [](uintptr_t address, void* detour, void** original, const char* name) {
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
auto create_status = MH_CreateHook(reinterpret_cast<void*>(address), detour, original);
|
||||
if (create_status != MH_OK && create_status != MH_ERROR_ALREADY_CREATED) {
|
||||
return;
|
||||
}
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(address));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_logf("[cgss-dmm-hook] hooked %s", name);
|
||||
}
|
||||
};
|
||||
|
||||
hook_method(
|
||||
get_application_server_url_addr,
|
||||
reinterpret_cast<void*>(&get_application_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_get_application_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_get_application_server_url),
|
||||
"Stage.NetworkUtil.GetApplicationServerUrl"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(get_application_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Stage.NetworkUtil.GetApplicationServerUrl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_resource_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(get_resource_server_url_addr),
|
||||
hook_method(
|
||||
get_resource_server_url_addr,
|
||||
reinterpret_cast<void*>(&get_resource_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_get_resource_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_get_resource_server_url),
|
||||
"Stage.NetworkUtil.GetResourceServerUrl"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(get_resource_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Stage.NetworkUtil.GetResourceServerUrl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_tele_scope_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(get_tele_scope_server_url_addr),
|
||||
hook_method(
|
||||
get_tele_scope_server_url_addr,
|
||||
reinterpret_cast<void*>(&get_tele_scope_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_get_tele_scope_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_get_tele_scope_server_url),
|
||||
"Stage.NetworkUtil.GetTeleScopeServerUrl"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(get_tele_scope_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Stage.NetworkUtil.GetTeleScopeServerUrl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (get_concert_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(get_concert_server_url_addr),
|
||||
hook_method(
|
||||
get_concert_server_url_addr,
|
||||
reinterpret_cast<void*>(&get_concert_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_get_concert_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_get_concert_server_url),
|
||||
"Stage.NetworkUtil.GetConcertServerUrl"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(get_concert_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Stage.NetworkUtil.GetConcertServerUrl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_get_application_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_get_application_server_url_addr),
|
||||
hook_method(
|
||||
cp_get_application_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_get_application_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_application_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_application_server_url),
|
||||
"Cute.CustomPreference.GetApplicationServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_get_application_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.GetApplicationServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_get_tele_scope_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_get_tele_scope_server_url_addr),
|
||||
hook_method(
|
||||
cp_get_tele_scope_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_get_tele_scope_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_tele_scope_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_tele_scope_server_url),
|
||||
"Cute.CustomPreference.GetTeleScopeServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_get_tele_scope_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.GetTeleScopeServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_get_concert_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_get_concert_server_url_addr),
|
||||
hook_method(
|
||||
cp_get_concert_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_get_concert_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_concert_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_concert_server_url),
|
||||
"Cute.CustomPreference.GetConcertServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_get_concert_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.GetConcertServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_get_resource_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_get_resource_server_url_addr),
|
||||
hook_method(
|
||||
cp_get_resource_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_get_resource_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_resource_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_get_resource_server_url),
|
||||
"Cute.CustomPreference.GetResourceServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_get_resource_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.GetResourceServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_set_application_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_set_application_server_url_addr),
|
||||
hook_method(
|
||||
cp_set_application_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_set_application_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_application_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_application_server_url),
|
||||
"Cute.CustomPreference.SetApplicationServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_set_application_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.SetApplicationServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_set_tele_scope_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_set_tele_scope_server_url_addr),
|
||||
hook_method(
|
||||
cp_set_tele_scope_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_set_tele_scope_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_tele_scope_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_tele_scope_server_url),
|
||||
"Cute.CustomPreference.SetTeleScopeServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_set_tele_scope_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.SetTeleScopeServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_set_concert_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_set_concert_server_url_addr),
|
||||
hook_method(
|
||||
cp_set_concert_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_set_concert_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_concert_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_concert_server_url),
|
||||
"Cute.CustomPreference.SetConcertServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_set_concert_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.SetConcertServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_set_resource_server_url_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_set_resource_server_url_addr),
|
||||
hook_method(
|
||||
cp_set_resource_server_url_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_set_resource_server_url_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_resource_server_url)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_resource_server_url),
|
||||
"Cute.CustomPreference.SetResourceServerURL"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_set_resource_server_url_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.SetResourceServerURL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cp_set_scheme_mode_addr) {
|
||||
auto create_status = MH_CreateHook(
|
||||
reinterpret_cast<void*>(cp_set_scheme_mode_addr),
|
||||
hook_method(
|
||||
cp_set_scheme_mode_addr,
|
||||
reinterpret_cast<void*>(&custom_preference_set_scheme_mode_hook),
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_scheme_mode)
|
||||
reinterpret_cast<void**>(&g_orig_custom_preference_set_scheme_mode),
|
||||
"Cute.CustomPreference.SetScemeMode"
|
||||
);
|
||||
if (create_status == MH_OK || create_status == MH_ERROR_ALREADY_CREATED) {
|
||||
auto enable_status = MH_EnableHook(reinterpret_cast<void*>(cp_set_scheme_mode_addr));
|
||||
if (enable_status == MH_OK || enable_status == MH_ERROR_ENABLED) {
|
||||
hook_log("[cgss-dmm-hook] hooked Cute.CustomPreference.SetScemeMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply_custom_preference_url_overrides();
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void start_hook_thread();
|
||||
+102
-2
@@ -1,5 +1,8 @@
|
||||
#include "stdinclude.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "hook.hpp"
|
||||
|
||||
extern "C" {
|
||||
void* GetFileVersionInfoA_Original = nullptr;
|
||||
void* GetFileVersionInfoSizeA_Original = nullptr;
|
||||
@@ -33,11 +36,45 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
|
||||
void start_hook_thread();
|
||||
|
||||
namespace {
|
||||
constexpr DWORD kProxyInitDelayMs = 1000;
|
||||
constexpr const char* kHelperExeName = "cgss-borderless-helper.exe";
|
||||
constexpr const char* kGameExeName = "imascgstage.exe";
|
||||
HMODULE g_original = nullptr;
|
||||
volatile LONG g_helper_started = 0;
|
||||
|
||||
bool is_game_process() {
|
||||
char path[MAX_PATH] = {};
|
||||
DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH);
|
||||
if (len == 0 || len >= MAX_PATH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* base_name = path;
|
||||
for (const char* p = path; *p != '\0'; ++p) {
|
||||
if (*p == '\\' || *p == '/') {
|
||||
base_name = p + 1;
|
||||
}
|
||||
}
|
||||
return lstrcmpiA(base_name, kGameExeName) == 0;
|
||||
}
|
||||
|
||||
std::string get_game_directory() {
|
||||
char path[MAX_PATH] = {};
|
||||
DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH);
|
||||
if (len == 0 || len >= MAX_PATH) {
|
||||
return {};
|
||||
}
|
||||
|
||||
char* last_sep = path;
|
||||
for (char* p = path; *p != '\0'; ++p) {
|
||||
if (*p == '\\' || *p == '/') {
|
||||
last_sep = p;
|
||||
}
|
||||
}
|
||||
*last_sep = '\0';
|
||||
return path;
|
||||
}
|
||||
|
||||
void load_original_version_dll() {
|
||||
if (g_original) {
|
||||
@@ -68,9 +105,72 @@ namespace {
|
||||
hook_log("[cgss-dmm-hook] version.dll initialized");
|
||||
}
|
||||
|
||||
void start_borderless_helper() {
|
||||
if (!config::get().launch_borderless_helper) {
|
||||
hook_log("[cgss-dmm-hook] launch_borderless_helper=false, skipping helper launch");
|
||||
return;
|
||||
}
|
||||
if (InterlockedCompareExchange(&g_helper_started, 1, 0) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto game_dir = get_game_directory();
|
||||
if (game_dir.empty()) {
|
||||
hook_log("[cgss-dmm-hook] failed to resolve game directory for helper launch");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string helper_path = game_dir;
|
||||
helper_path += "\\";
|
||||
helper_path += kHelperExeName;
|
||||
|
||||
DWORD attributes = GetFileAttributesA(helper_path.c_str());
|
||||
if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
||||
hook_log("[cgss-dmm-hook] borderless helper exe not found, skipping launch");
|
||||
return;
|
||||
}
|
||||
|
||||
char command_line[512] = {};
|
||||
wsprintfA(command_line, "\"%s\" --pid %lu", helper_path.c_str(), GetCurrentProcessId());
|
||||
|
||||
STARTUPINFOA startup_info = {};
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
BOOL result = CreateProcessA(
|
||||
helper_path.c_str(),
|
||||
command_line,
|
||||
nullptr,
|
||||
nullptr,
|
||||
FALSE,
|
||||
CREATE_NO_WINDOW,
|
||||
nullptr,
|
||||
game_dir.c_str(),
|
||||
&startup_info,
|
||||
&process_info
|
||||
);
|
||||
if (!result) {
|
||||
hook_logf(
|
||||
"[cgss-dmm-hook] failed to launch borderless helper, error=%lu",
|
||||
static_cast<unsigned long>(GetLastError())
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
CloseHandle(process_info.hThread);
|
||||
CloseHandle(process_info.hProcess);
|
||||
hook_log("[cgss-dmm-hook] launched borderless helper");
|
||||
}
|
||||
|
||||
DWORD WINAPI init_thread(void*) {
|
||||
Sleep(kProxyInitDelayMs);
|
||||
if (!is_game_process()) {
|
||||
load_original_version_dll();
|
||||
hook_log("[cgss-dmm-hook] non-game process detected, skipping hook/helper startup");
|
||||
return 0;
|
||||
}
|
||||
config::load();
|
||||
load_original_version_dll();
|
||||
start_borderless_helper();
|
||||
start_hook_thread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user