From 1017d4731234e6f9dfffa611e68eafdb04920b43 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 10 Oct 2022 21:01:16 +0200 Subject: [PATCH] Allows to show the load order and list plugins. i don't think allowing to edit the load order un the CLI will be easy. maybe it's doable with a TUI ? but if i continue to work with command line arguments it doesn't seem possible to do it cleanly. maybe i can do the same thing i did with the mod order. numbering mods and assking two numbers to swap. but this is shit. i only see 2 way this might go forward. 1. a gtk based ui like the one being developped in the interface branch but i doubt it will ever be finished if i don't intervene at some point. 2. a tui im really curious about tuis like nmtui or "n"(a node version manager). --- CMakeLists.txt | 2 +- src/getDataPath.c | 15 +++++++++-- src/loadOrder.c | 33 +++++++++++++++++++----- src/main.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/steam.h | 2 +- 5 files changed, 107 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9dc092..216a472 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ endif() set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wmaybe-uninitialized") set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address -Werror -O0") -set(CMAKE_C_FLAGS_RELEASE "-O2") +set(CMAKE_C_FLAGS_RELEASE "-g") # generate the compile_commands for vscode / clang set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") diff --git a/src/getDataPath.c b/src/getDataPath.c index a2442b1..4ea3b56 100644 --- a/src/getDataPath.c +++ b/src/getDataPath.c @@ -5,6 +5,8 @@ #include #include +#include +#include error_t getDataPath(int appid, char ** destination) { GHashTable * gamePaths; @@ -15,21 +17,30 @@ error_t getDataPath(int appid, char ** destination) { int gameId = steam_gameIdFromAppId(appid); if(gameId < 0 ) { + fprintf(stderr, "invalid appid"); + return ERR_FAILURE; + } + + const char * path = g_hash_table_lookup(gamePaths, &gameId); + + if(path == NULL) { + fprintf(stderr, "game not found\n"); return ERR_FAILURE; } - const char * path = g_hash_table_lookup(gamePaths, &appid); char * gameFolder = g_build_filename(path, "steamapps/common", GAMES_NAMES[gameId], NULL); //the folder names for older an newer titles char * dataFolderOld = g_build_filename(gameFolder, "Data Files", NULL); char * dataFolderNew = g_build_filename(gameFolder, "Data", NULL); + g_free(gameFolder); *destination = NULL; + //struct stat sb; if(access(dataFolderOld, F_OK) == 0) { *destination = strdup(dataFolderOld); } else if(access(dataFolderNew, F_OK) == 0) { - *destination = strdup(dataFolderOld); + *destination = strdup(dataFolderNew); } g_free(dataFolderNew); diff --git a/src/loadOrder.c b/src/loadOrder.c index 25e536f..d3533b9 100644 --- a/src/loadOrder.c +++ b/src/loadOrder.c @@ -3,6 +3,7 @@ #include "steam.h" #include "file.h" +#include #include #include #include @@ -13,8 +14,6 @@ //TODO: deploy the game error_t order_listPlugins(int appid, GList ** plugins) { - - //save appid parsing //TODO: apply a similar mechanism everywhere size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1; @@ -30,19 +29,32 @@ error_t order_listPlugins(int appid, GList ** plugins) { //esp && esm files are loadable DIR * d = opendir(dataFolder); struct dirent *dir; - if (d) { + if (d != NULL) { while ((dir = readdir(d)) != NULL) { const char * extension = file_extractExtension(dir->d_name); + //folder don't have file extensions + if(extension == NULL) + continue; + if(strcmp(extension, "esp") == 0 || strcmp(extension, "esm") == 0) { *plugins = g_list_append(*plugins, strdup(dir->d_name)); } } + closedir(d); } free(dataFolder); return ERR_SUCCESS; } +static void removeCRLF_CR_LF(char * string) { + int size = strlen(string); + if(string[size - 1] == '\r') size--; + if(string[size - 1] == '\n') size--; + if(string[size - 1] == '\r') size--; + string[size] = '\0'; +} + error_t order_getLoadOrder(int appid, GList ** order) { GHashTable * gamePaths; @@ -66,11 +78,15 @@ error_t order_getLoadOrder(int appid, GList ** order) { char appidStr[appidStrLen]; sprintf(appidStr, "%d", appid); - const char * path = g_hash_table_lookup(gamePaths, &appid); - char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/AppData/Local/", GAMES_NAMES[gameId], "loadorder.txt", NULL); + const char * path = g_hash_table_lookup(gamePaths, &gameId); + + + //this is the path i would use in windows but it seems it is not avaliable in all wine versions. + //char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/AppData/Local/", GAMES_NAMES[gameId], "loadorder.txt", NULL); + char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/Local Settings/Application Data/", GAMES_NAMES[gameId], "loadorder.txt", NULL); GList * l_currentLoadOrder = NULL; - if(access(loadOrderPath, F_OK)) { + if(access(loadOrderPath, R_OK) == 0) { FILE * f_loadOrder = fopen(loadOrderPath, "r"); size_t length = 0; @@ -80,8 +96,11 @@ error_t order_getLoadOrder(int appid, GList ** order) { if(line[0] == '#' || line[0] == '\n') continue; + removeCRLF_CR_LF(line); + l_currentLoadOrder = g_list_append(l_currentLoadOrder, strdup(line)); } + if(line != NULL)free(line); } @@ -125,7 +144,7 @@ error_t order_setLoadOrder(int appid, GList * loadOrder) { char appidStr[appidStrLen]; sprintf(appidStr, "%d", appid); - const char * path = g_hash_table_lookup(gamePaths, &appid); + const char * path = g_hash_table_lookup(gamePaths, &gameId); char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/AppData/Local/", GAMES_NAMES[gameId], "loadorder.txt", NULL); FILE * f_loadOrder = fopen(loadOrderPath, "w"); diff --git a/src/main.c b/src/main.c index b9d7752..bf96014 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,7 @@ #include #include "getDataPath.h" +#include "loadOrder.h" #include "overlayfs.h" #include "install.h" #include "getHome.h" @@ -60,6 +61,9 @@ static int usage() { printf("Use --swap MODID B> to swap two mod in the loading order\n"); printf("Use --version or -v to get the version number\n"); + + printf("Use --show-load-order \n"); + printf("Use --list-plugins \n"); return EXIT_FAILURE; } @@ -543,6 +547,62 @@ static int swapMod(int argc, char ** argv) { return order_swapPlace(appid, modIdA, modIdB); } +static int printLoadOrder(int argc, char ** argv) { + if(argc != 3) return usage(); + + char * appIdStr = argv[2]; + int appid = validateAppId(appIdStr); + if(appid < 0) { + fprintf(stderr, "Invalid appid"); + return EXIT_FAILURE; + } + + GList * order = NULL; + error_t status = order_getLoadOrder(appid, &order); + if(status != ERR_SUCCESS) { + fprintf(stderr, "Could not fetch the load order\n"); + return EXIT_FAILURE; + } + + printf("If the load order is empty that mean it wasn't set\n"); + + GList * cur_order = order; + while (cur_order != NULL) { + printf("%s\n", (char *)cur_order->data); + cur_order = g_list_next(cur_order); + } + + g_list_free_full(order, free); + return EXIT_SUCCESS; +} + +static int printPlugins(int argc, char ** argv) { + if(argc != 3) return usage(); + + char * appIdStr = argv[2]; + int appid = validateAppId(appIdStr); + if(appid < 0) { + fprintf(stderr, "Invalid appid"); + return EXIT_FAILURE; + } + + GList * mods = NULL; + error_t status = order_listPlugins(appid, &mods); + if(status != ERR_SUCCESS) { + fprintf(stderr, "Could not list plugins\n"); + return EXIT_FAILURE; + } + + GList * cur_mods = mods; + while (cur_mods != NULL) { + printf("%s\n", (char *)cur_mods->data); + cur_mods = g_list_next(cur_mods); + } + + g_list_free_full(mods, free); + return EXIT_SUCCESS; +} + int main(int argc, char ** argv) { if(argc < 2 ) return usage(); @@ -608,6 +668,12 @@ int main(int argc, char ** argv) { else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0) returnValue = swapMod(argc, argv); + else if(strcmp(argv[1], "--list-plugins") == 0) + returnValue = printPlugins(argc, argv); + + else if(strcmp(argv[1], "--show-load-order") == 0) + returnValue = printLoadOrder(argc, argv); + else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) { #ifdef __clang__ printf("%s: Clang: %d.%d.%d\n", VERSION, __clang_major__, __clang_minor__, __clang_patchlevel__); diff --git a/src/steam.h b/src/steam.h index af5006b..a2c72b1 100644 --- a/src/steam.h +++ b/src/steam.h @@ -50,7 +50,7 @@ _Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Name * This method has a singleton so after the first execution it will no rescan for new games. * * @param status pointer to a status variable that will be modified to EXIT_SUCCESS or EXIT_FAILURE - * @return GHashTable* a map appid(int) => path(char *) to the corresponding steam library + * @return GHashTable* a map gameId(int) => path(char *) to the corresponding steam library */ error_t steam_searchGames(GHashTable** tablePointer);