Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,11 @@ All Doorstop arguments start with `--doorstop-` and always contain an argument.
* `string` = any sequence of characters and numbers. Wrap into `"`s if the string contains spaces

| Argument | Description |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| ------------------------------------------------- |------------------------------------------------------------------------------------------------------|
| `--doorstop-enabled bool` | Enable or disable Doorstop. |
| `--doorstop-redirect-output-log bool` | *Only on Windows*: If `true` Unity's output log is redirected to `<current folder>\output_log.txt` |
| `--doorstop-target-assembly string` | Path to the assembly to load and execute. |
| `--doorstop-boot-config-override string` | Overrides the boot.config file path. |
| `--doorstop-mono-dll-search-path-override string` | Overrides default Mono DLL search path |
| `--doorstop-mono-debug-enabled bool` | If true, Mono debugger server will be enabled |
| `--doorstop-mono-debug-suspend bool` | Whether to suspend the game execution until the debugger is attached. |
Expand Down
8 changes: 8 additions & 0 deletions assets/nix/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ enabled="1"
# NOTE: The entrypoint must be of format `static void Doorstop.Entrypoint.Start()`
target_assembly="Doorstop.dll"

# Overrides the default boot.config file path
boot_config_override=

# If enabled, DOORSTOP_DISABLE env var value is ignored
# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS
ignore_disable_switch="0"
Expand Down Expand Up @@ -197,6 +200,10 @@ while :; do
target_assembly="$2"
shift
;;
--doorstop-boot-config-override)
boot_config_override="$2"
shift
;;
--doorstop-mono-dll-search-path-override)
dll_search_path_override="$2"
shift
Expand Down Expand Up @@ -234,6 +241,7 @@ done
# Move variables to environment
export DOORSTOP_ENABLED="$enabled"
export DOORSTOP_TARGET_ASSEMBLY="$target_assembly"
export DOORSTOP_BOOT_CONFIG_OVERRIDE="$boot_config_override"
export DOORSTOP_IGNORE_DISABLED_ENV="$ignore_disable_switch"
export DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE="$dll_search_path_override"
export DOORSTOP_MONO_DEBUG_ENABLED="$debug_enable"
Expand Down
3 changes: 3 additions & 0 deletions assets/windows/doorstop_config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ target_assembly=Doorstop.dll
# If true, Unity's output log is redirected to <current folder>\output_log.txt
redirect_output_log=false

# Overrides the default boot.config file path
boot_config_override=

