Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9099d50ba8 | |||
| 7aa7047ccd | |||
| d0b966eac7 | |||
| 0903e39be2 | |||
| 46c0597492 | |||
| 1b32246f9a | |||
| cb3db372df | |||
| 2fee742f75 | |||
| b36d217284 | |||
| 8520356083 | |||
| 9bd1379244 | |||
| 7d7967f477 | |||
| 8550abdf9f | |||
| 912bc2e55f | |||
| f0da1b8456 |
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 mkrsym1 <mkrsym1@gmail.com>
|
Copyright (c) 2023-2024 mkrsym1 <mkrsym1@gmail.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
12
README.md
12
README.md
@ -1,10 +1,14 @@
|
|||||||
# Jadeite Autopatcher
|
# Jadeite Autopatcher
|
||||||
### Current game support:
|
### Current game support:
|
||||||
- **3rd**: glb/sea/tw/kr/jp **v7.1.0+**, cn **v7.2.0+**
|
- **3rd**: glb/sea/tw/kr/jp **v7.3.0+**, cn **v7.3.0+**
|
||||||
- **SR**: os/cn **v1.6.0**
|
- **SR**: os/cn **v2.0.0**\*
|
||||||
|
|
||||||
You can expect newer versions to work immediately after release with the same jadeite binary if the version is specified with a + above.
|
You can expect newer versions to work immediately after release with the same jadeite binary if the version is specified with a + above.
|
||||||
|
|
||||||
|
**Note:** you can test the experimental version-independent patching method for SR. See [#37](https://codeberg.org/mkrsym1/jadeite/issues/37).
|
||||||
|
|
||||||
|
**Note:** the Steam version of 3rd was reported to work, however I neither tested it nor intend to support it, so it might break at any time. Use at your own risk.
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
The anticheat the games use is fundamentally incompatible with Wine in multiple ways. This tool launches the game without it (`injector`) and imitates it's behaviour (`game_payload`).
|
The anticheat the games use is fundamentally incompatible with Wine in multiple ways. This tool launches the game without it (`injector`) and imitates it's behaviour (`game_payload`).
|
||||||
|
|
||||||
@ -38,8 +42,12 @@ Example command: `jadeite.exe 'Z:\path\to\game.exe' -- -screen-fullscreen 1`
|
|||||||
## Configuration
|
## Configuration
|
||||||
These environment variables can be used to configure the behaviour of the tool. Any value except empty string counts as set. `1` will be used in all examples.
|
These environment variables can be used to configure the behaviour of the tool. Any value except empty string counts as set. `1` will be used in all examples.
|
||||||
|
|
||||||
|
**Global**:
|
||||||
- `WAIT_BEFORE_RESUME=1` - show a messagebox and wait for user input before resuming the game process. Useful on my side for debugging
|
- `WAIT_BEFORE_RESUME=1` - show a messagebox and wait for user input before resuming the game process. Useful on my side for debugging
|
||||||
|
|
||||||
|
**SR-specific**:
|
||||||
|
- `BREAK_CRYPTCAT=1` - use the experimental patching method. See [#37](https://codeberg.org/mkrsym1/jadeite/issues/37) for details
|
||||||
|
|
||||||
## Internals and building
|
## Internals and building
|
||||||
To compile jadeite, you will need meson, mingw and nasm. You can probably install all three using your repository's package manager. Once all dependencies are installed, run `./build.sh` in this directory. The compiled files will be located in `./out`.
|
To compile jadeite, you will need meson, mingw and nasm. You can probably install all three using your repository's package manager. Once all dependencies are installed, run `./build.sh` in this directory. The compiled files will be located in `./out`.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -1,9 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define UTILS_COUNT(arr) (sizeof(arr) / sizeof(*arr))
|
#define UTILS_COUNT(arr) (sizeof(arr) / sizeof(*arr))
|
||||||
|
|
||||||
|
struct file_mapping {
|
||||||
|
HANDLE file;
|
||||||
|
HANDLE mapping;
|
||||||
|
unsigned char *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void utils_map_file(const wchar_t *path, struct file_mapping *map);
|
||||||
|
void utils_unmap_file(struct file_mapping *map);
|
||||||
|
|
||||||
int utils_path_exists(const wchar_t *filePath);
|
int utils_path_exists(const wchar_t *filePath);
|
||||||
uint32_t utils_file_crc32c(const wchar_t *filePath);
|
uint32_t utils_file_crc32c(const wchar_t *filePath);
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const char *HSR_ASSEMBLY_PATH = "GameAssembly.dll";
|
|||||||
const char *HSR_TXS_SECTION_NAME = ".ace";
|
const char *HSR_TXS_SECTION_NAME = ".ace";
|
||||||
const char *HSR_TVM_SECTION_NAME = ".tvm0";
|
const char *HSR_TVM_SECTION_NAME = ".tvm0";
|
||||||
|
|
||||||
#define HSR_VERSION "1.6.0"
|
#define HSR_VERSION "2.0.0"
|
||||||
|
|
||||||
enum hsr_region {
|
enum hsr_region {
|
||||||
HSR_INVALID,
|
HSR_INVALID,
|
||||||
@ -23,14 +23,14 @@ struct crc_region_pair {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const struct crc_region_pair HSR_REGIONS[] = {
|
const struct crc_region_pair HSR_REGIONS[] = {
|
||||||
{ 0x5741ce50, HSR_OS }, // os v1.6.0
|
{ 0x5be3a560, HSR_OS }, // os v2.0.0
|
||||||
{ 0xce891f97, HSR_CN } // cn v1.6.0
|
{ 0x974e826f, HSR_CN } // cn v2.0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
#define JUMP_SIZE (6 + sizeof(void*))
|
#define JUMP_SIZE (6 + sizeof(void*))
|
||||||
|
|
||||||
// Temporarily hardcoded offset
|
// Temporarily hardcoded offset
|
||||||
// v1.6.0, same for os and cn
|
// v2.0.0, same for os and cn
|
||||||
#define WTSUD_PATCH_OFFSET 0x16510
|
#define WTSUD_PATCH_OFFSET 0x16510
|
||||||
|
|
||||||
char wtsud_original_bytes[JUMP_SIZE];
|
char wtsud_original_bytes[JUMP_SIZE];
|
||||||
|
|||||||
@ -75,6 +75,11 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LPWSTR targetExe = malloc(MAX_PATH);
|
||||||
|
GetModuleFileNameW(NULL, targetExe, 0);
|
||||||
|
SetCurrentDirectoryW(targetExe);
|
||||||
|
free(targetExe);
|
||||||
|
|
||||||
this_module = instance;
|
this_module = instance;
|
||||||
|
|
||||||
// Dynamically link functions from ntdll
|
// Dynamically link functions from ntdll
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <pe.h>
|
#include <pe.h>
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <utils.h>
|
||||||
|
|
||||||
#include <tx.h>
|
#include <tx.h>
|
||||||
|
|
||||||
@ -15,30 +16,23 @@ void tx_table_file(struct game_data *game, wchar_t *buf) {
|
|||||||
GetTempPathW(MAX_PATH, tempDir);
|
GetTempPathW(MAX_PATH, tempDir);
|
||||||
|
|
||||||
// Memorymap the base module
|
// Memorymap the base module
|
||||||
HANDLE baseFile = CreateFileA(game->base_module_name, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
wchar_t baseModuleW[MAX_PATH];
|
||||||
if (!baseFile) {
|
MultiByteToWideChar(CP_UTF8, 0, game->base_module_name, strlen(game->base_module_name) + 1, baseModuleW, MAX_PATH);
|
||||||
msg_err_a("Could not open file: %s", game->base_module_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE hBaseMap = CreateFileMappingA(baseFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
struct file_mapping map;
|
||||||
char *baseMap = MapViewOfFile(hBaseMap, FILE_MAP_READ, 0, 0, 0);
|
utils_map_file(baseModuleW, &map);
|
||||||
if (!baseMap) {
|
|
||||||
msg_err_a("Could not create file mapping for %s", game->base_module_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checksum the TXS section
|
// Checksum the TXS section
|
||||||
IMAGE_SECTION_HEADER *txsSection = pe_find_section(baseMap, game->txs_section_name);
|
IMAGE_SECTION_HEADER *txsSection = pe_find_section(map.data, game->txs_section_name);
|
||||||
if (!txsSection) {
|
if (!txsSection) {
|
||||||
msg_err_a("Could not find %s in %s. " ISSUE_SUFFIX, game->txs_section_name, game->base_module_name);
|
msg_err_a("Could not find %s in %s. " ISSUE_SUFFIX, game->txs_section_name, game->base_module_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t txsChecksum = crc32c(0, baseMap + txsSection->PointerToRawData, txsSection->SizeOfRawData);
|
uint32_t txsChecksum = crc32c(0, map.data + txsSection->PointerToRawData, txsSection->SizeOfRawData);
|
||||||
|
|
||||||
// Format the path
|
// Format the path
|
||||||
wsprintfW(buf, L"%sjadeite\\" JADEITE_VERSION "\\%hs.%x.dat", tempDir, game->base_module_name, txsChecksum);
|
wsprintfW(buf, L"%sjadeite\\" JADEITE_VERSION "\\%hs.%x.dat", tempDir, game->base_module_name, txsChecksum);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
UnmapViewOfFile(baseMap);
|
utils_unmap_file(&map);
|
||||||
CloseHandle(hBaseMap);
|
|
||||||
CloseHandle(baseFile);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,31 +5,53 @@
|
|||||||
|
|
||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
|
|
||||||
|
void utils_map_file(const wchar_t *path, struct file_mapping *map) {
|
||||||
|
wchar_t* final_path = malloc(MAX_PATH);
|
||||||
|
if (wcsstr(path, L"C:\\") == NULL) {
|
||||||
|
wchar_t* tmp = malloc(MAX_PATH);
|
||||||
|
GetEnvironmentVariableW(L"GAME_PATH", tmp, MAX_PATH);
|
||||||
|
swprintf(final_path, MAX_PATH, L"%ls\\%ls", tmp, path);
|
||||||
|
free(tmp);
|
||||||
|
} else {
|
||||||
|
swprintf(final_path, MAX_PATH, L"%ls", path);
|
||||||
|
}
|
||||||
|
map->file = CreateFileW(final_path, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (map->file == INVALID_HANDLE_VALUE) {
|
||||||
|
msg_err_w(L"Could not open file: %ls", final_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
map->mapping = CreateFileMappingA(map->file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||||
|
map->data = MapViewOfFile(map->mapping, FILE_MAP_READ, 0, 0, 0);
|
||||||
|
if (!map->data) {
|
||||||
|
msg_err_w(L"Could not map view of file %ls", final_path);
|
||||||
|
}
|
||||||
|
free(final_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void utils_unmap_file(struct file_mapping *map) {
|
||||||
|
UnmapViewOfFile(map->data);
|
||||||
|
CloseHandle(map->mapping);
|
||||||
|
CloseHandle(map->file);
|
||||||
|
}
|
||||||
|
|
||||||
int utils_path_exists(const wchar_t *filePath) {
|
int utils_path_exists(const wchar_t *filePath) {
|
||||||
return GetFileAttributesW(filePath) != INVALID_FILE_ATTRIBUTES;
|
return GetFileAttributesW(filePath) != INVALID_FILE_ATTRIBUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t utils_file_crc32c(const wchar_t *filePath) {
|
uint32_t utils_file_crc32c(const wchar_t *filePath) {
|
||||||
HANDLE file = CreateFileW(filePath, FILE_READ_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
struct file_mapping map;
|
||||||
if (!file) {
|
// LPWSTR cwd = malloc(MAX_PATH);
|
||||||
msg_err_w(L"Could not open file: %ls", filePath);
|
// GetCurrentDirectoryW(MAX_PATH, cwd);
|
||||||
}
|
// msg_info_w(L"File %ls %ls", filePath, cwd);
|
||||||
|
// free(cwd);
|
||||||
|
utils_map_file(filePath, &map);
|
||||||
|
|
||||||
LARGE_INTEGER fileSize;
|
LARGE_INTEGER fileSize;
|
||||||
GetFileSizeEx(file, &fileSize);
|
GetFileSizeEx(map.file, &fileSize);
|
||||||
|
|
||||||
HANDLE hMap = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
uint32_t crc = crc32c(0, map.data, fileSize.QuadPart);
|
||||||
char *map = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
|
|
||||||
if (!map) {
|
|
||||||
msg_err_w(L"Could not create file mapping for %ls", filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t crc = crc32c(0, map, fileSize.QuadPart);
|
|
||||||
|
|
||||||
UnmapViewOfFile(map);
|
|
||||||
CloseHandle(hMap);
|
|
||||||
CloseHandle(file);
|
|
||||||
|
|
||||||
|
utils_unmap_file(&map);
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +73,8 @@ void utils_create_parent_dirs(const wchar_t *path) {
|
|||||||
|
|
||||||
void utils_save_to_file(const wchar_t *filePath, const void *buf, size_t length) {
|
void utils_save_to_file(const wchar_t *filePath, const void *buf, size_t length) {
|
||||||
HANDLE file = CreateFileW(filePath, FILE_WRITE_ACCESS, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
HANDLE file = CreateFileW(filePath, FILE_WRITE_ACCESS, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (!file) {
|
if (file == INVALID_HANDLE_VALUE) {
|
||||||
msg_err_w(L"Could not open file: %ls", filePath);
|
msg_err_w(L"Could not create file: %ls", filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteFile(file, buf, length, NULL, FALSE);
|
WriteFile(file, buf, length, NULL, FALSE);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tlhelp32.h>
|
||||||
#include <inject.h>
|
#include <inject.h>
|
||||||
#include <envs.h>
|
#include <envs.h>
|
||||||
|
|
||||||
@ -9,6 +10,75 @@ typedef char *(*wgufn_t)(wchar_t* path); // wine_get_unix_file_name
|
|||||||
|
|
||||||
const wchar_t *J_MB_TITLE = L"Jadeite Launcher Payload";
|
const wchar_t *J_MB_TITLE = L"Jadeite Launcher Payload";
|
||||||
|
|
||||||
|
// Copied from https://cocomelonc.github.io/pentest/2021/09/29/findmyprocess.html
|
||||||
|
// Find process ID by process name
|
||||||
|
DWORD find_proc_id(const char *procname) {
|
||||||
|
|
||||||
|
HANDLE hSnapshot;
|
||||||
|
PROCESSENTRY32 pe;
|
||||||
|
DWORD pid = 0;
|
||||||
|
BOOL hResult;
|
||||||
|
|
||||||
|
// snapshot of all processes in the system
|
||||||
|
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
|
||||||
|
|
||||||
|
// initializing size: needed for using Process32First
|
||||||
|
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||||
|
|
||||||
|
// info about first process encountered in a system snapshot
|
||||||
|
hResult = Process32First(hSnapshot, &pe);
|
||||||
|
|
||||||
|
// retrieve information about the processes
|
||||||
|
// and exit if unsuccessful
|
||||||
|
while (hResult) {
|
||||||
|
// if we find the process: return process ID
|
||||||
|
if (strcmp(procname, pe.szExeFile) == 0) {
|
||||||
|
pid = pe.th32ProcessID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hResult = Process32Next(hSnapshot, &pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// closes an open handle (CreateToolhelp32Snapshot)
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find thread ID by process PID
|
||||||
|
int find_main_thread(DWORD pid) {
|
||||||
|
|
||||||
|
HANDLE hSnapshot;
|
||||||
|
THREADENTRY32 pe;
|
||||||
|
DWORD threadId = 0;
|
||||||
|
BOOL hResult;
|
||||||
|
|
||||||
|
// snapshot of all processes in the system
|
||||||
|
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||||
|
if (INVALID_HANDLE_VALUE == hSnapshot) return 0;
|
||||||
|
|
||||||
|
// initializing size: needed for using Process32First
|
||||||
|
pe.dwSize = sizeof(THREADENTRY32);
|
||||||
|
|
||||||
|
// info about first process encountered in a system snapshot
|
||||||
|
hResult = Thread32First(hSnapshot, &pe);
|
||||||
|
|
||||||
|
// retrieve information about the processes
|
||||||
|
// and exit if unsuccessful
|
||||||
|
while (hResult) {
|
||||||
|
// if we find the process: return process ID
|
||||||
|
if (pid == pe.th32OwnerProcessID) {
|
||||||
|
threadId = pe.th32ThreadID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hResult = Thread32Next(hSnapshot, &pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// closes an open handle (CreateToolhelp32Snapshot)
|
||||||
|
CloseHandle(hSnapshot);
|
||||||
|
return threadId;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||||
// Only listen for attach
|
// Only listen for attach
|
||||||
if (reason != DLL_PROCESS_ATTACH) {
|
if (reason != DLL_PROCESS_ATTACH) {
|
||||||
@ -32,6 +102,13 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|||||||
wcscpy(workdir, targetExe);
|
wcscpy(workdir, targetExe);
|
||||||
*(wcsrchr(workdir, L'\\')) = L'\0';
|
*(wcsrchr(workdir, L'\\')) = L'\0';
|
||||||
|
|
||||||
|
// Change the game's working directory
|
||||||
|
LPWSTR game_path = malloc(MAX_PATH);
|
||||||
|
GetEnvironmentVariableW(L"GAME_PATH", game_path, MAX_PATH);
|
||||||
|
wchar_t message[64];
|
||||||
|
wsprintfW(message, L"Game Path: %ls", game_path);
|
||||||
|
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
|
||||||
|
|
||||||
// SAFETY: verify that the injector is not inside the game directory
|
// SAFETY: verify that the injector is not inside the game directory
|
||||||
HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
|
HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
|
||||||
wgufn_t wine_get_unix_file_name = (wgufn_t)GetProcAddress(kernel32, "wine_get_unix_file_name");
|
wgufn_t wine_get_unix_file_name = (wgufn_t)GetProcAddress(kernel32, "wine_get_unix_file_name");
|
||||||
@ -81,7 +158,7 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
FALSE,
|
FALSE,
|
||||||
CREATE_SUSPENDED,
|
INHERIT_PARENT_AFFINITY,
|
||||||
NULL,
|
NULL,
|
||||||
workdir,
|
workdir,
|
||||||
&si,
|
&si,
|
||||||
@ -94,10 +171,27 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the game process
|
||||||
|
wprintf(L"Waiting for game process to start...\n");
|
||||||
|
DWORD game_pid = 0;
|
||||||
|
while (!game_pid) {
|
||||||
|
// wprintf(L"Looking for game process...\n");
|
||||||
|
game_pid = find_proc_id("StarRail.exe");
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
DWORD thread_id = 0;
|
||||||
|
while (!thread_id) {
|
||||||
|
// wprintf(L"Looking for game process...\n");
|
||||||
|
thread_id = find_main_thread(game_pid);
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
HANDLE game = OpenProcess(PROCESS_ALL_ACCESS, FALSE, game_pid);
|
||||||
|
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, thread_id);
|
||||||
|
SuspendThread(hThread);
|
||||||
// Inject
|
// Inject
|
||||||
void *payloadStart = &_binary_game_p_o_p_game_p_bin_start;
|
void *payloadStart = &_binary_game_p_o_p_game_p_bin_start;
|
||||||
size_t payloadSize = (size_t)&_binary_game_p_o_p_game_p_bin_size;
|
size_t payloadSize = (size_t)&_binary_game_p_o_p_game_p_bin_size;
|
||||||
inject(pi.hProcess, payloadStart, payloadSize, injectDll);
|
inject(game, payloadStart, payloadSize, injectDll);
|
||||||
|
|
||||||
// Remove the restart flag file
|
// Remove the restart flag file
|
||||||
DeleteFileW(restartFlagFile);
|
DeleteFileW(restartFlagFile);
|
||||||
@ -106,15 +200,15 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|||||||
char *waitEnabled = getenv("WAIT_BEFORE_RESUME");
|
char *waitEnabled = getenv("WAIT_BEFORE_RESUME");
|
||||||
if (waitEnabled && *waitEnabled) {
|
if (waitEnabled && *waitEnabled) {
|
||||||
wchar_t message[64];
|
wchar_t message[64];
|
||||||
wsprintfW(message, L"PID: %ld. Press OK to continue", pi.dwProcessId);
|
wsprintfW(message, L"PID: %ld. Thread ID: %ld. Press OK to continue", game_pid, thread_id);
|
||||||
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
|
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume the process
|
// Resume the process
|
||||||
ResumeThread(pi.hThread);
|
ResumeThread(hThread);
|
||||||
|
|
||||||
// The launcher process should now hang untill the game terminates
|
// The launcher process should now hang untill the game terminates
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
WaitForSingleObject(game, INFINITE);
|
||||||
} while (GetFileAttributesW(restartFlagFile) != INVALID_FILE_ATTRIBUTES);
|
} while (GetFileAttributesW(restartFlagFile) != INVALID_FILE_ATTRIBUTES);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
project('jadeite', 'c', version: '3.1.0')
|
project('jadeite', 'c', version: '3.1.1')
|
||||||
|
|
||||||
nasm = find_program('nasm')
|
nasm = find_program('nasm')
|
||||||
gen_res = find_program('gen_resources.sh')
|
gen_res = find_program('gen_resources.sh')
|
||||||
|
|||||||
@ -1,42 +1,42 @@
|
|||||||
{
|
{
|
||||||
"jadeite": {
|
"jadeite": {
|
||||||
"version": "3.1.0"
|
"version": "3.1.1"
|
||||||
},
|
},
|
||||||
"games": {
|
"games": {
|
||||||
"hi3rd": {
|
"hi3rd": {
|
||||||
"global": {
|
"global": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "7.1.0"
|
"version": "7.3.0"
|
||||||
},
|
},
|
||||||
"sea": {
|
"sea": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "7.1.0"
|
"version": "7.3.0"
|
||||||
},
|
},
|
||||||
"china": {
|
"china": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "7.2.0"
|
"version": "7.3.0"
|
||||||
},
|
},
|
||||||
"taiwan": {
|
"taiwan": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "7.1.0"
|
"version": "7.3.0"
|
||||||
},
|
},
|
||||||
"korea": {
|
"korea": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "7.1.0"
|
"version": "7.3.0"
|
||||||
},
|
},
|
||||||
"japan": {
|
"japan": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "7.1.0"
|
"version": "7.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hsr": {
|
"hsr": {
|
||||||
"global": {
|
"global": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "1.6.0"
|
"version": "2.0.0"
|
||||||
},
|
},
|
||||||
"china": {
|
"china": {
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
"version": "1.6.0"
|
"version": "2.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user