diff --git a/src/common/files.c b/src/common/files.c index b145fcf..96bd4eb 100644 --- a/src/common/files.c +++ b/src/common/files.c @@ -11,6 +11,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -20,7 +21,12 @@ #include "common.h" #include "files.h" +#include "log.h" +#include "nica/array.h" #include "nica/util.h" +#include "vdf.h" + +DEF_AUTOFREE(VdfFile, vdf_file_close) bool lsi_file_exists(const char *path) { @@ -98,6 +104,95 @@ char *lsi_get_process_name(void) return strdup(basep); } +/** + * Quickly determine if a string is numeric.. + */ +static inline bool lsi_is_string_numeric(char *p) +{ + char *s; + + for (s = p; *s; s++) { + if (!isdigit(*s)) { + return false; + } + } + + /* Didn't wind, empty string */ + if (s == p) { + return false; + } + return true; +} + +char **lsi_get_steam_paths(void) +{ + autofree(char) *steam_root = NULL; + autofree(char) *lib_conf = NULL; + autofree(VdfFile) *vdf = NULL; + VdfNode *root = NULL; + VdfNode *node = NULL; + NcArray *array = NULL; + + steam_root = lsi_get_steam_dir(); + if (!steam_root) { + return NULL; + } + + array = nc_array_new(); + if (!nc_array_add(array, strdup(steam_root))) { + goto bail; + } + + if (asprintf(&lib_conf, "%s/steamapps/libraryfolders.vdf", steam_root) < 0) { + return NULL; + } + + vdf = vdf_file_open(lib_conf); + if (!vdf) { + goto fallback; + } + + if (!vdf_file_parse(vdf)) { + goto fallback; + } + + /* Look up LibraryFolders under root */ + root = vdf_file_get_root(vdf); + node = vdf_node_get(root, "LibraryFolders", NULL); + if (!node) { + goto fallback; + } + + /* Walk all nodes immediately within LibraryFolders and ignore sections */ + for (VdfNode *n = node->child; n; n = n->sibling) { + if (!n->value) { + continue; + } + if (lsi_is_string_numeric(n->key)) { + if (!nc_array_add(array, strdup(n->value))) { + goto bail; + } + lsi_log_debug("vdf: discovered LibraryFolders: %s", n->value); + } + } + +fallback: + /* Trick nica into storing empty value to so we can terminate the array */ + if (!nc_array_add(array, (void *)(long)1)) { + goto bail; + } + + /* Steal the blob straight out of the array */ + char **data = (char **)array->data; + data[array->len - 1] = NULL; + free(array); + return data; + +bail: + nc_array_free(&array, free); + return NULL; +} + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/src/common/files.h b/src/common/files.h index c575ccd..9f223c1 100644 --- a/src/common/files.h +++ b/src/common/files.h @@ -31,6 +31,11 @@ char *lsi_get_user_config_dir(void); */ char *lsi_get_steam_dir(void); +/** + * Grab all the valid steam installation directories + */ +char **lsi_get_steam_paths(void); + /** * Quick helper to determine if the path exists */ diff --git a/src/redirect/main.c b/src/redirect/main.c index 8c65690..25c44f7 100644 --- a/src/redirect/main.c +++ b/src/redirect/main.c @@ -134,13 +134,12 @@ failed: /** * Testing: Create profile for ARK: Survival Evolved */ -static LsiRedirectProfile *lsi_redirect_profile_new_ark(void) +static LsiRedirectProfile *lsi_redirect_profile_new_ark(char **steam_paths) { LsiRedirectProfile *p = NULL; LsiRedirect *redirect = NULL; #define ARK_BASE "steamapps/common/ARK/ShooterGame/Content" - autofree(char) *steam_dir = NULL; autofree(char) *mic_source = NULL; autofree(char) *mic_target = NULL; static const char *def_mic_source = @@ -155,19 +154,28 @@ static LsiRedirectProfile *lsi_redirect_profile_new_ark(void) return NULL; } - steam_dir = lsi_get_steam_dir(); - if (asprintf(&mic_source, "%s/%s", steam_dir, def_mic_source) < 0) { - goto failed; - } - if (asprintf(&mic_target, "%s/%s", steam_dir, def_mic_target) < 0) { - goto failed; + /* For each steam path, try to create a rule for it */ + while (*steam_paths) { + char *steam_dir = *steam_paths; + + if (asprintf(&mic_source, "%s/%s", steam_dir, def_mic_source) < 0) { + goto failed; + } + if (asprintf(&mic_target, "%s/%s", steam_dir, def_mic_target) < 0) { + goto failed; + } + + /* Don't add a redirect if the paths don't exist. */ + redirect = lsi_redirect_new_path_replacement(mic_source, mic_target); + if (!redirect) { + goto next; + } + + lsi_redirect_profile_insert_rule(p, redirect); + next: + ++steam_paths; } - redirect = lsi_redirect_new_path_replacement(mic_source, mic_target); - if (!redirect) { - goto failed; - } - lsi_redirect_profile_insert_rule(p, redirect); return p; failed: @@ -189,6 +197,7 @@ __attribute__((constructor)) static void lsi_redirect_init(void) /* Ensure we're open. */ lsi_redirect_init_tables(); autofree(char) *process_name = NULL; + char **paths = NULL; process_name = lsi_get_process_name(); @@ -197,19 +206,33 @@ __attribute__((constructor)) static void lsi_redirect_init(void) return; } + /* Grab the stam installation directories */ + paths = lsi_get_steam_paths(); + /* Temporary hack, we'll make this more generic later */ - if (strcmp(process_name, "ShooterGame") == 0) { + if (strcmp(process_name, "ShooterGame") != 0) { lsi_log_set_id("ShooterGame"); - lsi_profile = lsi_redirect_profile_new_ark(); + lsi_profile = lsi_redirect_profile_new_ark(paths); } if (!lsi_profile) { - return; + goto clean_array; } lsi_override = true; - lsi_log_debug("Enable lsi_redirect for '%s'", lsi_profile->name); + +clean_array: + + /* Free them again */ + if (paths) { + char **orig = paths; + while (*paths) { + free(*paths); + ++paths; + } + free(orig); + } } _nica_public_ int open(const char *p, int flags, ...)