redirect: Add new workaround for Project Highrise

In the current build of Project Highrise, after the first start of the
game settings are stored. Once that leading structure exists, the game
is now able to see files/directories that weren't there on the first run.

The issue however is that instead of loading "prefs/prefs.txt" on Linux,
the game is loading "prefs" directory with fopen64() as if it were a file,
and then attempting to map it. Malloc bombs out and the game instantly
dies.

This is very likely down to an invalid escaping/path join in the game that
could trivially be fixed, however we'll carry this workaround for now until
such point as a new build is out.

Signed-off-by: Ikey Doherty <ikey@solus-project.com>
This commit is contained in:
Ikey Doherty
2017-10-23 23:40:30 +01:00
parent ede4e130f4
commit a89de2bb58
5 changed files with 201 additions and 23 deletions
+77 -23
View File
@@ -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);
}
/*
+1
View File
@@ -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')
+5
View File
@@ -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
*
+117
View File
@@ -0,0 +1,117 @@
/*
* This file is part of linux-steam-integration.
*
* Copyright © 2017 Ikey Doherty <ikey@solus-project.com>
*
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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:
*/
+1
View File
@@ -1,5 +1,6 @@
{
global:
fopen64;
open;
local:
*;