# If enabled, DOORSTOP_DISABLE env var value is ignored
# USE THIS ONLY WHEN ASKED TO OR YOU KNOW WHAT THIS MEANS
ignore_disable_switch=false
Expand Down
7 changes: 4 additions & 3 deletions src/bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,14 @@ void il2cpp_doorstop_bootstrap() {
strcat(app_paths_env, config.clr_corlib_dir);
strcat(app_paths_env, PATH_SEP);
strcat(app_paths_env, target_dir);
char *app_paths_env_n = narrow(app_paths_env);
const char *app_paths_env_n = narrow(app_paths_env);

LOG("App path: %s", app_path);
LOG("Target dir: %s", target_dir);
LOG("Target name: %s", target_name);
LOG("APP_PATHS: %s", app_paths_env);

char *props = "APP_PATHS";
const char *props = "APP_PATHS";

setenv(TEXT("DOORSTOP_INITIALIZED"), TEXT("TRUE"), TRUE);
setenv(TEXT("DOORSTOP_INVOKE_DLL_PATH"), config.target_assembly, TRUE);
Expand All @@ -293,7 +293,8 @@ void il2cpp_doorstop_bootstrap() {

void (*startup)() = NULL;
result = coreclr.create_delegate(host, domain_id, target_name_n,
"Doorstop.Entrypoint", "Start", &startup);
"Doorstop.Entrypoint", "Start",
(void **)&startup);
if (result != 0) {
LOG("Failed to get entrypoint delegate: 0x%08x", result);
return;
Expand Down
2 changes: 2 additions & 0 deletions src/config/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ void cleanup_config() {
}

FREE_NON_NULL(config.target_assembly);
FREE_NON_NULL(config.boot_config_override);
FREE_NON_NULL(config.mono_dll_search_path_override);
FREE_NON_NULL(config.clr_corlib_dir);
FREE_NON_NULL(config.clr_runtime_coreclr_path);
Expand All @@ -27,6 +28,7 @@ void init_config_defaults() {
config.mono_debug_suspend = FALSE;
config.mono_debug_address = NULL;
config.target_assembly = NULL;
config.boot_config_override = NULL;
config.mono_dll_search_path_override = NULL;
config.clr_corlib_dir = NULL;
config.clr_runtime_coreclr_path = NULL;
Expand Down
6 changes: 6 additions & 0 deletions src/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ typedef struct {
*/
char_t *target_assembly;

/**
* @brief Path to a custom boot.config file to use. If enabled, this file
* takes precedence over the default one in the Data folder.
*/
char_t *boot_config_override;

/**
* @brief Path to use as the main DLL search path. If enabled, this folder
* takes precedence over the default Managed folder.
Expand Down
3 changes: 2 additions & 1 deletion src/nix/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void get_env_bool(const char_t *name, bool_t *target) {
}
}

void try_get_env(const char_t *name, const char_t *def, char_t **target) {
void try_get_env(const char_t *name, char_t *def, char_t **target) {
char_t *value = getenv(name);
if (value != NULL && strlen(value) > 0) {
*target = strdup(value);
Expand All @@ -37,6 +37,7 @@ void load_config() {
try_get_env("DOORSTOP_MONO_DEBUG_ADDRESS", TEXT("127.0.0.1:10000"),
&config.mono_debug_address);
get_env_path("DOORSTOP_TARGET_ASSEMBLY", &config.target_assembly);
get_env_path("DOORSTOP_BOOT_CONFIG_OVERRIDE", &config.boot_config_override);
try_get_env("DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE", TEXT(""),
&config.mono_dll_search_path_override);
get_env_path("DOORSTOP_CLR_RUNTIME_CORECLR_PATH",
Expand Down
53 changes: 52 additions & 1 deletion src/nix/entrypoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,31 @@ int fclose_hook(FILE *stream) {
return fclose(stream);
}

char_t *default_boot_config_path = NULL;
#if !defined(__APPLE__)
FILE *fopen64_hook(char *filename, char *mode) {
char *actual_file_name = filename;

if (strcmp(filename, default_boot_config_path) == 0) {
actual_file_name = config.boot_config_override;
LOG("Overriding boot.config to %s", actual_file_name);
}

return fopen64(actual_file_name, mode);
}
#endif

FILE *fopen_hook(char *filename, char *mode) {
char *actual_file_name = filename;

if (strcmp(filename, default_boot_config_path) == 0) {
actual_file_name = config.boot_config_override;
LOG("Overriding boot.config to %s", actual_file_name);
}

return fopen(actual_file_name, mode);
}

int dup2_hook(int od, int nd) {
// Newer versions of Unity redirect stdout to player.log, we don't want
// that
Expand All @@ -80,7 +105,8 @@ __attribute__((constructor)) void doorstop_ctor() {

void *unity_player = plthook_handle_by_name("UnityPlayer");

if (unity_player && PLTHOOK_OPEN_BY_HANDLE_OR_ADDRESS(&hook, unity_player) == 0) {
if (unity_player &&
PLTHOOK_OPEN_BY_HANDLE_OR_ADDRESS(&hook, unity_player) == 0) {
LOG("Found UnityPlayer, hooking into it instead");
} else if (plthook_open(&hook, NULL) != 0) {
LOG("Failed to open current process PLT! Cannot run Doorstop! "
Expand All @@ -94,6 +120,31 @@ __attribute__((constructor)) void doorstop_ctor() {
printf("Failed to hook dlsym, ignoring it. Error: %s\n",
plthook_error());

if (config.boot_config_override) {
if (file_exists(config.boot_config_override)) {
default_boot_config_path = calloc(MAX_PATH, sizeof(char_t));
memset(default_boot_config_path, 0, MAX_PATH * sizeof(char_t));
strcat(default_boot_config_path, get_working_dir());
strcat(default_boot_config_path, TEXT("/"));
strcat(default_boot_config_path,
get_file_name(program_path(), FALSE));
strcat(default_boot_config_path, TEXT("_Data/boot.config"));

#if !defined(__APPLE__)
if (plthook_replace(hook, "fopen64", &fopen64_hook, NULL) != 0)
printf("Failed to hook fopen64, ignoring it. Error: %s\n",
plthook_error());
#endif
if (plthook_replace(hook, "fopen", &fopen_hook, NULL) != 0)
printf("Failed to hook fopen, ignoring it. Error: %s\n",
plthook_error());
} else {
LOG("The boot.config file won't be overriden because the provided "
"one does not exist: %s",
config.boot_config_override);
}
}

if (plthook_replace(hook, "fclose", &fclose_hook, NULL) != 0)
printf("Failed to hook fclose, ignoring it. Error: %s\n",
plthook_error());
Expand Down
2 changes: 2 additions & 0 deletions src/util/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ typedef int bool_t;

#define TRUE 1
#define FALSE 0
#ifndef NULL
#define NULL 0
#endif

#define TEXT(text) text

Expand Down
4 changes: 4 additions & 0 deletions src/windows/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ static inline void init_config_file() {
TEXT("false"), &config.redirect_output_log);
load_path_file(config_path, TEXT("General"), TEXT("target_assembly"),
DEFAULT_TARGET_ASSEMBLY, &config.target_assembly);
load_path_file(config_path, TEXT("General"), TEXT("boot_config_override"),
NULL, &config.boot_config_override);

load_str_file(config_path, TEXT("UnityMono"),
TEXT("dll_search_path_override"), TEXT(""),
Expand Down Expand Up @@ -144,6 +146,8 @@ static inline void init_cmd_args() {
config.redirect_output_log, load_bool_argv);
PARSE_ARG(TEXT("--doorstop-target-assembly"), config.target_assembly,
load_path_argv);
PARSE_ARG(TEXT("--doorstop-boot-config-override"),
config.boot_config_override, load_path_argv);

PARSE_ARG(TEXT("--doorstop-mono-dll-search-path-override"),
config.mono_dll_search_path_override, load_path_argv);
Expand Down
85 changes: 85 additions & 0 deletions src/windows/entrypoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ bool_t fix_cwd() {
#define LOG_FILE_CMD_END L"\\output_log.txt\""
#define LOG_FILE_CMD_END_LEN STR_LEN(LOG_FILE_CMD_END)

char_t *default_boot_config_path = NULL;

char_t *new_cmdline_args = NULL;
char *new_cmdline_args_narrow = NULL;

Expand All @@ -63,6 +65,70 @@ bool_t WINAPI close_handle_hook(void *handle) {
return CloseHandle(handle);
}

HANDLE WINAPI create_file_hook(LPCWSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile) {
LPCWSTR actual_file_name = lpFileName;

char_t *normalised_path = calloc(strlen(lpFileName) + 1, sizeof(char_t));
memset(normalised_path, 0, (strlen(lpFileName) + 1) * sizeof(char_t));
strcpy(normalised_path, lpFileName);
for (size_t i = 0; i < strlen(normalised_path); i++) {
if (normalised_path[i] == L'/') {
normalised_path[i] = L'\\';
}
}

if (strcmpi(normalised_path, default_boot_config_path) == 0) {
actual_file_name = config.boot_config_override;
LOG("Overriding boot.config to %s", actual_file_name);
}

free(normalised_path);

return CreateFileW(actual_file_name, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}

HANDLE WINAPI create_file_hook_narrow(
void *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
void *actual_file_name = lpFileName;

char_t *widened_filename = widen(lpFileName);
char_t *normalised_path =
calloc(strlen(widened_filename) + 1, sizeof(char_t));
memset(normalised_path, 0, (strlen(widened_filename) + 1) * sizeof(char_t));
strcpy(normalised_path, widened_filename);
free(widened_filename);

for (size_t i = 0; i < strlen(normalised_path); i++) {
if (normalised_path[i] == L'/') {
normalised_path[i] = L'\\';
}
}

if (strcmpi(normalised_path, default_boot_config_path) == 0) {
char *narrowed_boot_config_override =
narrow(config.boot_config_override);
memcpy(actual_file_name, narrowed_boot_config_override,
strlen(config.boot_config_override));
free(narrowed_boot_config_override);
LOG("Overriding boot.config to %s", actual_file_name);
}

free(normalised_path);

return CreateFileA(actual_file_name, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}

void capture_mono_path(void *handle) {
char_t *result;
get_module_path(handle, &result, NULL, 0);
Expand Down Expand Up @@ -146,6 +212,25 @@ void inject(DoorstopPaths const *paths) {

HOOK_SYS(target_module, GetProcAddress, get_proc_address_detour);
HOOK_SYS(target_module, CloseHandle, close_handle_hook);
if (config.boot_config_override) {
if (file_exists(config.boot_config_override)) {
default_boot_config_path = calloc(MAX_PATH, sizeof(char_t));
memset(default_boot_config_path, 0, MAX_PATH * sizeof(char_t));
strcat(default_boot_config_path, get_working_dir());
strcat(default_boot_config_path, TEXT("\\"));
strcat(default_boot_config_path,
get_file_name(program_path(), FALSE));
strcat(default_boot_config_path, TEXT("_Data\\boot.config"));

HOOK_SYS(target_module, CreateFileW, create_file_hook);
HOOK_SYS(target_module, CreateFileA, create_file_hook_narrow);
} else {
LOG("The boot.config file won't be overriden because the provided "
"one does not exist: %s",
config.boot_config_override);
}
}

HOOK_SYS(app_module, GetCommandLineW, get_command_line_hook);
HOOK_SYS(app_module, GetCommandLineA, get_command_line_hook_narrow);

Expand Down