diff --git a/src/redirect/main.c b/src/redirect/main.c index a0951a7..64ed27b 100644 --- a/src/redirect/main.c +++ b/src/redirect/main.c @@ -44,11 +44,14 @@ static volatile bool lsi_override = false; */ typedef int (*lsi_open_file)(const char *p, int flags, mode_t mode); +typedef FILE *(*lsi_fopen64_file)(const char *p, const char *modes); + /** * Known profiles */ static lsi_profile_generator_func generators[] = { &lsi_redirect_profile_new_ark, + &lsi_redirect_profile_new_project_highrise, }; /** @@ -56,6 +59,7 @@ static lsi_profile_generator_func generators[] = { */ typedef struct LsiRedirectTable { lsi_open_file open; + lsi_fopen64_file fopen64; /* Allow future handle opens.. */ struct { @@ -129,6 +133,18 @@ static void lsi_redirect_init_tables(void) /* Have the symbol correctly, copy it to make it usable */ memcpy(&lsi_table.open, &symbol_lookup, sizeof(lsi_table.open)); + symbol_lookup = NULL; + + /* Safely look up the fopen64 symbol */ + symbol_lookup = dlsym(lsi_table.handles.libc, "fopen64"); + dl_error = dlerror(); + if (dl_error || !symbol_lookup) { + fprintf(stderr, "Failed to bind 'fopen64': %s\n", dl_error); + goto failed; + } + + /* Have the symbol correctly, copy it to make it usable */ + memcpy(&lsi_table.fopen64, &symbol_lookup, sizeof(lsi_table.fopen64)); /* We might not get an unload, so chain onto atexit */ atexit(lsi_redirect_shutdown); @@ -200,14 +216,50 @@ clean_array: } } +/** + * Get a redirect path from the table if it exists, otherwise return NULL + */ +static char *lsi_get_redirect_path(const char *syscall_id, LsiRedirectOperation op, const char *p) +{ + autofree(char) *path = NULL; + LsiRedirect *redirect = NULL; + + /* Get the absolute path here */ + path = realpath(p, NULL); + if (!path) { + return NULL; + } + + /* find a valid replacement */ + for (redirect = lsi_profile->op_table[op]; redirect; redirect = redirect->next) { + /* Currently we only known path replacements */ + if (redirect->type != LSI_REDIRECT_PATH) { + continue; + } + if (strcmp(redirect->path_source, path) != 0) { + continue; + } + if (!lsi_file_exists(redirect->path_target)) { + lsi_log_warn("Replacement path does not exist: %s", redirect->path_target); + return NULL; + } + lsi_log_info("%s(): Replaced '%s' with '%s'", + syscall_id, + path, + redirect->path_target); + return strdup(redirect->path_target); + } + + /* Got nothin' */ + return NULL; +} + _nica_public_ int open(const char *p, int flags, ...) { va_list va; mode_t mode; - autofree(char) *replaced_path = NULL; - LsiRedirect *redirect = NULL; LsiRedirectOperation op = LSI_OPERATION_OPEN; - autofree(char) *path = NULL; + autofree(char) *replacement = NULL; /* Must ensure we're **really** initialised, as we might see open happen * before the constructor.. @@ -224,31 +276,33 @@ _nica_public_ int open(const char *p, int flags, ...) return lsi_table.open(p, flags, mode); } - /* Get the absolute path here */ - path = realpath(p, NULL); - if (!path) { + replacement = lsi_get_redirect_path("open", op, p); + if (!replacement) { return lsi_table.open(p, flags, mode); } + return lsi_table.open(replacement, flags, mode); +} - /* find a valid replacement */ - for (redirect = lsi_profile->op_table[op]; redirect; redirect = redirect->next) { - /* Currently we only known path replacements */ - if (redirect->type != LSI_REDIRECT_PATH) { - continue; - } - if (strcmp(redirect->path_source, path) != 0) { - continue; - } - if (!lsi_file_exists(redirect->path_target)) { - lsi_log_warn("Replacement path does not exist: %s", redirect->path_target); - return lsi_table.open(p, flags, mode); - } - lsi_log_info("Replaced '%s' with '%s'", path, redirect->path_target); - return lsi_table.open(redirect->path_target, flags, mode); +_nica_public_ FILE *fopen64(const char *p, const char *modes) +{ + LsiRedirectOperation op = LSI_OPERATION_OPEN; + autofree(char) *replacement = NULL; + + /* Must ensure we're **really** initialised, as we might see open happen + * before the constructor.. + */ + lsi_redirect_init_tables(); + + /* Not interested in this guy apparently */ + if (!lsi_override) { + return lsi_table.fopen64(p, modes); } - /* No replacement */ - return lsi_table.open(p, flags, mode); + replacement = lsi_get_redirect_path("fopen64", op, p); + if (!replacement) { + return lsi_table.fopen64(p, modes); + } + return lsi_table.fopen64(replacement, modes); } /* diff --git a/src/redirect/meson.build b/src/redirect/meson.build index be49063..75b531e 100644 --- a/src/redirect/meson.build +++ b/src/redirect/meson.build @@ -9,6 +9,7 @@ if with_libredirect == true 'main.c', 'profile.c', 'profiles/ark.c', + 'profiles/project_highrise.c', ] sym_map = join_paths(meson.current_source_dir(), 'sym.map') diff --git a/src/redirect/profile.h b/src/redirect/profile.h index 73d7d53..21dcf7a 100644 --- a/src/redirect/profile.h +++ b/src/redirect/profile.h @@ -27,6 +27,11 @@ typedef LsiRedirectProfile *(*lsi_profile_generator_func)(char *process_name, ch */ LsiRedirectProfile *lsi_redirect_profile_new_ark(char *process_name, char *steam_root); +/** + * Profile generator for Project Highrise + */ +LsiRedirectProfile *lsi_redirect_profile_new_project_highrise(char *process_name, char *steam_root); + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/src/redirect/profiles/project_highrise.c b/src/redirect/profiles/project_highrise.c new file mode 100644 index 0000000..65d00b5 --- /dev/null +++ b/src/redirect/profiles/project_highrise.c @@ -0,0 +1,117 @@ +/* + * This file is part of linux-steam-integration. + * + * Copyright © 2017 Ikey Doherty + * + * linux-steam-integration is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include "../common/files.h" +#include "../common/log.h" +#include "nica/util.h" +#include "profile.h" +#include "redirect.h" + +#define PHR_PREF_DIR "unity3d/SomaSim/Project Highrise/prefs" +#define PHR_PREF_FILE "unity3d/SomaSim/Project Highrise/prefs/prefs.txt" + +#if UINTPTR_MAX == 0xffffffffffffffff +#define PHR_BINARY "steamapps/common/Project Highrise/Game.x86_64" +#else +#define PHR_BINARY "steamapps/common/Project Highrise/Game.x86" +#endif + +/** + * This generator function is responsible for supporting: Project Highrise + * + * Currently a bug exists in Projec Highrise, where on the second launch, i.e. + * preferences tree exists, it will try to load/map the preferences directory + * as a file, and fails to use the correct *file path* + * + * This is a temporary workaround and we'll let them know what we found. + */ +LsiRedirectProfile *lsi_redirect_profile_new_project_highrise(char *process_name, char *steam_path) +{ + LsiRedirectProfile *p = NULL; + LsiRedirect *redirect = NULL; + autofree(char) *p_source = NULL; + autofree(char) *p_target = NULL; + autofree(char) *match_process = NULL; + autofree(char) *test_process = NULL; + autofree(char) *config_dir = NULL; + + if (asprintf(&match_process, "%s/%s", steam_path, PHR_BINARY) < 0) { + return NULL; + } + + /* Check it even exists */ + test_process = realpath(match_process, NULL); + if (!test_process) { + return NULL; + } + + /* Check the process name now */ + if (strcmp(test_process, process_name) != 0) { + return NULL; + } + + config_dir = lsi_get_user_config_dir(); + if (!config_dir) { + return NULL; + } + + p = lsi_redirect_profile_new("Project Highrise"); + if (!p) { + fputs("OUT OF MEMORY\n", stderr); + return NULL; + } + + if (asprintf(&p_source, "%s/%s", config_dir, PHR_PREF_DIR) < 0) { + goto failed; + } + if (asprintf(&p_target, "%s/%s", config_dir, PHR_PREF_FILE) < 0) { + goto failed; + } + + /* Don't add a redirect if the paths don't exist. */ + redirect = lsi_redirect_new_path_replacement(p_source, p_target); + if (!redirect) { + goto unused; + } + + /* We're in, set our log ID now */ + lsi_log_set_id("ProjectHighrise"); + + lsi_redirect_profile_insert_rule(p, redirect); + + return p; + +failed: + /* Dead in the water.. */ + fputs("OUT OF MEMORY\n", stderr); +unused: + lsi_redirect_profile_free(p); + return NULL; +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=8 tabstop=8 expandtab: + * :indentSize=8:tabSize=8:noTabs=true: + */ diff --git a/src/redirect/sym.map b/src/redirect/sym.map index ea3974f..b8109ff 100644 --- a/src/redirect/sym.map +++ b/src/redirect/sym.map @@ -1,5 +1,6 @@ { global: + fopen64; open; local: *;