From 3534b709bd53d613aa648119320462bb2eb92237 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 4 Oct 2022 14:08:54 +0200 Subject: [PATCH 01/18] allow to change the mod loading order --- src/main.c | 23 +++++++++++++++++++++++ src/order.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- src/order.h | 2 +- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index 31d99e4..545318a 100644 --- a/src/main.c +++ b/src/main.c @@ -62,6 +62,7 @@ int usage() { printf("Use --deploy or -d to deploy the mods for the game\n"); printf("Use --unbind to rollback a deployment\n"); printf("Use --fomod to create a new mod using the result from the FOMod\n"); + printf("Use --swap MODID B> to swap two mod in the loading order\n"); return EXIT_FAILURE; } @@ -489,6 +490,22 @@ int fomod(int argc, char ** argv) { return returnValue; } +int swapMod(int argc, char ** argv) { + if(argc != 5) return usage(); + char * appIdStr = argv[2]; + int appid = validateAppId(appIdStr); + if(appid < 0) { + printf("Invalid appid"); + return EXIT_FAILURE; + } + + + //no mod will go over the MAX_INT value + int modIdA = (int)strtoul(argv[3], NULL, 10); + int modIdB = (int)strtoul(argv[4], NULL, 10); + return swapPlace(appid, modIdA, modIdB); +} + int main(int argc, char ** argv) { if(argc < 2 ) return usage(); @@ -589,6 +606,12 @@ int main(int argc, char ** argv) { else returnValue = removeMod(argc, argv); + } else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0) { + if(isRoot()) + returnValue = noRoot(); + else + returnValue = swapMod(argc, argv); + } else { usage(); returnValue = EXIT_FAILURE; diff --git a/src/order.c b/src/order.c index 0354585..35bd256 100644 --- a/src/order.c +++ b/src/order.c @@ -1,4 +1,5 @@ #include +#include #include "order.h" //no function are declared in main it's just macros @@ -92,9 +93,51 @@ GList * listMods(int appid) { } -void swapPlace(int appid, int modId, int modId2) { - GList * list = listMods(appid); +int swapPlace(int appid, int modIdA, int modIdB) { + char appidStr[10]; + sprintf(appidStr, "%d", appid); + char * home = getHome(); + char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appidStr, NULL); + free(home); + + GList * list = listMods(appid); + GList * listA = list; + GList * listB = list; + + for(int i = 0; i < modIdA; i++) { + listA = g_list_next(listA); + } + + for(int i = 0; i < modIdB; i++) { + listB = g_list_next(listB); + } + + if(listA == NULL || listB == NULL) { + printf("Invalid modId\n"); + return EXIT_FAILURE; + } + + char * modAFolder = g_build_filename(modFolder, listA->data, ORDER_FILE, NULL); + char * modBFolder = g_build_filename(modFolder, listB->data, ORDER_FILE, NULL); + + FILE * fileA = fopen(modAFolder, "w"); + FILE * fileB = fopen(modBFolder, "w"); + + g_free(modAFolder); + g_free(modBFolder); + + if(fileA == NULL || fileB == NULL) { + fprintf(stderr, "Error could not open order file\n"); + return EXIT_FAILURE; + } + + fprintf(fileA, "%d", modIdB); + fprintf(fileB, "%d", modIdA); + + fclose(fileA); + fclose(fileB); g_list_free(list); + return EXIT_SUCCESS; } diff --git a/src/order.h b/src/order.h index 020b04d..33da0fe 100644 --- a/src/order.h +++ b/src/order.h @@ -24,6 +24,6 @@ GList * listMods(int appid); * @param modId * @param modId2 */ -void swapPlace(int appid, int modId, int modId2); +int swapPlace(int appid, int modId, int modId2); #endif From c223d149f41b5f8dd39c6c7a11b915326e12076d Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 4 Oct 2022 14:18:12 +0200 Subject: [PATCH 02/18] Fix unsafe strtoul --- src/main.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index 545318a..dafc485 100644 --- a/src/main.c +++ b/src/main.c @@ -67,14 +67,15 @@ int usage() { } int validateAppId(const char * appIdStr) { + char * strtoulSentinel; //strtoul set EINVAL(after C99) if the string is invalid - unsigned long appid = strtoul(appIdStr, NULL, 10); - if(errno == EINVAL) { + unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10); + if(errno == EINVAL || strtoulSentinel == appIdStr) { printf("Appid has to be a valid number\n"); return -1; } - int gameId = getGameIdFromAppId(appid); + int gameId = getGameIdFromAppId((int)appid); if(gameId < 0) { printf("Game is not compatible\n"); return -1; @@ -500,9 +501,20 @@ int swapMod(int argc, char ** argv) { } + char * sentinel; //no mod will go over the MAX_INT value - int modIdA = (int)strtoul(argv[3], NULL, 10); - int modIdB = (int)strtoul(argv[4], NULL, 10); + int modIdA = (int)strtoul(argv[3], &sentinel, 10); + if(errno == EINVAL || sentinel == argv[3]) { + printf("Modid A has to be a valid number\n"); + return -1; + } + int modIdB = (int)strtoul(argv[4], &sentinel, 10); + if(errno == EINVAL || sentinel == argv[4]) { + printf("Modid B has to be a valid number\n"); + return -1; + } + + printf("%d, %d\n", modIdA, modIdB); return swapPlace(appid, modIdA, modIdB); } From c276e18282951d1550d3d56fe186d47e6a6d4f2d Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 4 Oct 2022 14:21:58 +0200 Subject: [PATCH 03/18] properly setting the C version --- CMakeLists.txt | 2 ++ src/main.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc7cbbc..bdcb15c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,3 +41,5 @@ target_link_libraries(ModManager ${LIBXML_LIBRARIES}) target_link_libraries(ModManager ${AUDIT_LIBRARIES}) install(TARGETS ModManager DESTINATION bin) + +set_property(TARGET ModManager PROPERTY C_STANDARD 23) diff --git a/src/main.c b/src/main.c index dafc485..e77a4bc 100644 --- a/src/main.c +++ b/src/main.c @@ -90,7 +90,7 @@ int validateAppId(const char * appIdStr) { return (int)appid; } -int listGames(int argc, char ** argv) { +int listGames(int argc, char **) { if(argc != 2) return usage(); GList * gamesIds = g_hash_table_get_keys(gamePaths); GList * gamesIdsFirstPointer = gamesIds; From f8c5020a877b663dfc4267c4a94df906bb181e78 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 4 Oct 2022 16:25:44 +0200 Subject: [PATCH 04/18] I discovered the static keyword --- CMakeLists.txt | 10 +++++++--- src/file.c | 14 +++++++------- src/fomod.c | 20 ++++++++++---------- src/fomod/group.c | 10 +++++----- src/fomod/parser.c | 12 ++++++------ src/install.c | 13 ++++++------- src/main.c | 32 ++++++++++++++++---------------- src/order.c | 2 +- src/steam.c | 6 +++--- 9 files changed, 61 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bdcb15c..21c78dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,12 @@ cmake_minimum_required(VERSION 3.10) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -Werror -fsanitize=address") +set(CMAKE_CXX_FLAGS_DEBUG "-g") +set(CMAKE_CXX_FLAGS_RELEASE "-O2") # generate the compile_commands for vscode / clang set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") @@ -24,8 +31,6 @@ set(SOURCES # add the executable add_executable(ModManager src/main.c ${SOURCES}) -add_compile_options(-Wall -Wextra -pedantic -Werror) - find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED glib-2.0) pkg_search_module(AUDIT REQUIRED audit) @@ -33,7 +38,6 @@ pkg_search_module(LIBXML REQUIRED libxml-2.0) target_include_directories(ModManager PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${AUDIT_INCLUDE_DIRS}) -add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) target_link_libraries(ModManager ${GLIB_LDFLAGS}) diff --git a/src/file.c b/src/file.c index b4439a7..b7f5348 100644 --- a/src/file.c +++ b/src/file.c @@ -8,13 +8,13 @@ #include #include "file.h" -u_int32_t countSetBits(u_int32_t n) { - // base case - if (n == 0) - return 0; - else - // if last bit set add 1 else add 0 - return (n & 1) + countSetBits(n >> 1); +static u_int32_t countSetBits(u_int32_t n) { + // base case + if (n == 0) + return 0; + else + // if last bit set add 1 else add 0 + return (n & 1) + countSetBits(n >> 1); } //TODO: add interruption support diff --git a/src/fomod.c b/src/fomod.c index 3d5c2e8..b784fe0 100644 --- a/src/fomod.c +++ b/src/fomod.c @@ -14,7 +14,7 @@ #include "file.h" #include "libxml/globals.h" -int getInputCount(const char * input) { +static int getInputCount(const char * input) { char buff[2]; buff[1] = '\0'; @@ -40,21 +40,21 @@ int getInputCount(const char * input) { return elementCount; } -gint priorityCmp(gconstpointer a, gconstpointer b) { +static gint priorityCmp(gconstpointer a, gconstpointer b) { const FOModFile_t * fileA = (const FOModFile_t *)a; const FOModFile_t * fileB = (const FOModFile_t *)b; return fileA->priority - fileB->priority; } -void printfOptionsInOrder(FOModGroup_t group) { +static void printfOptionsInOrder(FOModGroup_t group) { for(int i = 0; i < group.pluginCount; i++) { printf("%d, %s\n", i, group.plugins[i].name); printf("%s\n", group.plugins[i].description); } } -gint flagEqual(const FOModFlag_t * a, const FOModFlag_t * b) { +static gint flagEqual(const FOModFlag_t * a, const FOModFlag_t * b) { int nameCmp = strcmp(a->name, b->name); if(nameCmp == 0) { if(strcmp(a->value, b->value) == 0) @@ -66,20 +66,20 @@ gint flagEqual(const FOModFlag_t * a, const FOModFlag_t * b) { return nameCmp; } -int stepCmpAsc(const void * stepA, const void * stepB) { +static int stepCmpAsc(const void * stepA, const void * stepB) { const FOModStep_t * step1 = (const FOModStep_t *)stepA; const FOModStep_t * step2 = (const FOModStep_t *)stepB; return strcmp(step1->name, step2->name); } -int stepCmpDesc(const void * stepA, const void * stepB) { +static int stepCmpDesc(const void * stepA, const void * stepB) { const FOModStep_t * step1 = (const FOModStep_t *)stepA; const FOModStep_t * step2 = (const FOModStep_t *)stepB; return 1 - strcmp(step1->name, step2->name); } -void sortSteps(FOMod_t * fomod) { +static void sortSteps(FOMod_t * fomod) { switch(fomod->stepOrder) { case ASC: qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpAsc); @@ -93,19 +93,19 @@ void sortSteps(FOMod_t * fomod) { } } -int groupCmpAsc(const void * stepA, const void * stepB) { +static int groupCmpAsc(const void * stepA, const void * stepB) { const FOModGroup_t * step1 = (const FOModGroup_t *)stepA; const FOModGroup_t * step2 = (const FOModGroup_t *)stepB; return strcmp(step1->name, step2->name); } -int groupCmpDesc(const void * stepA, const void * stepB) { +static int groupCmpDesc(const void * stepA, const void * stepB) { const FOModGroup_t * step1 = (const FOModGroup_t *)stepA; const FOModGroup_t * step2 = (const FOModGroup_t *)stepB; return 1 - strcmp(step1->name, step2->name); } -void sortGroup(FOModGroup_t * group) { +static void sortGroup(FOModGroup_t * group) { switch(group->order) { case ASC: qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpAsc); diff --git a/src/fomod/group.c b/src/fomod/group.c index 470153a..87cc98a 100644 --- a/src/fomod/group.c +++ b/src/fomod/group.c @@ -3,7 +3,7 @@ #include "string.h" #include -GroupType_t getGroupType(const char * type) { +static GroupType_t getGroupType(const char * type) { if(strcmp(type, "SelectAtLeastOne") == 0) { return AT_LEAST_ONE; } else if(strcmp(type, "SelectAtMostOne") == 0) { @@ -19,7 +19,7 @@ GroupType_t getGroupType(const char * type) { } } -TypeDescriptor_t getDescriptor(const char * descriptor) { +static TypeDescriptor_t getDescriptor(const char * descriptor) { if(strcmp(descriptor, "Optional") == 0) { return OPTIONAL; } else if(strcmp(descriptor, "Required") == 0) { @@ -64,7 +64,7 @@ void freeGroup(FOModGroup_t * group) { group->pluginCount = 0; } -int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { +static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { xmlNodePtr flagNode = nodeElement->children; while(flagNode != NULL) { if(!validateNode(&flagNode, true, "flag", NULL)) { @@ -89,7 +89,7 @@ int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { return EXIT_SUCCESS; } -int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { +static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { FOModFile_t * files = NULL; xmlNodePtr fileNode = nodeElement->children; while(fileNode != NULL) { @@ -123,7 +123,7 @@ int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { return EXIT_SUCCESS; } -int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { +static int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { if(xmlStrcmp(nodeElement->name, (const xmlChar *) "description") == 0) { plugin->description = freeAndDup(xmlNodeGetContent(nodeElement)); } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "image") == 0) { diff --git a/src/fomod/parser.c b/src/fomod/parser.c index fe8c437..78dd2a8 100644 --- a/src/fomod/parser.c +++ b/src/fomod/parser.c @@ -51,7 +51,7 @@ void freeFOMod(FOMod_t * fomod) { memset(fomod, 0, sizeof(FOMod_t)); } -int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) { +static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) { xmlNodePtr requiredFlagsNode = node->children; while (requiredFlagsNode != NULL) { @@ -76,7 +76,7 @@ int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) { return EXIT_SUCCESS; } -int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) { +static int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) { xmlChar * optionOrder = xmlGetProp(node, (const xmlChar *)"order"); step->optionOrder = getFOModOrder((char *)optionOrder); xmlFree(optionOrder); @@ -105,7 +105,7 @@ int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) { return EXIT_SUCCESS; } -FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) { +static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) { FOModStep_t * steps = NULL; *stepCount = 0; @@ -146,7 +146,7 @@ FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) { return steps; } -int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) { +static int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) { xmlNodePtr flagNode = node->children; if(!validateNode(&flagNode, true, "flagDependency", NULL)) { @@ -167,7 +167,7 @@ int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) { return EXIT_SUCCESS; } -int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) { +static int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) { xmlNodePtr filesNode = node->children; @@ -192,7 +192,7 @@ int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) { return EXIT_SUCCESS; } -int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { +static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { xmlNodePtr patterns = node->children; if(patterns != NULL) { if(!validateNode(&patterns, true, "patterns", NULL)) { diff --git a/src/install.c b/src/install.c index f212610..7578c83 100644 --- a/src/install.c +++ b/src/install.c @@ -9,7 +9,7 @@ #include "main.h" #include "file.h" -int unzip(char * path, char * outdir) { +static int unzip(char * path, char * outdir) { char * const args[] = { "unzip", "-LL", // to lowercase @@ -37,7 +37,7 @@ int unzip(char * path, char * outdir) { } } -int unrar(char * path, char * outdir) { +static int unrar(char * path, char * outdir) { char * const args[] = { "unrar", "x", @@ -66,7 +66,7 @@ int unrar(char * path, char * outdir) { } -int un7z(char * path, const char * outdir) { +static int un7z(char * path, const char * outdir) { gchar * outParameter = g_strjoin("", "-o", outdir, NULL); char * const args[] = { @@ -98,7 +98,7 @@ int un7z(char * path, const char * outdir) { } } -const char * extractLastPart(const char * filePath, const char delimeter) { +static const char * extractLastPart(const char * filePath, const char delimeter) { const unsigned long length = strlen(filePath); long index = -1; for(long i= length - 1; i >= 0; i--) { @@ -112,15 +112,14 @@ const char * extractLastPart(const char * filePath, const char delimeter) { return &filePath[index]; } -const char * extractExtension(const char * filePath) { +static const char * extractExtension(const char * filePath) { return extractLastPart(filePath, '.'); } -const char * extractFileName(const char * filePath) { +static const char * extractFileName(const char * filePath) { return extractLastPart(filePath, '/'); } - int addMod(char * filePath, int appId) { int returnValue = EXIT_SUCCESS; diff --git a/src/main.c b/src/main.c index e77a4bc..12122a4 100644 --- a/src/main.c +++ b/src/main.c @@ -20,11 +20,11 @@ GHashTable * gamePaths; -bool isRoot() { +static bool isRoot() { return getuid() == 0; } -GList * listFilesInFolder(const char * path) { +static GList * listFilesInFolder(const char * path) { GList * list = NULL; DIR *d; struct dirent *dir; @@ -42,17 +42,17 @@ GList * listFilesInFolder(const char * path) { return list; } -int noRoot() { +static int noRoot() { printf("Don't run this argument as root\n"); return EXIT_FAILURE; } -int needRoot() { +static int needRoot() { printf("Root is needed to bind with the game files\n"); return EXIT_FAILURE; } -int usage() { +static int usage() { printf("Use --list-games or -l to list compatible games\n"); printf("Use --add or -a to add a mod to a game\n"); printf("Use --list-mods or -m to list all mods for a game\n"); @@ -66,7 +66,7 @@ int usage() { return EXIT_FAILURE; } -int validateAppId(const char * appIdStr) { +static int validateAppId(const char * appIdStr) { char * strtoulSentinel; //strtoul set EINVAL(after C99) if the string is invalid unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10); @@ -90,7 +90,7 @@ int validateAppId(const char * appIdStr) { return (int)appid; } -int listGames(int argc, char **) { +static int listGames(int argc, char **) { if(argc != 2) return usage(); GList * gamesIds = g_hash_table_get_keys(gamePaths); GList * gamesIdsFirstPointer = gamesIds; @@ -107,7 +107,7 @@ int listGames(int argc, char **) { return EXIT_SUCCESS; } -int add(int argc, char ** argv) { +static int add(int argc, char ** argv) { if(argc != 4) return usage(); const char * appIdStr = argv[2]; @@ -120,7 +120,7 @@ int add(int argc, char ** argv) { return EXIT_SUCCESS; } -int listAllMods(int argc, char ** argv) { +static int listAllMods(int argc, char ** argv) { if(argc != 3) return usage(); char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); @@ -163,7 +163,7 @@ int listAllMods(int argc, char ** argv) { * this will just flag the mod as needed to be deployed * it's the deploying process that handle the rest */ -int installAndUninstallMod(int argc, char ** argv, bool install) { +static int installAndUninstallMod(int argc, char ** argv, bool install) { if(argc != 4) return usage(); char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); @@ -238,7 +238,7 @@ exit: return returnValue; } -int deploy(int argc, char ** argv) { +static int deploy(int argc, char ** argv) { if(argc != 3) return usage(); char * appIdStr = argv[2]; @@ -331,7 +331,7 @@ int deploy(int argc, char ** argv) { return EXIT_SUCCESS; } -int setup(int argc, char ** argv) { +static int setup(int argc, char ** argv) { if(argc != 3 ) return usage(); const char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); @@ -381,7 +381,7 @@ int setup(int argc, char ** argv) { } -int unbind(int argc, char ** argv) { +static int unbind(int argc, char ** argv) { if(argc != 3 ) return usage(); const char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); @@ -398,7 +398,7 @@ int unbind(int argc, char ** argv) { return EXIT_SUCCESS; } -int removeMod(int argc, char ** argv) { +static int removeMod(int argc, char ** argv) { if(argc != 4) return usage(); const char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); @@ -440,7 +440,7 @@ int removeMod(int argc, char ** argv) { return EXIT_SUCCESS; } -int fomod(int argc, char ** argv) { +static int fomod(int argc, char ** argv) { if(argc != 4) return usage(); char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); @@ -491,7 +491,7 @@ int fomod(int argc, char ** argv) { return returnValue; } -int swapMod(int argc, char ** argv) { +static int swapMod(int argc, char ** argv) { if(argc != 5) return usage(); char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); diff --git a/src/order.c b/src/order.c index 35bd256..7a048a2 100644 --- a/src/order.c +++ b/src/order.c @@ -12,7 +12,7 @@ typedef struct Mod { char * name; } Mod_t; -gint compareOrder(const void * a, const void * b) { +static gint compareOrder(const void * a, const void * b) { const Mod_t * ModA = a; const Mod_t * ModB = b; diff --git a/src/steam.c b/src/steam.c index ee77166..2d3803e 100644 --- a/src/steam.c +++ b/src/steam.c @@ -12,7 +12,7 @@ enum FieldIds { FIELD_PATH, FIELD_LABEL, FIELD_CONTENT_ID, FIELD_TOTAL_SIZE, FIELD_CLEAN_BYTES, FIELD_CORRUPTION, FIELD_APPS }; -int getFiledId(const char * field) { +static int getFiledId(const char * field) { //replace with a hash + switch if(strcmp(field, "path") == 0) { return FIELD_PATH; @@ -34,7 +34,7 @@ int getFiledId(const char * field) { } } -ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) { +static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) { FILE * fd = fopen(path, "r"); char * line = NULL; size_t len = 0; @@ -166,7 +166,7 @@ exit: return libraries; } -void freeLibraries(ValveLibraries_t * libraries, int size) { +static void freeLibraries(ValveLibraries_t * libraries, int size) { for(int i = 0; i < size; i++) { free(libraries[i].path); free(libraries[i].label); From 561a2a90b88604e6d53db6509cd2d9b657b549c2 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 5 Oct 2022 08:35:10 +0200 Subject: [PATCH 05/18] Made errors output to stderr --- src/file.c | 4 ++-- src/fomod.c | 1 - src/install.c | 22 ++++++++---------- src/main.c | 63 ++++++++++++++++++++++++++++----------------------- src/order.c | 2 +- src/steam.c | 5 ++-- 6 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/file.c b/src/file.c index b7f5348..01cf769 100644 --- a/src/file.c +++ b/src/file.c @@ -22,7 +22,7 @@ static u_int32_t countSetBits(u_int32_t n) { int copy(const char * path, const char * dest, u_int32_t flags) { int flagCount = countSetBits(flags); if(flagCount > 3) { - printf("Invalid flags for cp command\n"); + fprintf(stderr, "Invalid flags for cp command\n"); return -1; } //flags + cp + path + dest @@ -105,7 +105,7 @@ void casefold(const char * folder) { int result = move(file, destination); if(result != EXIT_SUCCESS) { - printf("Move failed: %s => %s \n", dir->d_name, destinationName); + fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName); } g_free(destinationName); g_free(destination); diff --git a/src/fomod.c b/src/fomod.c index b784fe0..9744847 100644 --- a/src/fomod.c +++ b/src/fomod.c @@ -142,7 +142,6 @@ int processFileOperations(GList * pendingFileOperations, const char * modFolder, if(copyResult != EXIT_SUCCESS) { fprintf(stderr, "Copy failed, some file might be corrupted\n"); } - printf("source: %s, destination: %s\n", source, destination); g_free(source); currentFileOperation = g_list_next(currentFileOperation); diff --git a/src/install.c b/src/install.c index 7578c83..f7d1a7b 100644 --- a/src/install.c +++ b/src/install.c @@ -30,10 +30,9 @@ static int unzip(char * path, char * outdir) { waitpid(pid, &returnValue, 0); if(returnValue != 0) { - printf("\nFailed to decompress archive\n"); - returnValue = EXIT_FAILURE; + fprintf(stderr, "\nFailed to decompress archive\n"); } - return EXIT_SUCCESS; + return returnValue; } } @@ -58,10 +57,9 @@ static int unrar(char * path, char * outdir) { waitpid(pid, &returnValue, 0); if(returnValue != 0) { - printf("\nFailed to decompress archive\n"); - returnValue = EXIT_FAILURE; + fprintf(stderr, "\nFailed to decompress archive\n"); } - return EXIT_SUCCESS; + return returnValue; } } @@ -89,12 +87,12 @@ static int un7z(char * path, const char * outdir) { waitpid(pid, &returnValue, 0); if(returnValue != 0) { - printf("\nFailed to decompress archive\n"); - returnValue = EXIT_FAILURE; + fprintf(stderr, "\nFailed to decompress archive\n"); + return returnValue; } //make everything lowercase since 7z don't have an argument for that. casefold(outdir); - return EXIT_SUCCESS; + return returnValue; } } @@ -124,14 +122,14 @@ int addMod(char * filePath, int appId) { int returnValue = EXIT_SUCCESS; if (access(filePath, F_OK) != 0) { - printf("File not found\n"); + fprintf(stderr, "File not found\n"); returnValue = EXIT_FAILURE; goto exit; } char * configFolder = g_build_filename(getenv("HOME"), MANAGER_FILES, NULL); if(g_mkdir_with_parents(configFolder, 0755) < 0) { - printf("Could not create mod folder"); + fprintf(stderr, "Could not create mod folder"); returnValue = EXIT_FAILURE; goto exit2; } @@ -153,7 +151,7 @@ int addMod(char * filePath, int appId) { } else if (strcmp(lowercaseExtension, "7z") == 0) { returnValue = un7z(filePath, outdir); } else { - printf("Unsupported format only zip/7z/rar are supported\n"); + fprintf(stderr, "Unsupported format only zip/7z/rar are supported\n"); returnValue = EXIT_FAILURE; } diff --git a/src/main.c b/src/main.c index 12122a4..c1e432b 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,9 @@ #include "fomod.h" #include "order.h" +#define ENABLED_COLOR "\033[0;32m" +#define DISABLED_COLOR "\033[0;31m" + GHashTable * gamePaths; static bool isRoot() { @@ -43,12 +46,12 @@ static GList * listFilesInFolder(const char * path) { } static int noRoot() { - printf("Don't run this argument as root\n"); + fprintf(stderr, "Don't run this argument as root\n"); return EXIT_FAILURE; } static int needRoot() { - printf("Root is needed to bind with the game files\n"); + fprintf(stderr, "Root is needed to bind with the game files\n"); return EXIT_FAILURE; } @@ -71,18 +74,18 @@ static int validateAppId(const char * appIdStr) { //strtoul set EINVAL(after C99) if the string is invalid unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10); if(errno == EINVAL || strtoulSentinel == appIdStr) { - printf("Appid has to be a valid number\n"); + fprintf(stderr, "Appid has to be a valid number\n"); return -1; } int gameId = getGameIdFromAppId((int)appid); if(gameId < 0) { - printf("Game is not compatible\n"); + fprintf(stderr, "Game is not compatible\n"); return -1; } if(!g_hash_table_contains(gamePaths, &gameId)) { - printf("Game not found\n"); + fprintf(stderr, "Game not found\n"); return -1; } @@ -142,9 +145,9 @@ static int listAllMods(int argc, char ** argv) { char * modPath = g_build_filename(modFolder, modName, INSTALLED_FLAG_FILE, NULL); if(access(modPath, F_OK) == 0) { - printf("\033[0;32m %d | ✓ | %s\n", index, modName); + printf(ENABLED_COLOR " %d | ✓ | %s\n", index, modName); } else { - printf("\033[0;31m %d | ✕ | %s\n", index, modName); + printf(DISABLED_COLOR " %d | ✕ | %s\n", index, modName); } index++; @@ -176,7 +179,7 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) { //strtoul set EINVAL if the string is invalid unsigned long modId = strtoul(argv[3], NULL, 10); if(errno == EINVAL) { - printf("ModId has to be a valid number\n"); + fprintf(stderr, "ModId has to be a valid number\n"); return -1; } @@ -192,7 +195,7 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) { } if(mods == NULL) { - printf("Mod not found\n"); + fprintf(stderr, "Mod not found\n"); return EXIT_FAILURE; } @@ -201,7 +204,7 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) { if(install) { if(access(modFlag, F_OK) == 0) { - printf("The mod is already activated \n"); + fprintf(stderr, "The mod is already activated \n"); returnValue = EXIT_SUCCESS; goto exit; } @@ -223,8 +226,8 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) { } if(unlink(modFlag) != 0) { - printf("Error could not disable the mod.\n"); - printf("please remove this file '%s' as root\n", modFlag); + fprintf(stderr, "Error could not disable the mod.\n"); + fprintf(stderr, "please remove this file '%s'\n", modFlag); returnValue = EXIT_FAILURE; goto exit; } @@ -251,7 +254,10 @@ static int deploy(int argc, char ** argv) { char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL); if(access(gameFolder, F_OK) != 0) { + free(home); + g_free(gameFolder); printf("Please run '%s --setup %d' first", APP_NAME, appid); + return EXIT_FAILURE; } //unmount everything beforhand @@ -314,13 +320,13 @@ static int deploy(int argc, char ** argv) { if(status == 0) { printf("Everything is ready, just launch the game\n"); } else if(status == -1) { - printf("Could not mount the mods overlay, try to install fuse-overlay\n"); + fprintf(stderr, "Could not mount the mods overlay, try to install fuse-overlay\n"); return EXIT_FAILURE; } else if(status == -2) { - printf("Could not mount the mods overlay\n"); + fprintf(stderr, "Could not mount the mods overlay\n"); return EXIT_FAILURE; } else { - printf("%d take a screenshot and go insult the dev that let this passthrough\n", __LINE__); + fprintf(stderr, "%d bug detected, please report this.\n", __LINE__); return EXIT_FAILURE; } @@ -403,14 +409,14 @@ static int removeMod(int argc, char ** argv) { const char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); if(appid < 0) { - printf("Invalid appid"); + fprintf(stderr, "Invalid appid"); return EXIT_FAILURE; } //strtoul set EINVAL if the string is invalid unsigned long modId = strtoul(argv[3], NULL, 10); if(errno == EINVAL) { - printf("Modid has to be a valid number\n"); + fprintf(stderr, "Modid has to be a valid number\n"); return EXIT_FAILURE; } @@ -426,7 +432,7 @@ static int removeMod(int argc, char ** argv) { } if(mods == NULL) { - printf("Mod not found\n"); + fprintf(stderr, "Mod not found\n"); return EXIT_FAILURE; } @@ -445,14 +451,14 @@ static int fomod(int argc, char ** argv) { char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); if(appid < 0) { - printf("Invalid appid"); + fprintf(stderr, "Invalid appid"); return EXIT_FAILURE; } //strtoul set EINVAL if the string is invalid unsigned long modId = strtoul(argv[3], NULL, 10); if(errno == EINVAL) { - printf("Modid has to be a valid number\n"); + fprintf(stderr, "Modid has to be a valid number\n"); return -1; } @@ -468,7 +474,7 @@ static int fomod(int argc, char ** argv) { } if(mods == NULL) { - printf("Mod not found\n"); + fprintf(stderr, "Mod not found\n"); return EXIT_FAILURE; } @@ -496,7 +502,7 @@ static int swapMod(int argc, char ** argv) { char * appIdStr = argv[2]; int appid = validateAppId(appIdStr); if(appid < 0) { - printf("Invalid appid"); + fprintf(stderr, "Invalid appid"); return EXIT_FAILURE; } @@ -505,12 +511,12 @@ static int swapMod(int argc, char ** argv) { //no mod will go over the MAX_INT value int modIdA = (int)strtoul(argv[3], &sentinel, 10); if(errno == EINVAL || sentinel == argv[3]) { - printf("Modid A has to be a valid number\n"); + fprintf(stderr, "Modid A has to be a valid number\n"); return -1; } int modIdB = (int)strtoul(argv[4], &sentinel, 10); if(errno == EINVAL || sentinel == argv[4]) { - printf("Modid B has to be a valid number\n"); + fprintf(stderr, "Modid B has to be a valid number\n"); return -1; } @@ -522,7 +528,7 @@ int main(int argc, char ** argv) { if(argc < 2 ) return usage(); if(audit_getloginuid() == 0) { - printf("Root user should not be using this\n"); + fprintf(stderr, "The root user should not be using this\n"); return EXIT_FAILURE; } @@ -532,7 +538,7 @@ int main(int argc, char ** argv) { int returnValue = EXIT_SUCCESS; if(searchStatus == EXIT_FAILURE) { returnValue = EXIT_FAILURE; - printf("Error while looking up libraries, do you have steam installed ?"); + fprintf(stderr, "Error while looking up libraries, do you have steam installed ?"); goto exit; } @@ -546,14 +552,15 @@ int main(int argc, char ** argv) { //such as problem with access rights and taking the risk of //running system as root (which is a big security issue) if(getuid() == 0) { - printf("For the first please run without sudo\n"); + fprintf(stderr, "For the first run please avoid sudo\n"); returnValue = EXIT_FAILURE; goto exit2; } else { //leading 0 == octal int i = g_mkdir_with_parents(configFolder, 0755); if(i < 0) { - printf("Could not create configs, check access rights for this path: %s", configFolder); + fprintf(stderr, "Could not create configs, check access rights for this path: %s", configFolder); + goto exit2; } } } diff --git a/src/order.c b/src/order.c index 7a048a2..ca9a163 100644 --- a/src/order.c +++ b/src/order.c @@ -114,7 +114,7 @@ int swapPlace(int appid, int modIdA, int modIdB) { } if(listA == NULL || listB == NULL) { - printf("Invalid modId\n"); + fprintf(stderr, "Invalid modId\n"); return EXIT_FAILURE; } diff --git a/src/steam.c b/src/steam.c index 2d3803e..92ebf92 100644 --- a/src/steam.c +++ b/src/steam.c @@ -29,7 +29,7 @@ static int getFiledId(const char * field) { } else if(strcmp(field, "apps") == 0) { return FIELD_APPS; } else { - printf("unknown field"); + fprintf(stderr, "unknown field"); return -1; } } @@ -140,7 +140,8 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu break; case '}': if(inQuotes) { - printf("Syntax error in VDF\n"); + fprintf(stderr, "Syntax error in VDF\n"); + //TODO: fix this leak free(libraries); libraries = NULL; *status = EXIT_FAILURE; From 635b1adcaf10044517ddc14a0c5ea3d2a299ce2e Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 5 Oct 2022 13:55:04 +0200 Subject: [PATCH 06/18] [Untested] fixed the compiler/linter options and fixed the corresponding code. the code fixes have not been tested. some are trivial orthers might be a bit worrying. --- CMakeLists.txt | 6 +++--- src/file.c | 10 ++++++---- src/fomod.c | 6 +++--- src/fomod/group.c | 1 - src/fomod/parser.c | 4 ++-- src/getHome.c | 3 ++- src/getHome.h | 2 +- src/install.c | 2 +- src/main.c | 6 +++--- src/steam.c | 18 +++++++++++++----- src/steam.h | 17 ++++++----------- 11 files changed, 40 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21c78dd..8deda90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,9 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -Werror -fsanitize=address") -set(CMAKE_CXX_FLAGS_DEBUG "-g") -set(CMAKE_CXX_FLAGS_RELEASE "-O2") +set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -fsanitize=address -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wno-pointer-arith") +set(CMAKE_C_FLAGS_DEBUG "-g -Werror") +set(CMAKE_C_FLAGS_RELEASE "-O2") # generate the compile_commands for vscode / clang set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") diff --git a/src/file.c b/src/file.c index 01cf769..2f0177d 100644 --- a/src/file.c +++ b/src/file.c @@ -26,10 +26,12 @@ int copy(const char * path, const char * dest, u_int32_t flags) { return -1; } //flags + cp + path + dest - const char * args[flagCount + 4]; + char * args[flagCount + 4]; args[0] = "/bin/cp"; - args[1] = path; - args[2] = dest; + args[1] = alloca((strlen(path) + 1) * sizeof(char)); + strcpy(args[1], path); + args[2] = alloca((strlen(dest) + 1) * sizeof(char)); + strcpy(args[2], dest); int argIndex = 3; if(flags & CP_LINK) { @@ -50,7 +52,7 @@ int copy(const char * path, const char * dest, u_int32_t flags) { int pid = fork(); if(pid == 0) { //discard the const. since we are in a fork we don't care. - execv("/bin/cp", (char **)args); + execv("/bin/cp", args); return 0; } else { int returnValue; diff --git a/src/fomod.c b/src/fomod.c index 9744847..51c4bab 100644 --- a/src/fomod.c +++ b/src/fomod.c @@ -21,7 +21,7 @@ static int getInputCount(const char * input) { int elementCount = 0; bool prevWasValue = false; - for(int i = 0; input + i != NULL && input[i] != '\0' && input[i] != '\n'; i++) { + for(int i = 0; input[i] != '\0' && input[i] != '\n'; i++) { buff[0] = input[i]; //if the character is a valid character @@ -156,7 +156,7 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin bool areAllFlagsValid = true; //checking if all flags are valid - for(int flagId = 0; flagId < condFile->flagCount; flagId++) { + for(long flagId = 0; flagId < condFile->flagCount; flagId++) { const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)flagEqual); if(link == NULL) { areAllFlagsValid = false; @@ -165,7 +165,7 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin } if(areAllFlagsValid) { - for(int fileId = 0; fileId < condFile->flagCount; fileId++) { + for(long fileId = 0; fileId < condFile->flagCount; fileId++) { const FOModFile_t * file = &(condFile->files[fileId]); FOModFile_t * fileCopy = malloc(sizeof(*file)); diff --git a/src/fomod/group.c b/src/fomod/group.c index 87cc98a..60d434d 100644 --- a/src/fomod/group.c +++ b/src/fomod/group.c @@ -90,7 +90,6 @@ static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { } static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { - FOModFile_t * files = NULL; xmlNodePtr fileNode = nodeElement->children; while(fileNode != NULL) { if(!validateNode(&fileNode, true, "folder", "file", NULL)) { diff --git a/src/fomod/parser.c b/src/fomod/parser.c index 78dd2a8..3703cc8 100644 --- a/src/fomod/parser.c +++ b/src/fomod/parser.c @@ -8,12 +8,12 @@ void freeFOMod(FOMod_t * fomod) { for(int i = 0; i < fomod->condFilesCount; i++) { FOModCondFile_t * condFile = &(fomod->condFiles[i]); - for(int fileId = 0; fileId < condFile->fileCount; fileId++) { + for(long fileId = 0; fileId < condFile->fileCount; fileId++) { free(condFile->files[fileId].destination); free(condFile->files[fileId].source); } - for(int flagId = 0; flagId < condFile->flagCount; flagId++) { + for(long flagId = 0; flagId < condFile->flagCount; flagId++) { FOModFlag_t * flag = &(condFile->requiredFlags[flagId]); free(flag->name); free(flag->value); diff --git a/src/getHome.c b/src/getHome.c index e294d51..3025efe 100644 --- a/src/getHome.c +++ b/src/getHome.c @@ -1,8 +1,9 @@ #include #include #include +#include "getHome.h" -char * getHome() { +char * getHome(void) { //not getting home from the env enable us to use sudo struct passwd *pw = getpwuid(audit_getloginuid()); return strdup(pw->pw_dir); diff --git a/src/getHome.h b/src/getHome.h index 141b34c..9ce8b04 100644 --- a/src/getHome.h +++ b/src/getHome.h @@ -5,4 +5,4 @@ * * @return char* path to the home dir */ -char * getHome(); +char * getHome(void); diff --git a/src/install.c b/src/install.c index f7d1a7b..a3d9c3f 100644 --- a/src/install.c +++ b/src/install.c @@ -97,7 +97,7 @@ static int un7z(char * path, const char * outdir) { } static const char * extractLastPart(const char * filePath, const char delimeter) { - const unsigned long length = strlen(filePath); + const int length = strlen(filePath); long index = -1; for(long i= length - 1; i >= 0; i--) { if(filePath[i] == delimeter) { diff --git a/src/main.c b/src/main.c index c1e432b..59b014e 100644 --- a/src/main.c +++ b/src/main.c @@ -190,7 +190,7 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) { GList * mods = listFilesInFolder(modFolder); GList * modsFirstPointer = mods; - for(int i = 0; i < modId; i++) { + for(unsigned long i = 0; i < modId; i++) { mods = g_list_next(mods); } @@ -427,7 +427,7 @@ static int removeMod(int argc, char ** argv) { GList * mods = listFilesInFolder(modFolder); GList * modsFirstPointer = mods; - for(int i = 0; i < modId; i++) { + for(unsigned long i = 0; i < modId; i++) { mods = g_list_next(mods); } @@ -469,7 +469,7 @@ static int fomod(int argc, char ** argv) { GList * mods = listFilesInFolder(modFolder); GList * modsFirstPointer = mods; - for(int i = 0; i < modId; i++) { + for(unsigned long i = 0; i < modId; i++) { mods = g_list_next(mods); } diff --git a/src/steam.c b/src/steam.c index 92ebf92..4703c29 100644 --- a/src/steam.c +++ b/src/steam.c @@ -12,6 +12,15 @@ enum FieldIds { FIELD_PATH, FIELD_LABEL, FIELD_CONTENT_ID, FIELD_TOTAL_SIZE, FIELD_CLEAN_BYTES, FIELD_CORRUPTION, FIELD_APPS }; +// relative to the home directory +static const char * steamLibraries[] = { + "/.steam/root/", + "/.steam/steam/", + "/.local/share/steam", + //flatpack steam. + "/.var/app/com.valvesoftware.Steam/.local/share/Steam" +}; + static int getFiledId(const char * field) { //replace with a hash + switch if(strcmp(field, "path") == 0) { @@ -38,7 +47,6 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu FILE * fd = fopen(path, "r"); char * line = NULL; size_t len = 0; - ssize_t read; *status = EXIT_SUCCESS; @@ -185,7 +193,7 @@ GHashTable* search_games(int * status) { char * home = getHome(); *status = EXIT_SUCCESS; - for(int i = 0; i < LEN(steamLibraries); i++) { + for(unsigned long i = 0; i < LEN(steamLibraries); i++) { char * path = g_build_filename(home, steamLibraries[i], "steamapps/libraryfolders.vdf", NULL); if (access(path, F_OK) == 0) { int parserStatus; @@ -208,8 +216,8 @@ GHashTable* search_games(int * status) { GHashTable* table = g_hash_table_new_full(g_int_hash, g_int_equal, free, free); //fill the table - for(int i = 0; i < size; i++) { - for(int j = 0; j < libraries[i].appsCount; j++) { + for(unsigned long i = 0; i < size; i++) { + for(unsigned long j = 0; j < libraries[i].appsCount; j++) { int gameId = getGameIdFromAppId(libraries[i].apps[j].appid); if(gameId >= 0) { int * key = malloc(sizeof(int)); @@ -225,7 +233,7 @@ GHashTable* search_games(int * status) { int getGameIdFromAppId(u_int32_t appid) { - for(int k = 0; k < LEN(GAMES_APPIDS); k++) { + for(unsigned long k = 0; k < LEN(GAMES_APPIDS); k++) { if(appid == GAMES_APPIDS[k]) { return k; } diff --git a/src/steam.h b/src/steam.h index 983ce4a..838287e 100644 --- a/src/steam.h +++ b/src/steam.h @@ -1,6 +1,8 @@ #ifndef __STEAM_H__ #define __STEAM_H__ +#include "macro.h" + #include #include #include @@ -23,30 +25,23 @@ typedef struct ValveLibraries { size_t appsCount; } ValveLibraries_t; -// relative to the home directory -const static char * steamLibraries[] = { - "/.steam/root/", - "/.steam/steam/", - "/.local/share/steam", - //flatpack steam. - "/.var/app/com.valvesoftware.Steam/.local/share/Steam" -}; - //todo add the older games // order has to be the same as in GAMES_NAMES -const static u_int32_t GAMES_APPIDS[] = { +static const u_int32_t GAMES_APPIDS[] = { 489830, 22330, 377160 }; //the name of the game in the steamapps/common folder -const static char * GAMES_NAMES[] = { +static const char * GAMES_NAMES[] = { "Skyrim Special Edition", "Oblivion", "Fallout 4" }; +_Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Names doesn't match"); + /** * @brief list all installed games and the paths to the game's files * From 3376ac1da6df672d82f3d089ecc5484b19705373 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 5 Oct 2022 14:02:01 +0200 Subject: [PATCH 07/18] fixed some pointer arithmetic issue on non GNU compilers. --- CMakeLists.txt | 2 +- src/fomod/xmlUtil.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8deda90..8b2e40b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -fsanitize=address -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wno-pointer-arith") +set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -fsanitize=address -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations") set(CMAKE_C_FLAGS_DEBUG "-g -Werror") set(CMAKE_C_FLAGS_RELEASE "-O2") diff --git a/src/fomod/xmlUtil.c b/src/fomod/xmlUtil.c index b7c0d3d..9e25d8a 100644 --- a/src/fomod/xmlUtil.c +++ b/src/fomod/xmlUtil.c @@ -1,6 +1,7 @@ #include "xmlUtil.h" #include +#include char * freeAndDup(xmlChar * line) { char * free = strdup((const char *) line); @@ -29,8 +30,9 @@ void fixPath(char * path) { int countUntilNull(void * pointers, size_t typeSize) { int i = 0; - while(pointers != NULL) { - pointers += typeSize; + char * arithmetic = (char *)pointers; + while(arithmetic != NULL) { + arithmetic += typeSize; i++; } return i; From 7725e19069215005047585633f72cb8291d57c9a Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 5 Oct 2022 19:08:57 +0200 Subject: [PATCH 08/18] fixed some memory leaks --- CMakeLists.txt | 9 +++++--- src/file.c | 25 ++++++++++++++-------- src/fomod.c | 53 +++++++++++++++++++++++++++++++++++++++------- src/fomod.h | 9 ++++++-- src/fomod/group.c | 1 + src/fomod/parser.c | 3 ++- src/main.c | 6 +++++- src/order.c | 4 +++- src/steam.c | 3 +++ 9 files changed, 88 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b2e40b..fd2e4c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,12 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -fsanitize=address -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations") -set(CMAKE_C_FLAGS_DEBUG "-g -Werror") +if(CMAKE_BUILD_TYPE MATCHES DEBUG) + add_link_options(-fsanitize=address) +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") # generate the compile_commands for vscode / clang @@ -38,7 +42,6 @@ pkg_search_module(LIBXML REQUIRED libxml-2.0) target_include_directories(ModManager PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${AUDIT_INCLUDE_DIRS}) -add_link_options(-fsanitize=address) target_link_libraries(ModManager ${GLIB_LDFLAGS}) target_link_libraries(ModManager ${LIBXML_LIBRARIES}) diff --git a/src/file.c b/src/file.c index 2f0177d..e395b1b 100644 --- a/src/file.c +++ b/src/file.c @@ -100,22 +100,29 @@ void casefold(const char * folder) { //removes .. & . from the list if(strcmp(dir->d_name, "..") != 0 && strcmp(dir->d_name, ".") != 0) { //only look at ascii and hope for the best. - gchar * file = g_build_path("/", folder, dir->d_name, NULL); + gchar * file = g_build_filename(folder, dir->d_name, NULL); gchar * destinationName = g_ascii_strdown(dir->d_name, -1); gchar * destination = g_build_path("/", folder,destinationName, NULL); - int result = move(file, destination); - if(result != EXIT_SUCCESS) { - fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName); - } - g_free(destinationName); - g_free(destination); - if(dir->d_type == DT_DIR) { - casefold(file); + if(strcmp(destinationName, dir->d_name) != 0) { + + int result = move(file, destination); + if(result != EXIT_SUCCESS) { + fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName); + } + + } g_free(file); + g_free(destinationName); + + if(dir->d_type == DT_DIR) { + casefold(destination); + } + + g_free(destination); } } closedir(d); diff --git a/src/fomod.c b/src/fomod.c index 51c4bab..784f451 100644 --- a/src/fomod.c +++ b/src/fomod.c @@ -12,6 +12,7 @@ #include "fomod.h" #include "file.h" +#include "fomod/fomodTypes.h" #include "libxml/globals.h" static int getInputCount(const char * input) { @@ -44,7 +45,7 @@ static gint priorityCmp(gconstpointer a, gconstpointer b) { const FOModFile_t * fileA = (const FOModFile_t *)a; const FOModFile_t * fileB = (const FOModFile_t *)b; - return fileA->priority - fileB->priority; + return fileB->priority - fileA->priority; } static void printfOptionsInOrder(FOModGroup_t group) { @@ -120,9 +121,10 @@ static void sortGroup(FOModGroup_t * group) { } //TODO: handle error -int processFileOperations(GList * pendingFileOperations, const char * modFolder, const char * destination) { - pendingFileOperations = g_list_sort(pendingFileOperations, priorityCmp); - GList * currentFileOperation = pendingFileOperations; +int processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) { + //priority higher a less important and should be processed first. + *pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp); + GList * currentFileOperation = *pendingFileOperations; while(currentFileOperation != NULL) { //TODO: support destination @@ -171,6 +173,13 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin FOModFile_t * fileCopy = malloc(sizeof(*file)); *fileCopy = *file; + //changing pathes to lowercase since we used casefold and the pathes in the xml might not like it + char * destination = g_ascii_strdown(file->destination, -1); + fileCopy->destination = destination; + + char * source = g_ascii_strdown(file->source, -1); + fileCopy->source = source; + pendingFileOperations = g_list_append(pendingFileOperations, fileCopy); } } @@ -178,6 +187,18 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin return pendingFileOperations; } +void freeFileOperations(GList * fileOperations) { + GList * fileOperationsStart = fileOperations; + while(fileOperations != NULL) { + FOModFile_t * file = (FOModFile_t *)fileOperations->data; + if(file->destination != NULL)free(file->destination); + if(file->source != NULL)free(file->source); + fileOperations = g_list_next(fileOperations); + } + + g_list_free_full(fileOperationsStart, free); +} + int installFOMod(const char * modFolder, const char * destination) { //everything should be lowercase since we use casefold() before calling any install function char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL); @@ -259,6 +280,10 @@ int installFOMod(const char * modFolder, const char * destination) { min = 0; max = 0; break; + default: + //never happen; + fprintf(stderr, "unexpected type please report this issue %d, %d", group.type, __LINE__); + return EXIT_FAILURE; } @@ -277,11 +302,14 @@ int installFOMod(const char * modFolder, const char * destination) { } } + //processing user input + GRegex* regex = g_regex_new("\\s*", 0, 0, NULL); char ** choices = g_regex_split(regex, inputBuffer, 0); g_regex_unref(regex); for(int choiceId = 0; choices[choiceId] != NULL; choiceId++) { + //TODO: safer user input int choice = atoi(choices[choiceId]); FOModPlugin_t plugin = group.plugins[choice]; for(int flagId = 0; flagId < plugin.flagCount; flagId++) { @@ -292,9 +320,18 @@ int installFOMod(const char * modFolder, const char * destination) { for(int pluginId = 0; pluginId < plugin.fileCount; pluginId++) { const FOModFile_t * file = &plugin.files[pluginId]; - FOModFile_t * fileCopy = malloc(sizeof(*file)); + FOModFile_t * fileCopy = malloc(sizeof(FOModFile_t)); *fileCopy = *file; + //changing pathes to lowercase since we used casefold and the pathes in the xml might not like it + char * destination = g_ascii_strdown(file->destination, -1); + fileCopy->destination = strdup(destination); + g_free(destination); + + char * source = g_ascii_strdown(file->source, -1); + fileCopy->source = strdup(source); + g_free(source); + pendingFileOperations = g_list_append(pendingFileOperations, fileCopy); } } @@ -304,14 +341,14 @@ int installFOMod(const char * modFolder, const char * destination) { } } - + //TODO: manage multiple files with the same name pendingFileOperations = processCondFiles(&fomod, flagList, pendingFileOperations); - processFileOperations(pendingFileOperations, modFolder, destination); + processFileOperations(&pendingFileOperations, modFolder, destination); printf("FOMod successfully installed!\n"); g_list_free(flagList); - g_list_free_full(pendingFileOperations, free); + freeFileOperations(pendingFileOperations); freeFOMod(&fomod); g_free(fomodFolder); return EXIT_SUCCESS; diff --git a/src/fomod.h b/src/fomod.h index 368af57..a7bb6af 100644 --- a/src/fomod.h +++ b/src/fomod.h @@ -35,8 +35,13 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin * @param destination folder of the new mod that contains the result of the process. * @return error code */ -int processFileOperations(GList * pendingFileOperations, const char * modFolder, const char * destination); - +int processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination); +/** + * @brief + * + * @param fileOperations + */ +void freeFileOperations(GList * fileOperations); #endif diff --git a/src/fomod/group.c b/src/fomod/group.c index 60d434d..9ea650c 100644 --- a/src/fomod/group.c +++ b/src/fomod/group.c @@ -36,6 +36,7 @@ static TypeDescriptor_t getDescriptor(const char * descriptor) { } void freeGroup(FOModGroup_t * group) { + free(group->name); if(group->pluginCount == 0) return; for(int pluginId = 0; pluginId < group->pluginCount; pluginId++) { FOModPlugin_t * plugin = &group->plugins[pluginId]; diff --git a/src/fomod/parser.c b/src/fomod/parser.c index 3703cc8..34a07c4 100644 --- a/src/fomod/parser.c +++ b/src/fomod/parser.c @@ -36,16 +36,17 @@ void freeFOMod(FOMod_t * fomod) { for(int groupId = 0; groupId < step->groupCount; groupId++) { FOModGroup_t * group = &step->groups[groupId]; freeGroup(group); - free(step->groups[groupId].plugins); } for(int flagId = 0; flagId < step->flagCount; flagId++) { FOModFlag_t * flag = &(step->requiredFlags[flagId]); free(flag->name); free(flag->value); } + free(step->groups); free(step->requiredFlags); free(step->name); } + free(fomod->steps); //set every counter to zero and every pointer to null memset(fomod, 0, sizeof(FOMod_t)); diff --git a/src/main.c b/src/main.c index 59b014e..01ba228 100644 --- a/src/main.c +++ b/src/main.c @@ -137,6 +137,7 @@ static int listAllMods(int argc, char ** argv) { free(home); GList * mods = listMods(appid); + GList * p_mods = mods; unsigned short index = 0; printf("Id | Installed | Name\n"); @@ -150,13 +151,15 @@ static int listAllMods(int argc, char ** argv) { printf(DISABLED_COLOR " %d | ✕ | %s\n", index, modName); } + g_free(modPath); + index++; mods = g_list_next(mods); } free(modFolder); - free(mods); + g_list_free_full(p_mods, free); return EXIT_SUCCESS; } @@ -535,6 +538,7 @@ int main(int argc, char ** argv) { int searchStatus; gamePaths = search_games(&searchStatus); + int returnValue = EXIT_SUCCESS; if(searchStatus == EXIT_FAILURE) { returnValue = EXIT_FAILURE; diff --git a/src/order.c b/src/order.c index ca9a163..75cc403 100644 --- a/src/order.c +++ b/src/order.c @@ -51,6 +51,7 @@ GList * listMods(int appid) { Mod_t * mod = alloca(sizeof(Mod_t)); mod->modId = modId; + //we are going to return this value so no alloca here mod->name = strdup(dir->d_name); //strdup but on the stack //add one for the \0 @@ -120,6 +121,7 @@ int swapPlace(int appid, int modIdA, int modIdB) { char * modAFolder = g_build_filename(modFolder, listA->data, ORDER_FILE, NULL); char * modBFolder = g_build_filename(modFolder, listB->data, ORDER_FILE, NULL); + g_free(modFolder); FILE * fileA = fopen(modAFolder, "w"); FILE * fileB = fopen(modBFolder, "w"); @@ -138,6 +140,6 @@ int swapPlace(int appid, int modIdA, int modIdB) { fclose(fileA); fclose(fileB); - g_list_free(list); + g_list_free_full(list, free); return EXIT_SUCCESS; } diff --git a/src/steam.c b/src/steam.c index 4703c29..177d934 100644 --- a/src/steam.c +++ b/src/steam.c @@ -125,8 +125,11 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu } else { library->apps[library->appsCount - 1].update = strtoul(value, NULL, 10); } + free(value); isAppId = !isAppId; break; + default: + free(value); } //field apps is using braces so the syntax is different if(nextFieldToFill != FIELD_APPS)nextFieldToFill = -1; From 7699b76333d35f9ac82d145b48154dadc18f7447 Mon Sep 17 00:00:00 2001 From: Marc Date: Thu, 6 Oct 2022 22:29:14 +0200 Subject: [PATCH 09/18] added version string --- src/main.c | 11 +++++++++++ src/main.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/main.c b/src/main.c index 01ba228..f925093 100644 --- a/src/main.c +++ b/src/main.c @@ -66,6 +66,7 @@ static int usage() { printf("Use --unbind to rollback a deployment\n"); printf("Use --fomod to create a new mod using the result from the FOMod\n"); 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"); return EXIT_FAILURE; } @@ -635,6 +636,16 @@ int main(int argc, char ** argv) { else returnValue = swapMod(argc, argv); + } else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) { + returnValue = EXIT_SUCCESS; + #ifdef __clang__ + printf("%s: Clang: %d.%d.%d\n", VERSION, __clang_major__, __clang_minor__, __clang_patchlevel__); + #elifdef __GNUC__ + printf("%s: GCC: %d.%d.%d\n", VERSION, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); + #else + printf("%s: unknown compiler\n", VERSION); + #endif + } else { usage(); returnValue = EXIT_FAILURE; diff --git a/src/main.h b/src/main.h index e63cd96..147d0ab 100644 --- a/src/main.h +++ b/src/main.h @@ -11,3 +11,5 @@ #define GAME_UPPER_DIR_NAME "UPPER_DIRS" //overlayfs temporary dir. #define GAME_WORK_DIR_NAME "WORK_DIRS" + +#define VERSION "0.1" From 67a68011eda526788aad7f08eb01dbcbc0c23e67 Mon Sep 17 00:00:00 2001 From: Marc Date: Thu, 6 Oct 2022 22:43:09 +0200 Subject: [PATCH 10/18] PKGBUILD directly build from the git a archlinux package. changed the name of the binary to not have issue. --- CMakeLists.txt | 16 ++++++++-------- PKGBUILD | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 PKGBUILD diff --git a/CMakeLists.txt b/CMakeLists.txt index fd2e4c9..2c42017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(CMAKE_C_FLAGS_RELEASE "-O2") set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") # set the project name -project(ModManager) +project(mod-manager) set(SOURCES @@ -33,20 +33,20 @@ set(SOURCES ) # add the executable -add_executable(ModManager src/main.c ${SOURCES}) +add_executable(mod-manager src/main.c ${SOURCES}) find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED glib-2.0) pkg_search_module(AUDIT REQUIRED audit) pkg_search_module(LIBXML REQUIRED libxml-2.0) -target_include_directories(ModManager PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${AUDIT_INCLUDE_DIRS}) +target_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${AUDIT_INCLUDE_DIRS}) -target_link_libraries(ModManager ${GLIB_LDFLAGS}) -target_link_libraries(ModManager ${LIBXML_LIBRARIES}) -target_link_libraries(ModManager ${AUDIT_LIBRARIES}) +target_link_libraries(mod-manager ${GLIB_LDFLAGS}) +target_link_libraries(mod-manager ${LIBXML_LIBRARIES}) +target_link_libraries(mod-manager ${AUDIT_LIBRARIES}) -install(TARGETS ModManager DESTINATION bin) +install(TARGETS mod-manager DESTINATION bin) -set_property(TARGET ModManager PROPERTY C_STANDARD 23) +set_property(TARGET mod-manager PROPERTY C_STANDARD 23) diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..71491fd --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,26 @@ +# Maintainer: Marc barbier +pkgname=modmanager +pkgver=0.1 +pkgrel=1 +pkgdesc="a Mod manager for bethesda games" +arch=('x86_64') # might work on other archs but i can not try +url='https://gitlab.marcbarbier.fr/Marc/modmanager' +license=('GPL2') +source=( 'git+https://gitlab.marcbarbier.fr/Marc/modmanager.git' ) +md5sums=( 'SKIP' ) + +optdepends=('fuse-overlayfs: special filesystem support' ) + +depends=( 'glib2' 'unrar' 'p7zip' 'unzip' ) + +build() { + cmake -B build -S "${pkgname}" \ + -DCMAKE_BUILD_TYPE='None' \ + -DCMAKE_INSTALL_PREFIX='/usr' \ + -Wno-dev + cmake --build build +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} From b6d11aa9ee747921abf9d013ce54f4466807c394 Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 7 Oct 2022 10:12:34 +0200 Subject: [PATCH 11/18] Droped support for kernel mode Overlayfs --- src/main.c | 107 +++++++++++++++--------------------------------- src/overlayfs.c | 42 ++++++++----------- src/overlayfs.h | 5 ++- 3 files changed, 52 insertions(+), 102 deletions(-) diff --git a/src/main.c b/src/main.c index f925093..d6bbbbf 100644 --- a/src/main.c +++ b/src/main.c @@ -45,16 +45,6 @@ static GList * listFilesInFolder(const char * path) { return list; } -static int noRoot() { - fprintf(stderr, "Don't run this argument as root\n"); - return EXIT_FAILURE; -} - -static int needRoot() { - fprintf(stderr, "Root is needed to bind with the game files\n"); - return EXIT_FAILURE; -} - static int usage() { printf("Use --list-games or -l to list compatible games\n"); printf("Use --add or -a to add a mod to a game\n"); @@ -320,15 +310,15 @@ static int deploy(int argc, char ** argv) { //it might crash / corrupt game file if the user do it while the game is running //but it's still very unlikely while(umount2(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0); - int status = overlayMount(modsToInstall, steamGameFolder, gameUpperDir, gameWorkDir); - if(status == 0) { + enum overlayErrors status = overlayMount(modsToInstall, steamGameFolder, gameUpperDir, gameWorkDir); + if(status == SUCESS) { printf("Everything is ready, just launch the game\n"); - } else if(status == -1) { - fprintf(stderr, "Could not mount the mods overlay, try to install fuse-overlay\n"); - return EXIT_FAILURE; - } else if(status == -2) { + } else if(status == FAILURE) { fprintf(stderr, "Could not mount the mods overlay\n"); return EXIT_FAILURE; + } else if(status == NOT_INSTALLED) { + fprintf(stderr, "Please install fuse-overlayfs\n"); + return EXIT_FAILURE; } else { fprintf(stderr, "%d bug detected, please report this.\n", __LINE__); return EXIT_FAILURE; @@ -531,8 +521,8 @@ static int swapMod(int argc, char ** argv) { int main(int argc, char ** argv) { if(argc < 2 ) return usage(); - if(audit_getloginuid() == 0) { - fprintf(stderr, "The root user should not be using this\n"); + if(audit_getloginuid() == 0 || isRoot()) { + fprintf(stderr, "Please don't run this software as root\n"); return EXIT_FAILURE; } @@ -571,72 +561,40 @@ int main(int argc, char ** argv) { } - if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = listGames(argc, argv); + if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0) + returnValue = listGames(argc, argv); - } else if(strcmp(argv[1], "--add") == 0 || strcmp(argv[1], "-a") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = add(argc, argv); + else if(strcmp(argv[1], "--add") == 0 || strcmp(argv[1], "-a") == 0) + returnValue = add(argc, argv); - } else if(strcmp(argv[1], "--list-mods") == 0 || strcmp(argv[1], "-m") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = listAllMods(argc, argv); + else if(strcmp(argv[1], "--list-mods") == 0 || strcmp(argv[1], "-m") == 0) + returnValue = listAllMods(argc, argv); - } else if(strcmp(argv[1], "--install") == 0 || strcmp(argv[1], "-i") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = installAndUninstallMod(argc, argv, true); + else if(strcmp(argv[1], "--install") == 0 || strcmp(argv[1], "-i") == 0) + returnValue = installAndUninstallMod(argc, argv, true); - } else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = installAndUninstallMod(argc, argv, false); + else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0) + returnValue = installAndUninstallMod(argc, argv, false); - } else if(strcmp(argv[1], "--deploy") == 0 || strcmp(argv[1], "-d") == 0) { - if(!isRoot()) - returnValue = needRoot(); - else - returnValue = deploy(argc, argv); + else if(strcmp(argv[1], "--deploy") == 0 || strcmp(argv[1], "-d") == 0) + returnValue = deploy(argc, argv); - } else if(strcmp(argv[1], "--unbind") == 0){ - if(!isRoot()) - returnValue = needRoot(); - else - returnValue = unbind(argc, argv); + else if(strcmp(argv[1], "--unbind") == 0) + returnValue = unbind(argc, argv); - } else if(strcmp(argv[1], "--setup") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = setup(argc, argv); - } else if(strcmp(argv[1], "--fomod") == 0){ - if(isRoot()) - returnValue = noRoot(); - else - returnValue = fomod(argc, argv); + else if(strcmp(argv[1], "--setup") == 0) + returnValue = setup(argc, argv); - } else if(strcmp(argv[1], "--remove") == 0 || strcmp(argv[1], "-r") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = removeMod(argc, argv); + else if(strcmp(argv[1], "--fomod") == 0) + returnValue = fomod(argc, argv); - } else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0) { - if(isRoot()) - returnValue = noRoot(); - else - returnValue = swapMod(argc, argv); + else if(strcmp(argv[1], "--remove") == 0 || strcmp(argv[1], "-r") == 0) + returnValue = removeMod(argc, argv); - } else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) { + else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0) + returnValue = swapMod(argc, argv); + + else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) { returnValue = EXIT_SUCCESS; #ifdef __clang__ printf("%s: Clang: %d.%d.%d\n", VERSION, __clang_major__, __clang_minor__, __clang_patchlevel__); @@ -645,7 +603,6 @@ int main(int argc, char ** argv) { #else printf("%s: unknown compiler\n", VERSION); #endif - } else { usage(); returnValue = EXIT_FAILURE; diff --git a/src/overlayfs.c b/src/overlayfs.c index 43a6b49..6a1d520 100644 --- a/src/overlayfs.c +++ b/src/overlayfs.c @@ -6,39 +6,29 @@ #include "overlayfs.h" #include -int overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir) { +enum overlayErrors overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir) { char * lowerdir = g_strjoinv(":", sources); char * mountData = g_strjoin("", "lowerdir=", lowerdir, ",workdir=", workdir, ",upperdir=", upperdir, NULL); - int result = mount("overlay", "none", "overlay", MS_RDONLY, mountData); - if(result < 0) { - //this is very important for a lot of filesystems - //even ext4 can be incompatible with the kernel's implementation - //with some flags such as casefold - if(access("/usr/bin/fuse-overlayfs", F_OK) == 0) { - int pid = fork(); - if(pid == 0) { - //exit is used to get the return value when using waitpid - exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-r", "-o", mountData, dest, NULL)); - } else { - - int returnValue = 0; - waitpid(pid, &returnValue, 0); - free(lowerdir); - free(mountData); - if(returnValue != 0) { - return -2; - } - return 0; - } + enum overlayErrors result = SUCESS; + if(access("/usr/bin/fuse-overlayfs", F_OK) == 0) { + int pid = fork(); + if(pid == 0) { + //exit is used to get the return value when using waitpid + exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-r", "-o", mountData, dest, NULL)); } else { - free(lowerdir); - free(mountData); - return -1; + + int returnValue = 0; + waitpid(pid, &returnValue, 0); + if(returnValue != 0) { + result = FAILURE; + } } + } else { + result = NOT_INSTALLED; } free(lowerdir); free(mountData); - return 0; + return result; } diff --git a/src/overlayfs.h b/src/overlayfs.h index f9eb612..8220f15 100644 --- a/src/overlayfs.h +++ b/src/overlayfs.h @@ -1,6 +1,9 @@ #ifndef __OVERLAY_H__ #define __OVERLAY_H__ +enum overlayErrors { SUCESS, NOT_INSTALLED, FAILURE }; + + /** * @brief Overlayfs is what make it possible to deploy to the game files without altering anything. * it allows us to overlay multiple folder over the game files. like appling filters to an image. @@ -10,6 +13,6 @@ * @param workdir a directory that will contains only temporary files. * @return int error code */ -int overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir); +enum overlayErrors overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir); #endif From 091ab3894c68d6055f1600df4803053cd33cd498 Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 7 Oct 2022 10:15:19 +0200 Subject: [PATCH 12/18] refactored usage and options --- src/main.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index d6bbbbf..c167e72 100644 --- a/src/main.c +++ b/src/main.c @@ -47,15 +47,20 @@ static GList * listFilesInFolder(const char * path) { static int usage() { printf("Use --list-games or -l to list compatible games\n"); + + printf("Use --bind or -d to deploy the mods for the game\n"); + printf("Use --unbind to rollback a deployment\n"); + printf("Use --add or -a to add a mod to a game\n"); + printf("Use --remove or -r to remove a mod\n"); + printf("Use --fomod to create a new mod using the result from the FOMod\n"); + printf("Use --list-mods or -m to list all mods for a game\n"); printf("Use --install or -i to add a mod to a game\n"); printf("Use --uninstall or -u to uninstall a mod from a game\n"); - printf("Use --remove or -r to remove a mod\n"); - printf("Use --deploy or -d to deploy the mods for the game\n"); - printf("Use --unbind to rollback a deployment\n"); - printf("Use --fomod to create a new mod using the result from the FOMod\n"); + 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"); return EXIT_FAILURE; } @@ -576,7 +581,7 @@ int main(int argc, char ** argv) { else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0) returnValue = installAndUninstallMod(argc, argv, false); - else if(strcmp(argv[1], "--deploy") == 0 || strcmp(argv[1], "-d") == 0) + else if(strcmp(argv[1], "--bind") == 0 || strcmp(argv[1], "-d") == 0) returnValue = deploy(argc, argv); else if(strcmp(argv[1], "--unbind") == 0) From 4ce99996e81c8e10af0f404ce8a258d57eb0b910 Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 7 Oct 2022 10:47:14 +0200 Subject: [PATCH 13/18] Adder the type alias error_t and refactored the code to use it. also extracted archive function from install.c to archives.c --- CMakeLists.txt | 1 + src/archives.c | 96 +++++++++++++++++++++++++++++++++++++++ src/archives.h | 30 ++++++++++++ src/file.h | 11 +++-- src/fomod.c | 19 ++++---- src/fomod.h | 7 +-- src/fomod/parser.c | 17 +++---- src/fomod/parser.h | 4 +- src/fomod/xmlUtil.h | 12 +++++ src/install.c | 108 +++++--------------------------------------- src/install.h | 4 +- src/main.h | 7 +++ src/order.c | 8 ++-- src/order.h | 3 +- src/steam.h | 1 + 15 files changed, 198 insertions(+), 130 deletions(-) create mode 100644 src/archives.c create mode 100644 src/archives.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c42017..777ab41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES src/getHome.c src/file.c src/order.c + src/archives.c src/fomod.c src/fomod/group.c src/fomod/xmlUtil.c diff --git a/src/archives.c b/src/archives.c new file mode 100644 index 0000000..fabd680 --- /dev/null +++ b/src/archives.c @@ -0,0 +1,96 @@ +#include "archives.h" +#include "file.h" + +#include +#include +#include +#include +#include +#include + +int unzip(char * path, char * outdir) { + char * const args[] = { + "unzip", + "-LL", // to lowercase + "-q", + path, + "-d", + outdir, + NULL + }; + + pid_t pid = fork(); + + if(pid == 0) { + execv("/usr/bin/unzip", args); + return EXIT_FAILURE; + } else { + int returnValue; + waitpid(pid, &returnValue, 0); + + if(returnValue != 0) { + fprintf(stderr, "\nFailed to decompress archive\n"); + } + return returnValue; + } +} + +int unrar(char * path, char * outdir) { + char * const args[] = { + "unrar", + "x", + "-y", //assume yes + "-cl", // to lowercase + path, + outdir, + NULL + }; + + pid_t pid = fork(); + + if(pid == 0) { + execv("/usr/bin/unrar", args); + return EXIT_FAILURE; + } else { + int returnValue; + waitpid(pid, &returnValue, 0); + + if(returnValue != 0) { + fprintf(stderr, "\nFailed to decompress archive\n"); + } + return returnValue; + } +} + + +int un7z(char * path, const char * outdir) { + gchar * outParameter = g_strjoin("", "-o", outdir, NULL); + + char * const args[] = { + "7z", + "-y", //assume yes + "x", + path, + outParameter, + NULL + }; + + pid_t pid = fork(); + + if(pid == 0) { + execv("/usr/bin/7z", args); + return EXIT_FAILURE; + } else { + g_free(outParameter); + int returnValue; + waitpid(pid, &returnValue, 0); + + if(returnValue != 0) { + fprintf(stderr, "\nFailed to decompress archive\n"); + return returnValue; + } + //make everything lowercase since 7z don't have an argument for that. + casefold(outdir); + return returnValue; + } +} diff --git a/src/archives.h b/src/archives.h new file mode 100644 index 0000000..01032a5 --- /dev/null +++ b/src/archives.h @@ -0,0 +1,30 @@ +#ifndef __ARCHIVES_H__ +#define __ARCHIVES_H__ + +//all of these function will make every file into lowercase + +/** + * @brief Execute the unzip command + * @param path path to archive + * @param outdir output director + * @return int return code + */ +int unzip(char * path, char * outdir); + +/** + * @brief Execute the unrar command + * @param path path to archive + * @param outdir output director + * @return int return code + */ +int unrar(char * path, char * outdir); + +/** + * @brief Execute the 7z command + * @param path path to archive + * @param outdir output director + * @return int return code + */ +int un7z(char * path, const char * outdir); + +#endif diff --git a/src/file.h b/src/file.h index 3c1be0c..68c65bd 100644 --- a/src/file.h +++ b/src/file.h @@ -1,9 +1,10 @@ -#include -#include - #ifndef __FILE_H__ #define __FILE_H__ +#include +#include +#include "main.h" + //valid copy flags #define cp_DEFAULT 0 @@ -16,7 +17,7 @@ * @param source path to the source files * @param dest path to the destination folder * @param flags refer to the "valid copy flags" use them like this CP_LINK | CP_RECURSIVE - * @return status code + * @return cp return value */ int copy(const char * source, const char * dest, u_int32_t flags); @@ -25,7 +26,7 @@ int copy(const char * source, const char * dest, u_int32_t flags); * @param source path to the source files * @param dest path to the destination folder * @param bool enable recursive rm -r - * @return status code + * @return rm return value */ int delete(const char * path, bool recursive); diff --git a/src/fomod.c b/src/fomod.c index 784f451..e8ffb20 100644 --- a/src/fomod.c +++ b/src/fomod.c @@ -14,6 +14,7 @@ #include "file.h" #include "fomod/fomodTypes.h" #include "libxml/globals.h" +#include "main.h" static int getInputCount(const char * input) { char buff[2]; @@ -121,7 +122,7 @@ static void sortGroup(FOModGroup_t * group) { } //TODO: handle error -int processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) { +error_t processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) { //priority higher a less important and should be processed first. *pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp); GList * currentFileOperation = *pendingFileOperations; @@ -148,7 +149,7 @@ int processFileOperations(GList ** pendingFileOperations, const char * modFolder currentFileOperation = g_list_next(currentFileOperation); } - return EXIT_SUCCESS; + return ERR_SUCCESS; } GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) { @@ -199,7 +200,7 @@ void freeFileOperations(GList * fileOperations) { g_list_free_full(fileOperationsStart, free); } -int installFOMod(const char * modFolder, const char * destination) { +error_t installFOMod(const char * modFolder, const char * destination) { //everything should be lowercase since we use casefold() before calling any install function char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL); char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL); @@ -208,14 +209,14 @@ int installFOMod(const char * modFolder, const char * destination) { fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n"); g_free(fomodFolder); g_free(fomodFile); - return EXIT_FAILURE; + return ERR_FAILURE; } FOMod_t fomod; int returnValue = parseFOMod(fomodFile, &fomod); - if(returnValue != EXIT_SUCCESS) { - return returnValue; - } + if(returnValue == ERR_FAILURE) + return ERR_FAILURE; + g_free(fomodFile); GList * flagList = NULL; @@ -283,7 +284,7 @@ int installFOMod(const char * modFolder, const char * destination) { default: //never happen; fprintf(stderr, "unexpected type please report this issue %d, %d", group.type, __LINE__); - return EXIT_FAILURE; + return ERR_FAILURE; } @@ -351,5 +352,5 @@ int installFOMod(const char * modFolder, const char * destination) { freeFileOperations(pendingFileOperations); freeFOMod(&fomod); g_free(fomodFolder); - return EXIT_SUCCESS; + return ERR_SUCCESS; } diff --git a/src/fomod.h b/src/fomod.h index a7bb6af..30d5386 100644 --- a/src/fomod.h +++ b/src/fomod.h @@ -3,8 +3,9 @@ #include #include -#include "fomod/parser.h" +#include "main.h" +#include "fomod/parser.h" #include "fomod/group.h" #include "fomod/xmlUtil.h" @@ -15,7 +16,7 @@ * @param destination folder of the new mod that contains the result of the fomod process. * @return int */ -int installFOMod(const char * modFolder, const char * destination); +error_t installFOMod(const char * modFolder, const char * destination); /** * @brief In fomod there is file operations which depends on multiple flags this function find the ones that mach our current flags and append them to a list. @@ -35,7 +36,7 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin * @param destination folder of the new mod that contains the result of the process. * @return error code */ -int processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination); +error_t processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination); /** * @brief diff --git a/src/fomod/parser.c b/src/fomod/parser.c index 34a07c4..fbd7470 100644 --- a/src/fomod/parser.c +++ b/src/fomod/parser.c @@ -239,7 +239,7 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { return EXIT_SUCCESS; } -int parseFOMod(const char * fomodFile, FOMod_t* fomod) { +error_t parseFOMod(const char * fomodFile, FOMod_t* fomod) { xmlDocPtr doc; xmlNodePtr cur; @@ -256,7 +256,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) { if(doc == NULL) { fprintf(stderr, "Document is not a valid xml file\n"); - return EXIT_FAILURE; + return ERR_FAILURE; } @@ -264,12 +264,13 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) { if(cur == NULL) { fprintf(stderr, "emptyDocument"); xmlFreeDoc(doc); - return EXIT_FAILURE; + return ERR_FAILURE; } if(xmlStrcmp(cur->name, (const xmlChar *) "config") != 0) { fprintf(stderr, "document of the wrong type, root node is '%s' instead of config\n", cur->name); - return EXIT_FAILURE; + xmlFreeDoc(doc); + return ERR_FAILURE; } cur = cur->children; @@ -286,7 +287,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) { if(validateNode(&requiredInstallFile, true, "folder", "file", NULL)) { //TODO: handle error printf("%d\n", __LINE__); - exit(EXIT_FAILURE); + exit(ERR_FAILURE); } int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **)) + 2; @@ -301,7 +302,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) { if(fomod->steps != NULL) { fprintf(stderr, "Multiple 'installSteps' tags"); //TODO: handle error - return EXIT_FAILURE; + return ERR_FAILURE; } xmlChar * stepOrder = xmlGetProp(cur, (xmlChar *)"order"); @@ -315,7 +316,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) { fprintf(stderr, "Failed to parse the install steps\n"); //TODO: manage the error properly - return EXIT_FAILURE; + return ERR_FAILURE; } fomod->steps = steps; @@ -327,5 +328,5 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) { } xmlFreeDoc(doc); - return EXIT_SUCCESS; + return ERR_SUCCESS; } diff --git a/src/fomod/parser.h b/src/fomod/parser.h index 6f08a5c..604230e 100644 --- a/src/fomod/parser.h +++ b/src/fomod/parser.h @@ -3,6 +3,7 @@ #include "group.h" +#include "../main.h" //combine installStep and optionalFileGroups typedef struct FOModStep { @@ -30,9 +31,8 @@ typedef struct FOMod { * * @param fomodFile path to the moduleconfig.xml * @param fomod pointer to a new FOMod_t - * @return error code. */ -int parseFOMod(const char * fomodFile, FOMod_t* fomod); +error_t parseFOMod(const char * fomodFile, FOMod_t* fomod); /** * @brief Free content of a fomod file. diff --git a/src/fomod/xmlUtil.h b/src/fomod/xmlUtil.h index e09e236..f38f7f8 100644 --- a/src/fomod/xmlUtil.h +++ b/src/fomod/xmlUtil.h @@ -7,12 +7,24 @@ typedef enum FOModOrder { ASC, DESC, ORD } FOModOrder_t; +/** + * @brief + * + * @param node a pointer to the current node pointer (xmlNode **) + * @param skipText if a text element is found skip it. + * @param names variadic of the valid names. + * @return return true if it found a valid node + */ bool validateNode(xmlNodePtr * node, bool skipText, const char * names, ...); + /** * @brief Free memory of and xmlChar and return a strdup version. just to make sure there is nothing remaining in libxml */ char * freeAndDup(xmlChar * line); + + FOModOrder_t getFOModOrder(const char * order); + /** * @brief replace / by \ * @param path diff --git a/src/install.c b/src/install.c index a3d9c3f..a381b76 100644 --- a/src/install.c +++ b/src/install.c @@ -1,100 +1,12 @@ #include -#include #include -#include #include #include + #include "install.h" #include "main.h" -#include "file.h" - -static int unzip(char * path, char * outdir) { - char * const args[] = { - "unzip", - "-LL", // to lowercase - "-q", - path, - "-d", - outdir, - NULL - }; - - pid_t pid = fork(); - - if(pid == 0) { - execv("/usr/bin/unzip", args); - return EXIT_FAILURE; - } else { - int returnValue; - waitpid(pid, &returnValue, 0); - - if(returnValue != 0) { - fprintf(stderr, "\nFailed to decompress archive\n"); - } - return returnValue; - } -} - -static int unrar(char * path, char * outdir) { - char * const args[] = { - "unrar", - "x", - "-y", //assume yes - "-cl", // to lowercase - path, - outdir, - NULL - }; - - pid_t pid = fork(); - - if(pid == 0) { - execv("/usr/bin/unrar", args); - return EXIT_FAILURE; - } else { - int returnValue; - waitpid(pid, &returnValue, 0); - - if(returnValue != 0) { - fprintf(stderr, "\nFailed to decompress archive\n"); - } - return returnValue; - } -} - - -static int un7z(char * path, const char * outdir) { - gchar * outParameter = g_strjoin("", "-o", outdir, NULL); - - char * const args[] = { - "7z", - "-y", //assume yes - "x", - path, - outParameter, - NULL - }; - - pid_t pid = fork(); - - if(pid == 0) { - execv("/usr/bin/7z", args); - return EXIT_FAILURE; - } else { - g_free(outParameter); - int returnValue; - waitpid(pid, &returnValue, 0); - - if(returnValue != 0) { - fprintf(stderr, "\nFailed to decompress archive\n"); - return returnValue; - } - //make everything lowercase since 7z don't have an argument for that. - casefold(outdir); - return returnValue; - } -} +#include "archives.h" static const char * extractLastPart(const char * filePath, const char delimeter) { const int length = strlen(filePath); @@ -118,19 +30,18 @@ static const char * extractFileName(const char * filePath) { return extractLastPart(filePath, '/'); } -int addMod(char * filePath, int appId) { - int returnValue = EXIT_SUCCESS; - +error_t addMod(char * filePath, int appId) { + error_t resultError = ERR_SUCCESS; if (access(filePath, F_OK) != 0) { fprintf(stderr, "File not found\n"); - returnValue = EXIT_FAILURE; + resultError = ERR_FAILURE; goto exit; } char * configFolder = g_build_filename(getenv("HOME"), MANAGER_FILES, NULL); if(g_mkdir_with_parents(configFolder, 0755) < 0) { fprintf(stderr, "Could not create mod folder"); - returnValue = EXIT_FAILURE; + resultError = ERR_FAILURE; goto exit2; } @@ -143,6 +54,8 @@ int addMod(char * filePath, int appId) { char * outdir = g_build_filename(configFolder, MOD_FOLDER_NAME, appIdStr, filename, NULL); g_mkdir_with_parents(outdir, 0755); + + int returnValue = EXIT_SUCCESS; printf("Adding mod, this process can be slow depending on your hardware\n"); if(strcmp(lowercaseExtension, "rar") == 0) { returnValue = unrar(filePath, outdir); @@ -155,6 +68,9 @@ int addMod(char * filePath, int appId) { returnValue = EXIT_FAILURE; } + if(returnValue == EXIT_FAILURE) + resultError = ERR_FAILURE; + printf("Done\n"); free(lowercaseExtension); @@ -162,5 +78,5 @@ int addMod(char * filePath, int appId) { exit2: free(configFolder); exit: - return returnValue; + return resultError; } diff --git a/src/install.h b/src/install.h index 5a89ca6..51c52f2 100644 --- a/src/install.h +++ b/src/install.h @@ -1,6 +1,7 @@ #ifndef __INSTALL_H__ #define __INSTALL_H__ #include +#include "main.h" #define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__" /** @@ -8,8 +9,7 @@ * * @param filePath path to the mod file * @param appId game for which the mod is destined to be used with. - * @return EXIT_SUCCESS or EXIT_FAILURE */ -int addMod(char * filePath, int appId); +error_t addMod(char * filePath, int appId); #endif diff --git a/src/main.h b/src/main.h index 147d0ab..3aa9d13 100644 --- a/src/main.h +++ b/src/main.h @@ -1,3 +1,6 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + //no function should ever be put here, only keep important macros here #define APP_NAME "modmanager" @@ -13,3 +16,7 @@ #define GAME_WORK_DIR_NAME "WORK_DIRS" #define VERSION "0.1" + +typedef enum { ERR_SUCCESS, ERR_FAILURE } error_t; + +#endif diff --git a/src/order.c b/src/order.c index 75cc403..becfb81 100644 --- a/src/order.c +++ b/src/order.c @@ -94,7 +94,7 @@ GList * listMods(int appid) { } -int swapPlace(int appid, int modIdA, int modIdB) { +error_t swapPlace(int appid, int modIdA, int modIdB) { char appidStr[10]; sprintf(appidStr, "%d", appid); @@ -116,7 +116,7 @@ int swapPlace(int appid, int modIdA, int modIdB) { if(listA == NULL || listB == NULL) { fprintf(stderr, "Invalid modId\n"); - return EXIT_FAILURE; + return ERR_FAILURE; } char * modAFolder = g_build_filename(modFolder, listA->data, ORDER_FILE, NULL); @@ -131,7 +131,7 @@ int swapPlace(int appid, int modIdA, int modIdB) { if(fileA == NULL || fileB == NULL) { fprintf(stderr, "Error could not open order file\n"); - return EXIT_FAILURE; + return ERR_FAILURE; } fprintf(fileA, "%d", modIdB); @@ -141,5 +141,5 @@ int swapPlace(int appid, int modIdA, int modIdB) { fclose(fileB); g_list_free_full(list, free); - return EXIT_SUCCESS; + return ERR_SUCCESS; } diff --git a/src/order.h b/src/order.h index 33da0fe..15d390d 100644 --- a/src/order.h +++ b/src/order.h @@ -2,6 +2,7 @@ #define __ORDER_H__ #include +#include "main.h" #define ORDER_FILE "__ORDER__" @@ -24,6 +25,6 @@ GList * listMods(int appid); * @param modId * @param modId2 */ -int swapPlace(int appid, int modId, int modId2); +error_t swapPlace(int appid, int modId, int modId2); #endif diff --git a/src/steam.h b/src/steam.h index 838287e..880fe18 100644 --- a/src/steam.h +++ b/src/steam.h @@ -2,6 +2,7 @@ #define __STEAM_H__ #include "macro.h" +#include "main.h" #include #include From 1abef497122a408afcacddfa2199d51f675ea803 Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 7 Oct 2022 11:17:24 +0200 Subject: [PATCH 14/18] Added singleton to search_games --- src/main.c | 62 +++++++++++++++++++++++++++++++++++------------------ src/main.h | 6 ++++-- src/steam.c | 22 ++++++++++++++----- src/steam.h | 8 ++++--- 4 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/main.c b/src/main.c index c167e72..46591f0 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -21,8 +20,6 @@ #define ENABLED_COLOR "\033[0;32m" #define DISABLED_COLOR "\033[0;31m" -GHashTable * gamePaths; - static bool isRoot() { return getuid() == 0; } @@ -65,7 +62,13 @@ static int usage() { return EXIT_FAILURE; } -static int validateAppId(const char * appIdStr) { +static error_t validateAppId(const char * appIdStr) { + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + return ERR_FAILURE; + } + char * strtoulSentinel; //strtoul set EINVAL(after C99) if the string is invalid unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10); @@ -91,6 +94,13 @@ static int validateAppId(const char * appIdStr) { static int listGames(int argc, char **) { if(argc != 2) return usage(); + + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + return EXIT_FAILURE; + } + GList * gamesIds = g_hash_table_get_keys(gamePaths); GList * gamesIdsFirstPointer = gamesIds; if(g_list_length(gamesIds) == 0) { @@ -103,7 +113,7 @@ static int listGames(int argc, char **) { } } g_list_free(gamesIdsFirstPointer); - return EXIT_SUCCESS; + return EXIT_FAILURE; } static int add(int argc, char ** argv) { @@ -303,6 +313,15 @@ static int deploy(int argc, char ** argv) { modsToInstall[modCount + 1] = NULL; printf("Mounting the overlay\n"); int gameId = getGameIdFromAppId(appid); + + GHashTable * gamePaths; + error_t lookup_status = search_games(&gamePaths); + if(lookup_status == ERR_FAILURE) { + free(home); + return ERR_FAILURE; + } + + const char * path = g_hash_table_lookup(gamePaths, &gameId); char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); @@ -353,6 +372,13 @@ static int setup(int argc, char ** argv) { free(gameUpperDir); free(gameWorkDir); + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + free(home); + return ERR_FAILURE; + } + const char * path = g_hash_table_lookup(gamePaths, &gameId); char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); @@ -393,6 +419,12 @@ static int unbind(int argc, char ** argv) { if(appid < 0) { return EXIT_FAILURE; } + + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) + return EXIT_FAILURE; + int gameId = getGameIdFromAppId(appid); const char * path = g_hash_table_lookup(gamePaths, &gameId); char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); @@ -531,17 +563,7 @@ int main(int argc, char ** argv) { return EXIT_FAILURE; } - int searchStatus; - gamePaths = search_games(&searchStatus); - - int returnValue = EXIT_SUCCESS; - if(searchStatus == EXIT_FAILURE) { - returnValue = EXIT_FAILURE; - fprintf(stderr, "Error while looking up libraries, do you have steam installed ?"); - goto exit; - } - char * home = getHome(); char * configFolder = g_build_filename(home, MANAGER_FILES, NULL); free(home); @@ -554,18 +576,17 @@ int main(int argc, char ** argv) { if(getuid() == 0) { fprintf(stderr, "For the first run please avoid sudo\n"); returnValue = EXIT_FAILURE; - goto exit2; + goto exit; } else { //leading 0 == octal int i = g_mkdir_with_parents(configFolder, 0755); if(i < 0) { fprintf(stderr, "Could not create configs, check access rights for this path: %s", configFolder); - goto exit2; + goto exit; } } } - if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0) returnValue = listGames(argc, argv); @@ -613,9 +634,8 @@ int main(int argc, char ** argv) { returnValue = EXIT_FAILURE; } -exit2: - g_free(configFolder); - g_hash_table_destroy(gamePaths); exit: + g_free(configFolder); + freeGameTableSingleton(); return returnValue; } diff --git a/src/main.h b/src/main.h index 3aa9d13..a563bd6 100644 --- a/src/main.h +++ b/src/main.h @@ -1,9 +1,11 @@ #ifndef __MAIN_H__ #define __MAIN_H__ -//no function should ever be put here, only keep important macros here +#include -#define APP_NAME "modmanager" +//no function should ever be put here, only keep important macros and globals here + +#define APP_NAME "mod-manager" //relative to home the url is not preprocessed so ../ might create some issues // in c "A" "B" is the same as "AB" #define MANAGER_FILES ".local/share/" APP_NAME diff --git a/src/steam.c b/src/steam.c index 177d934..87ae8d1 100644 --- a/src/steam.c +++ b/src/steam.c @@ -1,6 +1,7 @@ #include "steam.h" #include "macro.h" #include "getHome.h" +#include "main.h" #include #include #include @@ -190,11 +191,21 @@ static void freeLibraries(ValveLibraries_t * libraries, int size) { free(libraries); } -GHashTable* search_games(int * status) { +static GHashTable* gameTableSingleton = NULL; + +void freeGameTableSingleton() { + if(gameTableSingleton != NULL)g_hash_table_destroy(gameTableSingleton); +} + +error_t search_games(GHashTable ** p_hashTable) { + if(gameTableSingleton != NULL) { + *p_hashTable = gameTableSingleton; + return ERR_SUCCESS; + } + ValveLibraries_t * libraries = NULL; size_t size = 0; char * home = getHome(); - *status = EXIT_SUCCESS; for(unsigned long i = 0; i < LEN(steamLibraries); i++) { char * path = g_build_filename(home, steamLibraries[i], "steamapps/libraryfolders.vdf", NULL); @@ -212,8 +223,7 @@ GHashTable* search_games(int * status) { free(home); if(libraries == NULL) { - *status = EXIT_FAILURE; - return NULL; + return ERR_FAILURE; } GHashTable* table = g_hash_table_new_full(g_int_hash, g_int_equal, free, free); @@ -231,7 +241,9 @@ GHashTable* search_games(int * status) { } freeLibraries(libraries, size); - return table; + *p_hashTable = table; + gameTableSingleton = table; + return ERR_SUCCESS; } diff --git a/src/steam.h b/src/steam.h index 880fe18..92f3e13 100644 --- a/src/steam.h +++ b/src/steam.h @@ -45,12 +45,14 @@ _Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Name /** * @brief list all installed games and the paths to the game's files + * 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 *) + * @return GHashTable* a map appid(int) => path(char *) to the corresponding steam library */ - //TODO: flip the return value and parameter -GHashTable* search_games(int * status); +error_t search_games(GHashTable** tablePointer); + +void freeGameTableSingleton(void); /** * @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS From f3cd47b2bbcbc1e6b400e88e59201f26c087be0e Mon Sep 17 00:00:00 2001 From: Marc Date: Fri, 7 Oct 2022 14:18:13 +0200 Subject: [PATCH 15/18] [Untested] Experimental plugin order management support --- src/file.c | 22 ++++++++ src/file.h | 17 ++++++ src/install.c | 23 +------- src/loadOrder.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++ src/loadOrder.h | 11 ++++ 5 files changed, 192 insertions(+), 22 deletions(-) create mode 100644 src/loadOrder.c create mode 100644 src/loadOrder.h diff --git a/src/file.c b/src/file.c index e395b1b..b070169 100644 --- a/src/file.c +++ b/src/file.c @@ -128,3 +128,25 @@ void casefold(const char * folder) { closedir(d); } } + +const char * extractLastPart(const char * filePath, const char delimeter) { + const int length = strlen(filePath); + long index = -1; + for(long i= length - 1; i >= 0; i--) { + if(filePath[i] == delimeter) { + index = i + 1; + break; + } + } + + if(index <= 0 || index == length) return NULL; + return &filePath[index]; +} + +const char * extractExtension(const char * filePath) { + return extractLastPart(filePath, '.'); +} + +const char * extractFileName(const char * filePath) { + return extractLastPart(filePath, '/'); +} diff --git a/src/file.h b/src/file.h index 68c65bd..1f70f64 100644 --- a/src/file.h +++ b/src/file.h @@ -45,4 +45,21 @@ int move(const char * source, const char * destination); */ void casefold(const char * folder); + +const char * extractLastPart(const char * filePath, const char delimeter); + +/** + * @brief Return the extension of a file by looking for the character '.' + * @param filePath + * @return return a pointer to the address after the '.' or null if it was not found; + */ +const char * extractExtension(const char * filePath); + +/** + * @brief Return the file name by looking for the last character '/' + * @param filePath + * @return return a pointer to the address after the last '/' or null if it was not found (the path might not be a path in this case) + */ +const char * extractFileName(const char * filePath); + #endif diff --git a/src/install.c b/src/install.c index a381b76..a720943 100644 --- a/src/install.c +++ b/src/install.c @@ -7,28 +7,7 @@ #include "install.h" #include "main.h" #include "archives.h" - -static const char * extractLastPart(const char * filePath, const char delimeter) { - const int length = strlen(filePath); - long index = -1; - for(long i= length - 1; i >= 0; i--) { - if(filePath[i] == delimeter) { - index = i + 1; - break; - } - } - - if(index <= 0 || index == length) return NULL; - return &filePath[index]; -} - -static const char * extractExtension(const char * filePath) { - return extractLastPart(filePath, '.'); -} - -static const char * extractFileName(const char * filePath) { - return extractLastPart(filePath, '/'); -} +#include "file.h" error_t addMod(char * filePath, int appId) { error_t resultError = ERR_SUCCESS; diff --git a/src/loadOrder.c b/src/loadOrder.c new file mode 100644 index 0000000..2c812dc --- /dev/null +++ b/src/loadOrder.c @@ -0,0 +1,141 @@ +#include "loadOrder.h" +#include "main.h" +#include "steam.h" +#include "file.h" + +#include +#include + +error_t listPlugins(int appid, GList ** plugins) { + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + return ERR_FAILURE; + } + + int gameId = getGameIdFromAppId(appid); + if(gameId < 0 ) { + return ERR_FAILURE; + } + + //save appid parsing + //TODO: apply a similar mechanism everywhere + size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1; + char appidStr[appidStrLen]; + sprintf(appidStr, "%d", appid); + + + + const char * path = g_hash_table_lookup(gamePaths, &appid); + char * steamGameFolder = g_build_filename(path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); + + //esp && esm files are loadable + DIR * d = opendir(steamGameFolder); + struct dirent *dir; + if (d) { + while ((dir = readdir(d)) != NULL) { + const char * extension = extractExtension(dir->d_name); + if(strcmp(extension, "esp") == 0 || strcmp(extension, "esm") == 0) { + *plugins = g_list_append(*plugins, strdup(dir->d_name)); + } + } + } + + g_free(steamGameFolder); + return ERR_SUCCESS; +} + +error_t getLoadOrder(int appid, GList ** order) { + + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + return ERR_FAILURE; + } + + GList * l_plugins = NULL; + error_t error = listPlugins(appid, &l_plugins); + if(error == ERR_FAILURE) + return ERR_FAILURE; + + + int gameId = getGameIdFromAppId(appid); + if(gameId < 0 ) { + return ERR_FAILURE; + } + + size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1; + 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); + + GList * l_currentLoadOrder = NULL; + if(access(loadOrderPath, F_OK)) { + FILE * f_loadOrder = fopen(loadOrderPath, "r"); + + size_t length = 0; + char * line = NULL; + + while(getline(&line, &length, f_loadOrder) > 0) { + if(line[0] == '#' || line[0] == '\n') + continue; + + l_currentLoadOrder = g_list_append(l_currentLoadOrder, strdup(line)); + } + } + + + GList * l_currentLoadOrderCursor = l_currentLoadOrder; + GList * l_completeLoadOrder = NULL; + + while(l_currentLoadOrderCursor != NULL) { + char * modName = l_currentLoadOrderCursor->data; + //TODO: finir sa + GList * mod = g_list_find_custom(l_plugins, modName, (GCompareFunc)strcmp); + if(mod == NULL) { + //The plugin is no longer installed + continue; + } else { + l_completeLoadOrder = g_list_append(l_completeLoadOrder, strdup(modName)); + } + l_currentLoadOrderCursor = g_list_next(l_currentLoadOrderCursor); + } + + *order = l_completeLoadOrder; + + g_list_free_full(l_plugins, free); + g_list_free_full(l_currentLoadOrder, free); + g_free(loadOrderPath); + return ERR_SUCCESS; +} + +error_t setLoadOrder(int appid, GList * loadOrder) { + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + return ERR_FAILURE; + } + + int gameId = getGameIdFromAppId(appid); + if(gameId < 0 ) { + return ERR_FAILURE; + } + + size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1; + 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); + + FILE * f_loadOrder = fopen(loadOrderPath, "w"); + while(loadOrder != NULL) { + fwrite(loadOrder->data, sizeof(char), strlen(loadOrder->data), f_loadOrder); + loadOrder = g_list_next(loadOrder); + } + fclose(f_loadOrder); + + return ERR_SUCCESS; +} diff --git a/src/loadOrder.h b/src/loadOrder.h new file mode 100644 index 0000000..2b17a68 --- /dev/null +++ b/src/loadOrder.h @@ -0,0 +1,11 @@ +#ifndef __LOAD_ORDER_H__ +#define __LOAD_ORDER_H__ + +#include +#include "main.h" + +error_t listPlugins(int appid, GList ** list) __attribute__((warn_unused_result)); +error_t getLoadOrder(int appid, GList ** order) __attribute__((warn_unused_result)); +error_t setLoadOrder(int appid, GList * loadOrder) __attribute__((warn_unused_result)); + +#endif From 9ee12a6c7ef0c83d2870f5d0828d51c0a0a2e446 Mon Sep 17 00:00:00 2001 From: Marc Date: Sat, 8 Oct 2022 10:59:32 +0200 Subject: [PATCH 16/18] Added loadOrder support + fixed some bug add the getDataPath function / .h that will find the data folder of the game. this fixe some issue with older games like morrowind --- CMakeLists.txt | 2 ++ src/getDataPath.c | 40 +++++++++++++++++++++ src/getDataPath.h | 15 ++++++++ src/loadOrder.c | 88 ++++++++++++++++++++++++++++++++++++++--------- src/loadOrder.h | 25 ++++++++++++++ src/main.c | 54 +++++++++++------------------ src/steam.h | 6 ++-- 7 files changed, 179 insertions(+), 51 deletions(-) create mode 100644 src/getDataPath.c create mode 100644 src/getDataPath.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 777ab41..d9dc092 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,10 @@ project(mod-manager) set(SOURCES src/steam.c + src/loadOrder.c src/install.c src/overlayfs.c + src/getDataPath.c src/getHome.c src/file.c src/order.c diff --git a/src/getDataPath.c b/src/getDataPath.c new file mode 100644 index 0000000..efcc343 --- /dev/null +++ b/src/getDataPath.c @@ -0,0 +1,40 @@ + +#include "getDataPath.h" +#include "main.h" +#include "steam.h" + +#include +#include + +error_t getDataPath(int appid, char ** destination) { + GHashTable * gamePaths; + error_t status = search_games(&gamePaths); + if(status == ERR_FAILURE) { + return ERR_FAILURE; + } + + int gameId = getGameIdFromAppId(appid); + if(gameId < 0 ) { + 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; + if(access(dataFolderOld, F_OK) == 0) { + *destination = strdup(dataFolderOld); + } else if(access(dataFolderNew, F_OK) == 0) { + *destination = strdup(dataFolderOld); + } + + g_free(dataFolderNew); + g_free(dataFolderOld); + + if(*destination == NULL) return ERR_FAILURE; + else return ERR_SUCCESS; +} diff --git a/src/getDataPath.h b/src/getDataPath.h new file mode 100644 index 0000000..b0dcca7 --- /dev/null +++ b/src/getDataPath.h @@ -0,0 +1,15 @@ +#ifndef __DATA_PATH_H__ +#define __DATA_PATH_H__ + +#include "main.h" + +/** + * @brief Get the path to the data folder + * @param appid appid of the game + * @param destination pointer to a null char * variable. it's value will be allocated with malloc + * @return error_t + */ +error_t getDataPath(int appid, char ** destination); + + +#endif diff --git a/src/loadOrder.c b/src/loadOrder.c index 2c812dc..d906083 100644 --- a/src/loadOrder.c +++ b/src/loadOrder.c @@ -4,19 +4,16 @@ #include "file.h" #include +#include #include -error_t listPlugins(int appid, GList ** plugins) { - GHashTable * gamePaths; - error_t status = search_games(&gamePaths); - if(status == ERR_FAILURE) { - return ERR_FAILURE; - } +#include "getDataPath.h" + +//TODO: detect if the game is running +//TODO: deploy the game + +error_t listPlugins(int appid, GList ** plugins) { - int gameId = getGameIdFromAppId(appid); - if(gameId < 0 ) { - return ERR_FAILURE; - } //save appid parsing //TODO: apply a similar mechanism everywhere @@ -24,13 +21,14 @@ error_t listPlugins(int appid, GList ** plugins) { char appidStr[appidStrLen]; sprintf(appidStr, "%d", appid); - - - const char * path = g_hash_table_lookup(gamePaths, &appid); - char * steamGameFolder = g_build_filename(path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); + char * dataFolder = NULL; + error_t error = getDataPath(appid, &dataFolder); + if(error != ERR_SUCCESS) { + return ERR_FAILURE; + } //esp && esm files are loadable - DIR * d = opendir(steamGameFolder); + DIR * d = opendir(dataFolder); struct dirent *dir; if (d) { while ((dir = readdir(d)) != NULL) { @@ -41,7 +39,7 @@ error_t listPlugins(int appid, GList ** plugins) { } } - g_free(steamGameFolder); + free(dataFolder); return ERR_SUCCESS; } @@ -139,3 +137,61 @@ error_t setLoadOrder(int appid, GList * loadOrder) { return ERR_SUCCESS; } + +//TODO: support compression since it can change how we read the file +//https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format#Records +//https://www.mwmythicmods.com/argent/tech/es_format.html +error_t getModDependencies(const char * esmPath, GList ** dependencies) { + FILE * file = fopen(esmPath, "r"); + + char sectionName[5]; + sectionName[4] = '\0'; + fread(sectionName, sizeof(char), 4, file); + + size_t sizeFieldSize = 0; + u_int8_t recordHeaderToIgnore = 0; + + //the field "length" in the sub-record change between games. + //TES4 => Fallout4 Oblivion Skyrim(+SE) + //TES3 => Morrowind + if(strcmp(sectionName, "TES3") == 0) { + printf("Using tes3 file format\n"); + recordHeaderToIgnore = 8; + sizeFieldSize = 4; + } else if(strcmp(sectionName, "TES4") == 0) { + printf("Using tes4 file format\n"); + recordHeaderToIgnore = 16; + sizeFieldSize = 2; + } else { + fprintf(stderr, "Unrecognized file format %s\n", sectionName); + fclose(file); + return ERR_FAILURE; + } + + + u_int32_t lengthVal = 0; + fread(&lengthVal, 4, 1, file); + //ignore the rest of the data + fseek(file, recordHeaderToIgnore, SEEK_CUR); + + int64_t length = lengthVal; + while(length > 0) { + char sectionName[5]; + sectionName[4] = '\0'; + fread(sectionName, sizeof(char), 4, file); + unsigned long subsectionLength = 0; + fread(&subsectionLength, sizeFieldSize, 1, file); + + length -= 8 + subsectionLength; + if(strcmp(sectionName, "MAST") == 0) { + char * dependency = malloc(subsectionLength + 1); + dependency[subsectionLength] = '\0'; + fread(dependency, sizeof(char), subsectionLength, file); + *dependencies = g_list_append(*dependencies, dependency); + } else { + fseek(file, subsectionLength, SEEK_CUR); + } + } + fclose(file); + return ERR_SUCCESS; +} diff --git a/src/loadOrder.h b/src/loadOrder.h index 2b17a68..9b07adb 100644 --- a/src/loadOrder.h +++ b/src/loadOrder.h @@ -4,8 +4,33 @@ #include #include "main.h" +/** + * @brief find all plugins in the data folder of the game + * @param appid the appid of the game + * @param order a pointer to a null Glist *. in which there will be the list of esm files. + */ error_t listPlugins(int appid, GList ** list) __attribute__((warn_unused_result)); + +/** + * @brief fetch the load order of the game it might be null if setLoadOrder was never called. + * @param appid the appid of the game + * @param order a pointer to a null Glist *. in which there will be the list of esm files. + */ error_t getLoadOrder(int appid, GList ** order) __attribute__((warn_unused_result)); + +/** + * @brief change the plugin load order of the game + * @param appid the appid of the game + * @param loadOrder the load order + */ error_t setLoadOrder(int appid, GList * loadOrder) __attribute__((warn_unused_result)); +/** + * @brief List all dependencies for a esm mod. + * @param esmPath path to the esm file + * @param dependencies a pointer to a null Glist *. in which there will be the list of esm files. + * free it using g_list_free_full(dependencies, free); + */ +error_t getModDependencies(const char * esmPath, GList ** dependencies) __attribute__((warn_unused_result)); + #endif diff --git a/src/main.c b/src/main.c index 46591f0..b163327 100644 --- a/src/main.c +++ b/src/main.c @@ -8,6 +8,7 @@ #include #include +#include "getDataPath.h" #include "overlayfs.h" #include "install.h" #include "getHome.h" @@ -312,19 +313,14 @@ static int deploy(int argc, char ** argv) { modsToInstall[modCount] = gameFolder; modsToInstall[modCount + 1] = NULL; printf("Mounting the overlay\n"); - int gameId = getGameIdFromAppId(appid); - GHashTable * gamePaths; - error_t lookup_status = search_games(&gamePaths); - if(lookup_status == ERR_FAILURE) { + char * dataFolder = NULL; + error_t error = getDataPath(appid, &dataFolder); + if(error != ERR_SUCCESS) { free(home); return ERR_FAILURE; } - - const char * path = g_hash_table_lookup(gamePaths, &gameId); - char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); - char * gameUpperDir = g_build_filename(home, MANAGER_FILES, GAME_UPPER_DIR_NAME, appIdStr, NULL); char * gameWorkDir = g_build_filename(home, MANAGER_FILES, GAME_WORK_DIR_NAME, appIdStr, NULL); free(home); @@ -333,8 +329,8 @@ static int deploy(int argc, char ** argv) { //DETACH + FORCE allow us to be sure it will be unload. //it might crash / corrupt game file if the user do it while the game is running //but it's still very unlikely - while(umount2(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0); - enum overlayErrors status = overlayMount(modsToInstall, steamGameFolder, gameUpperDir, gameWorkDir); + while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0); + enum overlayErrors status = overlayMount(modsToInstall, dataFolder, gameUpperDir, gameWorkDir); if(status == SUCESS) { printf("Everything is ready, just launch the game\n"); } else if(status == FAILURE) { @@ -348,7 +344,7 @@ static int deploy(int argc, char ** argv) { return EXIT_FAILURE; } - g_free(steamGameFolder); + g_free(dataFolder); g_free(gameUpperDir); g_free(gameWorkDir); @@ -362,7 +358,6 @@ static int setup(int argc, char ** argv) { if(appid < 0) { return EXIT_FAILURE; } - int gameId = getGameIdFromAppId(appid); char * home = getHome(); char * gameUpperDir = g_build_filename(home, MANAGER_FILES, GAME_UPPER_DIR_NAME, appIdStr, NULL); @@ -372,16 +367,12 @@ static int setup(int argc, char ** argv) { free(gameUpperDir); free(gameWorkDir); - GHashTable * gamePaths; - error_t status = search_games(&gamePaths); - if(status == ERR_FAILURE) { - free(home); + char * dataFolder = NULL; + error_t error = getDataPath(appid, &dataFolder); + if(error != ERR_SUCCESS) { return ERR_FAILURE; } - const char * path = g_hash_table_lookup(gamePaths, &gameId); - char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); - char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL); if(access(gameFolder, F_OK) == 0) { //if the game folder alredy exists just delete it @@ -392,20 +383,20 @@ static int setup(int argc, char ** argv) { //links don't conflict with overlayfs and avoid coping 17Gb of files. //but links require the files to be on the same filesystem - int returnValue = copy(steamGameFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR | CP_LINK); + int returnValue = copy(dataFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR | CP_LINK); if(returnValue < 0) { printf("Coping game files. HINT: having the game on the same partition as you home director will make this operation use zero extra space"); - returnValue = copy(steamGameFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR); + returnValue = copy(dataFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR); if(returnValue < 0) { fprintf(stderr, "Copy failed make sure you have enough space on your device."); - free(steamGameFolder); + free(dataFolder); free(gameFolder); return EXIT_FAILURE; } } casefold(gameFolder); - free(steamGameFolder); + free(dataFolder); free(gameFolder); printf("Done\n"); return EXIT_SUCCESS; @@ -420,18 +411,15 @@ static int unbind(int argc, char ** argv) { return EXIT_FAILURE; } - GHashTable * gamePaths; - error_t status = search_games(&gamePaths); - if(status == ERR_FAILURE) - return EXIT_FAILURE; + char * dataFolder = NULL; + error_t error = getDataPath(appid, &dataFolder); + if(error != ERR_SUCCESS) { + return ERR_FAILURE; + } - int gameId = getGameIdFromAppId(appid); - const char * path = g_hash_table_lookup(gamePaths, &gameId); - char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); + while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0); - while(umount2(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0); - - free(steamGameFolder); + free(dataFolder); return EXIT_SUCCESS; } diff --git a/src/steam.h b/src/steam.h index 92f3e13..7866ec1 100644 --- a/src/steam.h +++ b/src/steam.h @@ -31,14 +31,16 @@ typedef struct ValveLibraries { static const u_int32_t GAMES_APPIDS[] = { 489830, 22330, - 377160 + 377160, + 22320 }; //the name of the game in the steamapps/common folder static const char * GAMES_NAMES[] = { "Skyrim Special Edition", "Oblivion", - "Fallout 4" + "Fallout 4", + "Morrowind" }; _Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Names doesn't match"); From b8affcadbcdc572b150c0f2c8dc7037f1f7a9bb8 Mon Sep 17 00:00:00 2001 From: Marc Date: Sun, 9 Oct 2022 15:46:58 +0200 Subject: [PATCH 17/18] New naming conventions to avoid conflicts. i don't know if it was such a great idea but at least it is a starting point. --- naming_conventions.md | 19 ++++++ src/archives.c | 8 +-- src/archives.h | 6 +- src/file.c | 28 ++++----- src/file.h | 22 +++---- src/fomod.c | 129 ++++++++++++++++++++++++++++------------- src/fomod.h | 15 +++-- src/fomod/fomodTypes.h | 16 ++--- src/fomod/group.c | 59 ++++++++++--------- src/fomod/group.h | 21 +++---- src/fomod/parser.c | 116 +++++++++++------------------------- src/fomod/parser.h | 19 +++--- src/fomod/xmlUtil.c | 10 ++-- src/fomod/xmlUtil.h | 12 ++-- src/getDataPath.c | 4 +- src/install.c | 12 ++-- src/install.h | 2 +- src/loadOrder.c | 20 +++---- src/loadOrder.h | 8 +-- src/main.c | 31 +++++----- src/order.c | 6 +- src/order.h | 4 +- src/overlayfs.c | 4 +- src/overlayfs.h | 4 +- src/steam.c | 24 ++++---- src/steam.h | 16 ++--- 26 files changed, 320 insertions(+), 295 deletions(-) create mode 100644 naming_conventions.md diff --git a/naming_conventions.md b/naming_conventions.md new file mode 100644 index 0000000..2b090e1 --- /dev/null +++ b/naming_conventions.md @@ -0,0 +1,19 @@ +# Current naming conventions (subject to debate) + +## Function names + +### shared functions + +function should start by the name of their module in camelCase followed by an underscore and the name of the function in camelCase + +example: + steam_searchGames() + + +### static functions + +there is no rule for now + +## Struct names + +function should start by the name of their module in camelCase followed by an underscore and the name of the struct in PascalCase diff --git a/src/archives.c b/src/archives.c index fabd680..b774baf 100644 --- a/src/archives.c +++ b/src/archives.c @@ -8,7 +8,7 @@ #include #include -int unzip(char * path, char * outdir) { +int archive_unzip(char * path, char * outdir) { char * const args[] = { "unzip", "-LL", // to lowercase @@ -35,7 +35,7 @@ int unzip(char * path, char * outdir) { } } -int unrar(char * path, char * outdir) { +int archive_unrar(char * path, char * outdir) { char * const args[] = { "unrar", "x", @@ -63,7 +63,7 @@ int unrar(char * path, char * outdir) { } -int un7z(char * path, const char * outdir) { +int archive_un7z(char * path, const char * outdir) { gchar * outParameter = g_strjoin("", "-o", outdir, NULL); char * const args[] = { @@ -90,7 +90,7 @@ int un7z(char * path, const char * outdir) { return returnValue; } //make everything lowercase since 7z don't have an argument for that. - casefold(outdir); + file_casefold(outdir); return returnValue; } } diff --git a/src/archives.h b/src/archives.h index 01032a5..d1be6d1 100644 --- a/src/archives.h +++ b/src/archives.h @@ -9,7 +9,7 @@ * @param outdir output director * @return int return code */ -int unzip(char * path, char * outdir); +int archive_unzip(char * path, char * outdir); /** * @brief Execute the unrar command @@ -17,7 +17,7 @@ int unzip(char * path, char * outdir); * @param outdir output director * @return int return code */ -int unrar(char * path, char * outdir); +int archive_unrar(char * path, char * outdir); /** * @brief Execute the 7z command @@ -25,6 +25,6 @@ int unrar(char * path, char * outdir); * @param outdir output director * @return int return code */ -int un7z(char * path, const char * outdir); +int archive_un7z(char * path, const char * outdir); #endif diff --git a/src/file.c b/src/file.c index b070169..30d7ca6 100644 --- a/src/file.c +++ b/src/file.c @@ -19,7 +19,7 @@ static u_int32_t countSetBits(u_int32_t n) { //TODO: add interruption support //simplest way to copy a file in c(linux) -int copy(const char * path, const char * dest, u_int32_t flags) { +int file_copy(const char * path, const char * dest, u_int32_t flags) { int flagCount = countSetBits(flags); if(flagCount > 3) { fprintf(stderr, "Invalid flags for cp command\n"); @@ -34,15 +34,15 @@ int copy(const char * path, const char * dest, u_int32_t flags) { strcpy(args[2], dest); int argIndex = 3; - if(flags & CP_LINK) { + if(flags & FILE_CP_LINK) { args[argIndex] = "--link"; argIndex += 1; } - if(flags & CP_RECURSIVE) { + if(flags & FILE_CP_RECURSIVE) { args[argIndex] = "-r"; argIndex += 1; } - if(flags & CP_NO_TARGET_DIR) { + if(flags & FILE_CP_NO_TARGET_DIR) { args[argIndex] = "-T"; argIndex += 1; } @@ -61,7 +61,7 @@ int copy(const char * path, const char * dest, u_int32_t flags) { } } -int delete(const char * path, bool recursive) { +int file_delete(const char * path, bool recursive) { int pid = fork(); if(pid == 0) { if(recursive) { @@ -77,7 +77,7 @@ int delete(const char * path, bool recursive) { } } -int move(const char * source, const char * destination) { +int file_move(const char * source, const char * destination) { int pid = fork(); if(pid == 0) { execl("/bin/mv", "/bin/mv", source, destination, NULL); @@ -92,7 +92,7 @@ int move(const char * source, const char * destination) { //rename a folder and all subfolder and files to lowercase //TODO: error handling -void casefold(const char * folder) { +void file_casefold(const char * folder) { DIR * d = opendir(folder); struct dirent *dir; if (d) { @@ -108,7 +108,7 @@ void casefold(const char * folder) { if(strcmp(destinationName, dir->d_name) != 0) { - int result = move(file, destination); + int result = file_move(file, destination); if(result != EXIT_SUCCESS) { fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName); } @@ -119,7 +119,7 @@ void casefold(const char * folder) { g_free(destinationName); if(dir->d_type == DT_DIR) { - casefold(destination); + file_casefold(destination); } g_free(destination); @@ -129,7 +129,7 @@ void casefold(const char * folder) { } } -const char * extractLastPart(const char * filePath, const char delimeter) { +const char * file_extractLastPart(const char * filePath, const char delimeter) { const int length = strlen(filePath); long index = -1; for(long i= length - 1; i >= 0; i--) { @@ -143,10 +143,10 @@ const char * extractLastPart(const char * filePath, const char delimeter) { return &filePath[index]; } -const char * extractExtension(const char * filePath) { - return extractLastPart(filePath, '.'); +const char * file_extractExtension(const char * filePath) { + return file_extractLastPart(filePath, '.'); } -const char * extractFileName(const char * filePath) { - return extractLastPart(filePath, '/'); +const char * file_extractFileName(const char * filePath) { + return file_extractLastPart(filePath, '/'); } diff --git a/src/file.h b/src/file.h index 1f70f64..57d5618 100644 --- a/src/file.h +++ b/src/file.h @@ -7,10 +7,10 @@ //valid copy flags -#define cp_DEFAULT 0 -#define CP_LINK 1 -#define CP_RECURSIVE 2 -#define CP_NO_TARGET_DIR 4 +#define FILE_CP_DEFAULT 0 +#define FILE_CP_LINK 1 +#define FILE_CP_RECURSIVE 2 +#define FILE_CP_NO_TARGET_DIR 4 /** * @brief execute the cp command from the source to the dest @@ -19,7 +19,7 @@ * @param flags refer to the "valid copy flags" use them like this CP_LINK | CP_RECURSIVE * @return cp return value */ -int copy(const char * source, const char * dest, u_int32_t flags); +int file_copy(const char * source, const char * dest, u_int32_t flags); /** * @brief execute the cp command from the source to the dest @@ -28,7 +28,7 @@ int copy(const char * source, const char * dest, u_int32_t flags); * @param bool enable recursive rm -r * @return rm return value */ -int delete(const char * path, bool recursive); +int file_delete(const char * path, bool recursive); /** * @brief Run the mv command @@ -36,30 +36,30 @@ int delete(const char * path, bool recursive); * @param destination * @return mv's exit value */ -int move(const char * source, const char * destination); +int file_move(const char * source, const char * destination); /** * @brief Recursively rename all file and folder to lowercase. * * @param folder */ -void casefold(const char * folder); +void file_casefold(const char * folder); -const char * extractLastPart(const char * filePath, const char delimeter); +const char * file_extractLastPart(const char * filePath, const char delimeter); /** * @brief Return the extension of a file by looking for the character '.' * @param filePath * @return return a pointer to the address after the '.' or null if it was not found; */ -const char * extractExtension(const char * filePath); +const char * file_extractExtension(const char * filePath); /** * @brief Return the file name by looking for the last character '/' * @param filePath * @return return a pointer to the address after the last '/' or null if it was not found (the path might not be a path in this case) */ -const char * extractFileName(const char * filePath); +const char * file_extractFileName(const char * filePath); #endif diff --git a/src/fomod.c b/src/fomod.c index e8ffb20..11375f7 100644 --- a/src/fomod.c +++ b/src/fomod.c @@ -13,6 +13,7 @@ #include "fomod.h" #include "file.h" #include "fomod/fomodTypes.h" +#include "fomod/group.h" #include "libxml/globals.h" #include "main.h" @@ -43,20 +44,20 @@ static int getInputCount(const char * input) { } static gint priorityCmp(gconstpointer a, gconstpointer b) { - const FOModFile_t * fileA = (const FOModFile_t *)a; - const FOModFile_t * fileB = (const FOModFile_t *)b; + const fomod_File_t * fileA = (const fomod_File_t *)a; + const fomod_File_t * fileB = (const fomod_File_t *)b; return fileB->priority - fileA->priority; } -static void printfOptionsInOrder(FOModGroup_t group) { +static void fomod_printOptionsInOrder(fomod_Group_t group) { for(int i = 0; i < group.pluginCount; i++) { printf("%d, %s\n", i, group.plugins[i].name); printf("%s\n", group.plugins[i].description); } } -static gint flagEqual(const FOModFlag_t * a, const FOModFlag_t * b) { +static gint fomod_flagEqual(const fomod_Flag_t * a, const fomod_Flag_t * b) { int nameCmp = strcmp(a->name, b->name); if(nameCmp == 0) { if(strcmp(a->value, b->value) == 0) @@ -81,7 +82,7 @@ static int stepCmpDesc(const void * stepA, const void * stepB) { } -static void sortSteps(FOMod_t * fomod) { +static void fomod_sortSteps(FOMod_t * fomod) { switch(fomod->stepOrder) { case ASC: qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpAsc); @@ -95,25 +96,25 @@ static void sortSteps(FOMod_t * fomod) { } } -static int groupCmpAsc(const void * stepA, const void * stepB) { - const FOModGroup_t * step1 = (const FOModGroup_t *)stepA; - const FOModGroup_t * step2 = (const FOModGroup_t *)stepB; +static int fomod_groupCmpAsc(const void * stepA, const void * stepB) { + const fomod_Group_t * step1 = (const fomod_Group_t *)stepA; + const fomod_Group_t * step2 = (const fomod_Group_t *)stepB; return strcmp(step1->name, step2->name); } -static int groupCmpDesc(const void * stepA, const void * stepB) { - const FOModGroup_t * step1 = (const FOModGroup_t *)stepA; - const FOModGroup_t * step2 = (const FOModGroup_t *)stepB; +static int fomod_groupCmpDesc(const void * stepA, const void * stepB) { + const fomod_Group_t * step1 = (const fomod_Group_t *)stepA; + const fomod_Group_t * step2 = (const fomod_Group_t *)stepB; return 1 - strcmp(step1->name, step2->name); } -static void sortGroup(FOModGroup_t * group) { +static void fomod_sortGroup(fomod_Group_t * group) { switch(group->order) { case ASC: - qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpAsc); + qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpAsc); break; case DESC: - qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpDesc); + qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpDesc); break; case ORD: //ord mean that we keep the curent order, so no need to sort anything. @@ -122,7 +123,7 @@ static void sortGroup(FOModGroup_t * group) { } //TODO: handle error -error_t processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) { +error_t fomod_processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) { //priority higher a less important and should be processed first. *pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp); GList * currentFileOperation = *pendingFileOperations; @@ -130,17 +131,17 @@ error_t processFileOperations(GList ** pendingFileOperations, const char * modFo while(currentFileOperation != NULL) { //TODO: support destination //no using link since priority is made to override files and link is annoying to deal with when overriding files. - const FOModFile_t * file = (const FOModFile_t *)currentFileOperation->data; + const fomod_File_t * file = (const fomod_File_t *)currentFileOperation->data; char * source = g_build_path("/", modFolder, file->source, NULL); //fix the / and \ windows - unix paths - fixPath(source); + xml_fixPath(source); int copyResult; if(file->isFolder) { - copyResult = copy(source, destination, CP_NO_TARGET_DIR | CP_RECURSIVE); + copyResult = file_copy(source, destination, FILE_CP_NO_TARGET_DIR | FILE_CP_RECURSIVE); } else { - copyResult = copy(source, destination, 0); + copyResult = file_copy(source, destination, 0); } if(copyResult != EXIT_SUCCESS) { fprintf(stderr, "Copy failed, some file might be corrupted\n"); @@ -152,15 +153,15 @@ error_t processFileOperations(GList ** pendingFileOperations, const char * modFo return ERR_SUCCESS; } -GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) { +GList * fomod_processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) { for(int condId = 0; condId < fomod->condFilesCount; condId++) { - const FOModCondFile_t *condFile = &fomod->condFiles[condId]; + const fomod_CondFile_t *condFile = &fomod->condFiles[condId]; bool areAllFlagsValid = true; //checking if all flags are valid for(long flagId = 0; flagId < condFile->flagCount; flagId++) { - const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)flagEqual); + const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)fomod_flagEqual); if(link == NULL) { areAllFlagsValid = false; break; @@ -169,9 +170,9 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin if(areAllFlagsValid) { for(long fileId = 0; fileId < condFile->flagCount; fileId++) { - const FOModFile_t * file = &(condFile->files[fileId]); + const fomod_File_t * file = &(condFile->files[fileId]); - FOModFile_t * fileCopy = malloc(sizeof(*file)); + fomod_File_t * fileCopy = malloc(sizeof(*file)); *fileCopy = *file; //changing pathes to lowercase since we used casefold and the pathes in the xml might not like it @@ -188,10 +189,10 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin return pendingFileOperations; } -void freeFileOperations(GList * fileOperations) { +void fomod_freeFileOperations(GList * fileOperations) { GList * fileOperationsStart = fileOperations; while(fileOperations != NULL) { - FOModFile_t * file = (FOModFile_t *)fileOperations->data; + fomod_File_t * file = (fomod_File_t *)fileOperations->data; if(file->destination != NULL)free(file->destination); if(file->source != NULL)free(file->source); fileOperations = g_list_next(fileOperations); @@ -200,7 +201,7 @@ void freeFileOperations(GList * fileOperations) { g_list_free_full(fileOperationsStart, free); } -error_t installFOMod(const char * modFolder, const char * destination) { +error_t fomod_installFOMod(const char * modFolder, const char * destination) { //everything should be lowercase since we use casefold() before calling any install function char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL); char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL); @@ -213,7 +214,7 @@ error_t installFOMod(const char * modFolder, const char * destination) { } FOMod_t fomod; - int returnValue = parseFOMod(fomodFile, &fomod); + int returnValue = parser_parseFOMod(fomodFile, &fomod); if(returnValue == ERR_FAILURE) return ERR_FAILURE; @@ -222,14 +223,14 @@ error_t installFOMod(const char * modFolder, const char * destination) { GList * flagList = NULL; GList * pendingFileOperations = NULL; - sortSteps(&fomod); + fomod_sortSteps(&fomod); for(int i = 0; i < fomod.stepCount; i++) { const FOModStep_t * step = &fomod.steps[i]; bool validFlags = true; for(int flagId = 0; flagId < step->flagCount; flagId++) { - const GList * flagLink = g_list_find_custom(flagList, &step->requiredFlags[flagId], (GCompareFunc)flagEqual); + const GList * flagLink = g_list_find_custom(flagList, &step->requiredFlags[flagId], (GCompareFunc)fomod_flagEqual); if(flagLink == NULL) { validFlags = false; break; @@ -239,9 +240,9 @@ error_t installFOMod(const char * modFolder, const char * destination) { if(!validFlags) continue; for(int groupId = 0; groupId < step->groupCount; groupId++ ) { - FOModGroup_t group = step->groups[groupId]; + fomod_Group_t group = step->groups[groupId]; - sortGroup(&group); + fomod_sortGroup(&group); u_int8_t min; u_int8_t max; @@ -250,7 +251,7 @@ error_t installFOMod(const char * modFolder, const char * destination) { size_t bufferSize = 0; while(true) { - printfOptionsInOrder(group); + fomod_printOptionsInOrder(group); switch(group.type) { case ONE_ONLY: printf("Select one :\n"); @@ -312,16 +313,16 @@ error_t installFOMod(const char * modFolder, const char * destination) { for(int choiceId = 0; choices[choiceId] != NULL; choiceId++) { //TODO: safer user input int choice = atoi(choices[choiceId]); - FOModPlugin_t plugin = group.plugins[choice]; + fomod_Plugin_t plugin = group.plugins[choice]; for(int flagId = 0; flagId < plugin.flagCount; flagId++) { flagList = g_list_append(flagList, &plugin.flags[flagId]); } //do the install for(int pluginId = 0; pluginId < plugin.fileCount; pluginId++) { - const FOModFile_t * file = &plugin.files[pluginId]; + const fomod_File_t * file = &plugin.files[pluginId]; - FOModFile_t * fileCopy = malloc(sizeof(FOModFile_t)); + fomod_File_t * fileCopy = malloc(sizeof(fomod_File_t)); *fileCopy = *file; //changing pathes to lowercase since we used casefold and the pathes in the xml might not like it @@ -344,13 +345,61 @@ error_t installFOMod(const char * modFolder, const char * destination) { //TODO: manage multiple files with the same name - pendingFileOperations = processCondFiles(&fomod, flagList, pendingFileOperations); - processFileOperations(&pendingFileOperations, modFolder, destination); + pendingFileOperations = fomod_processCondFiles(&fomod, flagList, pendingFileOperations); + fomod_processFileOperations(&pendingFileOperations, modFolder, destination); printf("FOMod successfully installed!\n"); g_list_free(flagList); - freeFileOperations(pendingFileOperations); - freeFOMod(&fomod); + fomod_freeFileOperations(pendingFileOperations); + fomod_freeFOMod(&fomod); g_free(fomodFolder); return ERR_SUCCESS; } + + +void fomod_freeFOMod(FOMod_t * fomod) { + for(int i = 0; i < fomod->condFilesCount; i++) { + fomod_CondFile_t * condFile = &(fomod->condFiles[i]); + for(long fileId = 0; fileId < condFile->fileCount; fileId++) { + free(condFile->files[fileId].destination); + free(condFile->files[fileId].source); + } + + for(long flagId = 0; flagId < condFile->flagCount; flagId++) { + fomod_Flag_t * flag = &(condFile->requiredFlags[flagId]); + free(flag->name); + free(flag->value); + } + free(condFile->files); + free(condFile->requiredFlags); + } + free(fomod->condFiles); + free(fomod->moduleImage); + free(fomod->moduleName); + + int size = fomod_countUntilNull(fomod->requiredInstallFiles, sizeof(char **)); + for(int i = 0; i < size; i++) { + free(fomod->requiredInstallFiles[i]); + } + free(fomod->requiredInstallFiles); + + for(int i = 0; i < fomod->stepCount; i++) { + FOModStep_t * step = &fomod->steps[i]; + for(int groupId = 0; groupId < step->groupCount; groupId++) { + fomod_Group_t * group = &step->groups[groupId]; + grp_freeGroup(group); + } + for(int flagId = 0; flagId < step->flagCount; flagId++) { + fomod_Flag_t * flag = &(step->requiredFlags[flagId]); + free(flag->name); + free(flag->value); + } + free(step->groups); + free(step->requiredFlags); + free(step->name); + } + free(fomod->steps); + + //set every counter to zero and every pointer to null + memset(fomod, 0, sizeof(FOMod_t)); +} diff --git a/src/fomod.h b/src/fomod.h index 30d5386..abd70bd 100644 --- a/src/fomod.h +++ b/src/fomod.h @@ -16,7 +16,7 @@ * @param destination folder of the new mod that contains the result of the fomod process. * @return int */ -error_t installFOMod(const char * modFolder, const char * destination); +error_t fomod_installFOMod(const char * modFolder, const char * destination); /** * @brief In fomod there is file operations which depends on multiple flags this function find the ones that mach our current flags and append them to a list. @@ -26,7 +26,7 @@ error_t installFOMod(const char * modFolder, const char * destination); * @param pendingFileOperations a list of pending FOModFile_t operation to which add the new ones. (can be null) * @return a list of pendingFileOperations(FOModFile_t) */ -GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) __attribute__((warn_unused_result)); +GList * fomod_processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) __attribute__((warn_unused_result)); /** * @brief FOModFile_t have a priority option and this function execute the file operation while taking this into account. @@ -36,13 +36,20 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin * @param destination folder of the new mod that contains the result of the process. * @return error code */ -error_t processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination); +error_t fomod_processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination); /** * @brief * * @param fileOperations */ -void freeFileOperations(GList * fileOperations); +void fomod_freeFileOperations(GList * fileOperations); + + +/** + * @brief Free content of a fomod file. + * @param fomod + */ +void fomod_freeFOMod(FOMod_t * fomod); #endif diff --git a/src/fomod/fomodTypes.h b/src/fomod/fomodTypes.h index 616f4a8..aa20d41 100644 --- a/src/fomod/fomodTypes.h +++ b/src/fomod/fomodTypes.h @@ -4,25 +4,25 @@ #include "stdbool.h" #include "xmlUtil.h" -typedef struct FOModFlag { +typedef struct fomod_Flag { char * name; char * value; -} FOModFlag_t; +} fomod_Flag_t; -typedef struct FOModFile { +typedef struct fomod_File { char * source; char * destination; int priority; bool isFolder; -} FOModFile_t; +} fomod_File_t; -typedef struct FOModCondFile { - FOModFlag_t * requiredFlags; +typedef struct fomod_CondFile { + fomod_Flag_t * requiredFlags; unsigned int flagCount; - FOModFile_t * files; + fomod_File_t * files; unsigned int fileCount; -} FOModCondFile_t; +} fomod_CondFile_t; #endif diff --git a/src/fomod/group.c b/src/fomod/group.c index 9ea650c..3b6b7f9 100644 --- a/src/fomod/group.c +++ b/src/fomod/group.c @@ -1,4 +1,5 @@ #include "group.h" +#include "fomodTypes.h" #include "xmlUtil.h" #include "string.h" #include @@ -35,11 +36,11 @@ static TypeDescriptor_t getDescriptor(const char * descriptor) { } } -void freeGroup(FOModGroup_t * group) { +void grp_freeGroup(fomod_Group_t * group){ free(group->name); if(group->pluginCount == 0) return; for(int pluginId = 0; pluginId < group->pluginCount; pluginId++) { - FOModPlugin_t * plugin = &group->plugins[pluginId]; + fomod_Plugin_t * plugin = &group->plugins[pluginId]; if(plugin->fileCount > 0) { for(int i = 0; i < plugin->fileCount; i++) { free(plugin->files[i].destination); @@ -65,10 +66,10 @@ void freeGroup(FOModGroup_t * group) { group->pluginCount = 0; } -static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { +static int parseConditionFlags(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) { xmlNodePtr flagNode = nodeElement->children; while(flagNode != NULL) { - if(!validateNode(&flagNode, true, "flag", NULL)) { + if(!xml_validateNode(&flagNode, true, "flag", NULL)) { if(plugin->flagCount > 0) { free(plugin->flags); } @@ -77,12 +78,12 @@ static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { if(flagNode == NULL)continue; plugin->flagCount += 1; - plugin->flags = realloc(plugin->flags, plugin->flagCount * sizeof(FOModFlag_t)); + plugin->flags = realloc(plugin->flags, plugin->flagCount * sizeof(fomod_Flag_t)); - FOModFlag_t * flag = &plugin->flags[plugin->flagCount - 1]; + fomod_Flag_t * flag = &plugin->flags[plugin->flagCount - 1]; - flag->name = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "name")); - flag->value = freeAndDup(xmlNodeGetContent(flagNode)); + flag->name = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "name")); + flag->value = xml_freeAndDup(xmlNodeGetContent(flagNode)); flagNode = flagNode->next; } @@ -90,10 +91,10 @@ static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { return EXIT_SUCCESS; } -static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { +static int parseGroupFiles(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) { xmlNodePtr fileNode = nodeElement->children; while(fileNode != NULL) { - if(!validateNode(&fileNode, true, "folder", "file", NULL)) { + if(!xml_validateNode(&fileNode, true, "folder", "file", NULL)) { fprintf(stderr, "Unexpected node in files"); //TODO: free return EXIT_FAILURE; @@ -103,11 +104,11 @@ static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { plugin->fileCount += 1; - plugin->files = realloc(plugin->files, (plugin->fileCount + 1) * sizeof(FOModFile_t)); - FOModFile_t * file = &plugin->files[plugin->fileCount - 1]; + plugin->files = realloc(plugin->files, (plugin->fileCount + 1) * sizeof(fomod_File_t)); + fomod_File_t * file = &plugin->files[plugin->fileCount - 1]; - file->destination = freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "destination")); - file->source = freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "source")); + file->destination = xml_freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "destination")); + file->source = xml_freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "source")); //TODO: test if it's a number xmlChar * priority = xmlGetProp(fileNode, (const xmlChar *) "priority"); @@ -123,18 +124,18 @@ static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { return EXIT_SUCCESS; } -static int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { +static int parseNodeElement(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) { if(xmlStrcmp(nodeElement->name, (const xmlChar *) "description") == 0) { - plugin->description = freeAndDup(xmlNodeGetContent(nodeElement)); + plugin->description = xml_freeAndDup(xmlNodeGetContent(nodeElement)); } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "image") == 0) { - plugin->image = freeAndDup(xmlGetProp(nodeElement, (const xmlChar *) "path")); + plugin->image = xml_freeAndDup(xmlGetProp(nodeElement, (const xmlChar *) "path")); } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "conditionFlags") == 0) { return parseConditionFlags(plugin, nodeElement); } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "files") == 0) { return parseGroupFiles(plugin, nodeElement); } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "typeDescriptor") == 0) { xmlNodePtr typeNode = nodeElement->children; - if(!validateNode(&typeNode, true, "type", NULL)) { + if(!xml_validateNode(&typeNode, true, "type", NULL)) { fprintf(stderr, "Unexpected node in typeDescriptor"); return EXIT_FAILURE; } @@ -145,29 +146,29 @@ static int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { return EXIT_SUCCESS; } -int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group) { +int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group) { xmlNodePtr pluginsNode = groupNode->children; - if(!validateNode(&pluginsNode, true, "plugins", NULL)) { + if(!xml_validateNode(&pluginsNode, true, "plugins", NULL)) { return EXIT_FAILURE; } - group->name = freeAndDup(xmlGetProp( groupNode, (const xmlChar *) "name")); + group->name = xml_freeAndDup(xmlGetProp( groupNode, (const xmlChar *) "name")); xmlChar * type = xmlGetProp(groupNode, (const xmlChar *) "type"); group->type = getGroupType((const char *)type); xmlFree(type); char * order = (char *) xmlGetProp(pluginsNode, (const xmlChar *) "order"); - group->order = getFOModOrder(order); + group->order = fomod_getOrder(order); xmlFree(order); - FOModPlugin_t * plugins = NULL; + fomod_Plugin_t * plugins = NULL; int pluginCount = 0; xmlNodePtr currentPlugin = pluginsNode->children; while(currentPlugin != NULL) { - if(!validateNode(¤tPlugin, true, "plugin", NULL)) { + if(!xml_validateNode(¤tPlugin, true, "plugin", NULL)) { //TODO handle error; printf("%d\n", __LINE__); exit(EXIT_FAILURE); @@ -177,12 +178,12 @@ int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group) { pluginCount += 1; - plugins = realloc(plugins, pluginCount * sizeof(FOModPlugin_t)); - FOModPlugin_t * plugin = &plugins[pluginCount - 1]; + plugins = realloc(plugins, pluginCount * sizeof(fomod_Plugin_t)); + fomod_Plugin_t * plugin = &plugins[pluginCount - 1]; //initialise everything to 0 and null pointers - memset(plugin, 0, sizeof(FOModPlugin_t)); + memset(plugin, 0, sizeof(fomod_Plugin_t)); - plugin->name = freeAndDup(xmlGetProp(currentPlugin, (const xmlChar *) "name")); + plugin->name = xml_freeAndDup(xmlGetProp(currentPlugin, (const xmlChar *) "name")); xmlNodePtr nodeElement = currentPlugin->children; while(nodeElement != NULL) { @@ -203,6 +204,6 @@ failure: //we need to free all of our allocations since we can't expect ou parent function to know what we allocated and what we haven't. group->plugins = plugins; group->pluginCount = pluginCount; - freeGroup(group); + grp_freeGroup(group); return EXIT_FAILURE; } diff --git a/src/fomod/group.h b/src/fomod/group.h index 27c0538..6583186 100644 --- a/src/fomod/group.h +++ b/src/fomod/group.h @@ -3,32 +3,33 @@ #include #include "fomodTypes.h" +#include "xmlUtil.h" typedef enum GroupType_t { ONE_ONLY, ANY, AT_LEAST_ONE, AT_MOST_ONE, ALL } GroupType_t; typedef enum TypeDescriptor { OPTIONAL, MAYBE_USABLE, NOT_USABLE, REQUIRED, RECOMMENDED } TypeDescriptor_t; -typedef struct FOModPlugin { +typedef struct fomod_Plugin { char * description; char * image; - FOModFlag_t * flags; + fomod_Flag_t * flags; int flagCount; - FOModFile_t * files; + fomod_File_t * files; int fileCount; TypeDescriptor_t type; char * name; -} FOModPlugin_t; +} fomod_Plugin_t; //combine group and "plugins" -typedef struct FOModGroup { - FOModPlugin_t * plugins; +typedef struct fomod_Group { + fomod_Plugin_t * plugins; int pluginCount; GroupType_t type; char * name; - FOModOrder_t order; -} FOModGroup_t; + fomod_Order_t order; +} fomod_Group_t; -int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group); -void freeGroup(FOModGroup_t * group); +int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group); +void grp_freeGroup(fomod_Group_t * group); #endif diff --git a/src/fomod/parser.c b/src/fomod/parser.c index fbd7470..0d71a7d 100644 --- a/src/fomod/parser.c +++ b/src/fomod/parser.c @@ -1,63 +1,17 @@ #include "parser.h" +#include "fomodTypes.h" +#include "group.h" #include "libxml/tree.h" #include "xmlUtil.h" #include #include -//Maybe integrate this into the rest of the code instead of freeing after the fact -void freeFOMod(FOMod_t * fomod) { - for(int i = 0; i < fomod->condFilesCount; i++) { - FOModCondFile_t * condFile = &(fomod->condFiles[i]); - for(long fileId = 0; fileId < condFile->fileCount; fileId++) { - free(condFile->files[fileId].destination); - free(condFile->files[fileId].source); - } - - for(long flagId = 0; flagId < condFile->flagCount; flagId++) { - FOModFlag_t * flag = &(condFile->requiredFlags[flagId]); - free(flag->name); - free(flag->value); - } - free(condFile->files); - free(condFile->requiredFlags); - } - free(fomod->condFiles); - free(fomod->moduleImage); - free(fomod->moduleName); - - int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **)); - for(int i = 0; i < size; i++) { - free(fomod->requiredInstallFiles[i]); - } - free(fomod->requiredInstallFiles); - - for(int i = 0; i < fomod->stepCount; i++) { - FOModStep_t * step = &fomod->steps[i]; - for(int groupId = 0; groupId < step->groupCount; groupId++) { - FOModGroup_t * group = &step->groups[groupId]; - freeGroup(group); - } - for(int flagId = 0; flagId < step->flagCount; flagId++) { - FOModFlag_t * flag = &(step->requiredFlags[flagId]); - free(flag->name); - free(flag->value); - } - free(step->groups); - free(step->requiredFlags); - free(step->name); - } - free(fomod->steps); - - //set every counter to zero and every pointer to null - memset(fomod, 0, sizeof(FOMod_t)); -} - static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) { xmlNodePtr requiredFlagsNode = node->children; while (requiredFlagsNode != NULL) { - if(!validateNode(&requiredFlagsNode, true, "flagDependency", NULL)) { + if(!xml_validateNode(&requiredFlagsNode, true, "flagDependency", NULL)) { //TODO: handle error printf("%d\n", __LINE__); return EXIT_FAILURE; @@ -66,10 +20,10 @@ static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) { if(requiredFlagsNode == NULL)break; step->flagCount += 1; - step->requiredFlags = realloc(step->requiredFlags, step->flagCount * sizeof(FOModFlag_t)); - FOModFlag_t * flag = &(step->requiredFlags[step->flagCount - 1]); - flag->name = freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "flag")); - flag->value = freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "value")); + step->requiredFlags = realloc(step->requiredFlags, step->flagCount * sizeof(fomod_Flag_t)); + fomod_Flag_t * flag = &(step->requiredFlags[step->flagCount - 1]); + flag->name = xml_freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "flag")); + flag->value = xml_freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "value")); requiredFlagsNode = requiredFlagsNode->next; } @@ -79,11 +33,11 @@ static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) { static int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) { xmlChar * optionOrder = xmlGetProp(node, (const xmlChar *)"order"); - step->optionOrder = getFOModOrder((char *)optionOrder); + step->optionOrder = fomod_getOrder((char *)optionOrder); xmlFree(optionOrder); xmlNodePtr group = node->children; while(group != NULL) { - if(!validateNode(&group, true, "group", NULL)) { + if(!xml_validateNode(&group, true, "group", NULL)) { //TODO: handle error printf("%d\n", __LINE__); return EXIT_FAILURE; @@ -92,8 +46,8 @@ static int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) { if(group == NULL)break; step->groupCount += 1; - step->groups = realloc(step->groups, step->groupCount * sizeof(FOModGroup_t)); - int status = parseGroup(group, &step->groups[step->groupCount - 1]); + step->groups = realloc(step->groups, step->groupCount * sizeof(fomod_Group_t)); + int status = grp_parseGroup(group, &step->groups[step->groupCount - 1]); if(status != EXIT_SUCCESS) { //TODO: handle error @@ -113,7 +67,7 @@ static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCo xmlNodePtr stepNode = installStepsNode->children; while(stepNode != NULL) { //skipping the text node - if(!validateNode(&stepNode, true, "installStep", NULL)) { + if(!xml_validateNode(&stepNode, true, "installStep", NULL)) { //TODO: handle error printf("%d\n", __LINE__); exit(EXIT_FAILURE); @@ -125,7 +79,7 @@ static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCo steps = realloc(steps, *stepCount * sizeof(FOModStep_t)); FOModStep_t * step = &steps[*stepCount - 1]; - step->name = freeAndDup(xmlGetProp(stepNode, (const xmlChar *)"name")); + step->name = xml_freeAndDup(xmlGetProp(stepNode, (const xmlChar *)"name")); step->requiredFlags = NULL; step->flagCount = 0; step->groupCount = 0; @@ -147,10 +101,10 @@ static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCo return steps; } -static int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) { +static int parseDependencies(xmlNodePtr node, fomod_CondFile_t * condFile) { xmlNodePtr flagNode = node->children; - if(!validateNode(&flagNode, true, "flagDependency", NULL)) { + if(!xml_validateNode(&flagNode, true, "flagDependency", NULL)) { //TODO: handle error printf("%d\n", __LINE__); return EXIT_FAILURE; @@ -158,32 +112,32 @@ static int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) { while(flagNode != NULL) { condFile->flagCount += 1; - condFile->requiredFlags = realloc(condFile->requiredFlags, condFile->flagCount * sizeof(FOModFlag_t)); - FOModFlag_t * flag = &(condFile->requiredFlags[condFile->flagCount - 1]); - flag->name = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "flag")); - flag->value = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "value")); + condFile->requiredFlags = realloc(condFile->requiredFlags, condFile->flagCount * sizeof(fomod_Flag_t)); + fomod_Flag_t * flag = &(condFile->requiredFlags[condFile->flagCount - 1]); + flag->name = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "flag")); + flag->value = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "value")); flagNode = flagNode->next; } return EXIT_SUCCESS; } -static int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) { +static int parseFiles(xmlNodePtr node, fomod_CondFile_t * condFile) { xmlNodePtr filesNode = node->children; while(filesNode != NULL) { - if(!validateNode(&filesNode, true, "folder", "file", NULL)) { + if(!xml_validateNode(&filesNode, true, "folder", "file", NULL)) { //TODO: handle error printf("%d\n", __LINE__); return EXIT_FAILURE; } condFile->fileCount += 1; - condFile->files = realloc(condFile->files, condFile->fileCount * sizeof(FOModFile_t)); - FOModFile_t * flag = &(condFile->files[condFile->fileCount - 1]); - flag->source = freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "source")); - flag->destination = freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "destination")); + condFile->files = realloc(condFile->files, condFile->fileCount * sizeof(fomod_File_t)); + fomod_File_t * flag = &(condFile->files[condFile->fileCount - 1]); + flag->source = xml_freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "source")); + flag->destination = xml_freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "destination")); flag->priority = 0; flag->isFolder = xmlStrcmp(filesNode->name, (xmlChar *) "folder") == 0; @@ -196,7 +150,7 @@ static int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) { static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { xmlNodePtr patterns = node->children; if(patterns != NULL) { - if(!validateNode(&patterns, true, "patterns", NULL)) { + if(!xml_validateNode(&patterns, true, "patterns", NULL)) { //TODO: handle error printf("%d\n", __LINE__); return EXIT_FAILURE; @@ -205,7 +159,7 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { while(currentPattern != NULL) { xmlNodePtr patternChild = currentPattern->children; - if(!validateNode(&patternChild, true, "pattern", NULL)) { + if(!xml_validateNode(&patternChild, true, "pattern", NULL)) { //TODO: handle error printf("%d\n", __LINE__); return EXIT_FAILURE; @@ -213,8 +167,8 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { while(patternChild != NULL) { fomod->condFilesCount += 1; - fomod->condFiles = realloc(fomod->condFiles, fomod->condFilesCount * sizeof(FOModCondFile_t)); - FOModCondFile_t * condFile = &(fomod->condFiles[fomod->condFilesCount - 1]); + fomod->condFiles = realloc(fomod->condFiles, fomod->condFilesCount * sizeof(fomod_CondFile_t)); + fomod_CondFile_t * condFile = &(fomod->condFiles[fomod->condFilesCount - 1]); condFile->fileCount = 0; condFile->files = NULL; @@ -239,7 +193,7 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { return EXIT_SUCCESS; } -error_t parseFOMod(const char * fomodFile, FOMod_t* fomod) { +error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod) { xmlDocPtr doc; xmlNodePtr cur; @@ -279,22 +233,22 @@ error_t parseFOMod(const char * fomodFile, FOMod_t* fomod) { //might cause some issues. when will c finally support utf-8 fomod->moduleName = (char *)cur->content; } else if(xmlStrcmp(cur->name, (const xmlChar *) "moduleImage") == 0) { - fomod->moduleImage = freeAndDup(xmlGetProp(cur, (const xmlChar *)"path")); + fomod->moduleImage = xml_freeAndDup(xmlGetProp(cur, (const xmlChar *)"path")); } else if(xmlStrcmp(cur->name, (const xmlChar *)"requiredInstallFiles") == 0) { //TODO: support non empty destination. xmlNodePtr requiredInstallFile = cur->children; while(requiredInstallFile != NULL) { - if(validateNode(&requiredInstallFile, true, "folder", "file", NULL)) { + if(xml_validateNode(&requiredInstallFile, true, "folder", "file", NULL)) { //TODO: handle error printf("%d\n", __LINE__); exit(ERR_FAILURE); } - int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **)) + 2; + int size = fomod_countUntilNull(fomod->requiredInstallFiles, sizeof(char **)) + 2; fomod->requiredInstallFiles = realloc(fomod->requiredInstallFiles, sizeof(char *) * size); //ensure it is null terminated fomod->requiredInstallFiles[size - 1] = NULL; - fomod->requiredInstallFiles[size - 2] = freeAndDup(xmlGetProp(requiredInstallFile, (const xmlChar *)"source")); + fomod->requiredInstallFiles[size - 2] = xml_freeAndDup(xmlGetProp(requiredInstallFile, (const xmlChar *)"source")); requiredInstallFile = cur->children; } @@ -306,7 +260,7 @@ error_t parseFOMod(const char * fomodFile, FOMod_t* fomod) { } xmlChar * stepOrder = xmlGetProp(cur, (xmlChar *)"order"); - fomod->stepOrder = getFOModOrder((char *)stepOrder); + fomod->stepOrder = fomod_getOrder((char *)stepOrder); xmlFree(stepOrder); int stepCount = 0; diff --git a/src/fomod/parser.h b/src/fomod/parser.h index 604230e..37ebce8 100644 --- a/src/fomod/parser.h +++ b/src/fomod/parser.h @@ -2,16 +2,17 @@ #define __FOMOD_PARSER_H__ +#include "fomodTypes.h" #include "group.h" #include "../main.h" //combine installStep and optionalFileGroups typedef struct FOModStep { - FOModOrder_t optionOrder; - FOModGroup_t * groups; + fomod_Order_t optionOrder; + fomod_Group_t * groups; int groupCount; char * name; - FOModFlag_t * requiredFlags; + fomod_Flag_t * requiredFlags; int flagCount; } FOModStep_t; @@ -19,10 +20,10 @@ typedef struct FOMod { char * moduleName; char * moduleImage; char ** requiredInstallFiles; - FOModOrder_t stepOrder; + fomod_Order_t stepOrder; FOModStep_t * steps; int stepCount; - FOModCondFile_t * condFiles; + fomod_CondFile_t * condFiles; int condFilesCount; } FOMod_t; @@ -32,12 +33,6 @@ typedef struct FOMod { * @param fomodFile path to the moduleconfig.xml * @param fomod pointer to a new FOMod_t */ -error_t parseFOMod(const char * fomodFile, FOMod_t* fomod); - -/** - * @brief Free content of a fomod file. - * @param fomod - */ -void freeFOMod(FOMod_t * fomod); +error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod); #endif diff --git a/src/fomod/xmlUtil.c b/src/fomod/xmlUtil.c index 9e25d8a..e2ac79a 100644 --- a/src/fomod/xmlUtil.c +++ b/src/fomod/xmlUtil.c @@ -3,13 +3,13 @@ #include #include -char * freeAndDup(xmlChar * line) { +char * xml_freeAndDup(xmlChar * line) { char * free = strdup((const char *) line); xmlFree(line); return free; } -FOModOrder_t getFOModOrder(const char * order) { +fomod_Order_t fomod_getOrder(const char * order) { if(order == NULL || strcmp(order, "Ascending") == 0) { return ASC; } else if(strcmp(order, "Explicit") == 0) { @@ -21,14 +21,14 @@ FOModOrder_t getFOModOrder(const char * order) { } //replace \ in the path by / -void fixPath(char * path) { +void xml_fixPath(char * path) { while(*path != '\0') { if(*path == '\\')*path = '/'; path++; } } -int countUntilNull(void * pointers, size_t typeSize) { +int fomod_countUntilNull(void * pointers, size_t typeSize) { int i = 0; char * arithmetic = (char *)pointers; while(arithmetic != NULL) { @@ -40,7 +40,7 @@ int countUntilNull(void * pointers, size_t typeSize) { //names cannot contain false //need to be null terminated -bool validateNode(xmlNodePtr * node, bool skipText, const char * names, ...) { +bool xml_validateNode(xmlNodePtr * node, bool skipText, const char * names, ...) { va_list namesPtr; while(*node != NULL && xmlStrcmp((*node)->name, (const xmlChar *)"text") == 0) { diff --git a/src/fomod/xmlUtil.h b/src/fomod/xmlUtil.h index f38f7f8..42af76f 100644 --- a/src/fomod/xmlUtil.h +++ b/src/fomod/xmlUtil.h @@ -5,7 +5,7 @@ #include -typedef enum FOModOrder { ASC, DESC, ORD } FOModOrder_t; +typedef enum FOModOrder { ASC, DESC, ORD } fomod_Order_t; /** * @brief @@ -15,21 +15,21 @@ typedef enum FOModOrder { ASC, DESC, ORD } FOModOrder_t; * @param names variadic of the valid names. * @return return true if it found a valid node */ -bool validateNode(xmlNodePtr * node, bool skipText, const char * names, ...); +bool xml_validateNode(xmlNodePtr * node, bool skipText, const char * names, ...); /** * @brief Free memory of and xmlChar and return a strdup version. just to make sure there is nothing remaining in libxml */ -char * freeAndDup(xmlChar * line); +char * xml_freeAndDup(xmlChar * line); -FOModOrder_t getFOModOrder(const char * order); +fomod_Order_t fomod_getOrder(const char * order); /** * @brief replace / by \ * @param path */ -void fixPath(char * path); +void xml_fixPath(char * path); /** * @brief Count the number of step before null @@ -38,6 +38,6 @@ void fixPath(char * path); * @param typeSize size of each element of the list * @return size */ -int countUntilNull(void * pointers, size_t typeSize); +int fomod_countUntilNull(void * pointers, size_t typeSize); #endif diff --git a/src/getDataPath.c b/src/getDataPath.c index efcc343..a2442b1 100644 --- a/src/getDataPath.c +++ b/src/getDataPath.c @@ -8,12 +8,12 @@ error_t getDataPath(int appid, char ** destination) { GHashTable * gamePaths; - error_t status = search_games(&gamePaths); + error_t status = steam_searchGames(&gamePaths); if(status == ERR_FAILURE) { return ERR_FAILURE; } - int gameId = getGameIdFromAppId(appid); + int gameId = steam_gameIdFromAppId(appid); if(gameId < 0 ) { return ERR_FAILURE; } diff --git a/src/install.c b/src/install.c index a720943..bbbddf8 100644 --- a/src/install.c +++ b/src/install.c @@ -9,7 +9,7 @@ #include "archives.h" #include "file.h" -error_t addMod(char * filePath, int appId) { +error_t install_addMod(char * filePath, int appId) { error_t resultError = ERR_SUCCESS; if (access(filePath, F_OK) != 0) { fprintf(stderr, "File not found\n"); @@ -24,8 +24,8 @@ error_t addMod(char * filePath, int appId) { goto exit2; } - const char * filename = extractFileName(filePath); - const char * extension = extractExtension(filename); + const char * filename = file_extractFileName(filePath); + const char * extension = file_extractExtension(filename); char * lowercaseExtension = g_ascii_strdown(extension, -1); char appIdStr[20]; @@ -37,11 +37,11 @@ error_t addMod(char * filePath, int appId) { int returnValue = EXIT_SUCCESS; printf("Adding mod, this process can be slow depending on your hardware\n"); if(strcmp(lowercaseExtension, "rar") == 0) { - returnValue = unrar(filePath, outdir); + returnValue = archive_unrar(filePath, outdir); } else if (strcmp(lowercaseExtension, "zip") == 0) { - returnValue = unzip(filePath, outdir); + returnValue = archive_unzip(filePath, outdir); } else if (strcmp(lowercaseExtension, "7z") == 0) { - returnValue = un7z(filePath, outdir); + returnValue = archive_un7z(filePath, outdir); } else { fprintf(stderr, "Unsupported format only zip/7z/rar are supported\n"); returnValue = EXIT_FAILURE; diff --git a/src/install.h b/src/install.h index 51c52f2..ba11f6d 100644 --- a/src/install.h +++ b/src/install.h @@ -10,6 +10,6 @@ * @param filePath path to the mod file * @param appId game for which the mod is destined to be used with. */ -error_t addMod(char * filePath, int appId); +error_t install_addMod(char * filePath, int appId); #endif diff --git a/src/loadOrder.c b/src/loadOrder.c index d906083..25e536f 100644 --- a/src/loadOrder.c +++ b/src/loadOrder.c @@ -12,7 +12,7 @@ //TODO: detect if the game is running //TODO: deploy the game -error_t listPlugins(int appid, GList ** plugins) { +error_t order_listPlugins(int appid, GList ** plugins) { //save appid parsing @@ -32,7 +32,7 @@ error_t listPlugins(int appid, GList ** plugins) { struct dirent *dir; if (d) { while ((dir = readdir(d)) != NULL) { - const char * extension = extractExtension(dir->d_name); + const char * extension = file_extractExtension(dir->d_name); if(strcmp(extension, "esp") == 0 || strcmp(extension, "esm") == 0) { *plugins = g_list_append(*plugins, strdup(dir->d_name)); } @@ -43,21 +43,21 @@ error_t listPlugins(int appid, GList ** plugins) { return ERR_SUCCESS; } -error_t getLoadOrder(int appid, GList ** order) { +error_t order_getLoadOrder(int appid, GList ** order) { GHashTable * gamePaths; - error_t status = search_games(&gamePaths); + error_t status = steam_searchGames(&gamePaths); if(status == ERR_FAILURE) { return ERR_FAILURE; } GList * l_plugins = NULL; - error_t error = listPlugins(appid, &l_plugins); + error_t error = order_listPlugins(appid, &l_plugins); if(error == ERR_FAILURE) return ERR_FAILURE; - int gameId = getGameIdFromAppId(appid); + int gameId = steam_gameIdFromAppId(appid); if(gameId < 0 ) { return ERR_FAILURE; } @@ -109,14 +109,14 @@ error_t getLoadOrder(int appid, GList ** order) { return ERR_SUCCESS; } -error_t setLoadOrder(int appid, GList * loadOrder) { +error_t order_setLoadOrder(int appid, GList * loadOrder) { GHashTable * gamePaths; - error_t status = search_games(&gamePaths); + error_t status = steam_searchGames(&gamePaths); if(status == ERR_FAILURE) { return ERR_FAILURE; } - int gameId = getGameIdFromAppId(appid); + int gameId = steam_gameIdFromAppId(appid); if(gameId < 0 ) { return ERR_FAILURE; } @@ -141,7 +141,7 @@ error_t setLoadOrder(int appid, GList * loadOrder) { //TODO: support compression since it can change how we read the file //https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format#Records //https://www.mwmythicmods.com/argent/tech/es_format.html -error_t getModDependencies(const char * esmPath, GList ** dependencies) { +error_t order_getModDependencies(const char * esmPath, GList ** dependencies) { FILE * file = fopen(esmPath, "r"); char sectionName[5]; diff --git a/src/loadOrder.h b/src/loadOrder.h index 9b07adb..2f8f67e 100644 --- a/src/loadOrder.h +++ b/src/loadOrder.h @@ -9,21 +9,21 @@ * @param appid the appid of the game * @param order a pointer to a null Glist *. in which there will be the list of esm files. */ -error_t listPlugins(int appid, GList ** list) __attribute__((warn_unused_result)); +error_t order_listPlugins(int appid, GList ** list) __attribute__((warn_unused_result)); /** * @brief fetch the load order of the game it might be null if setLoadOrder was never called. * @param appid the appid of the game * @param order a pointer to a null Glist *. in which there will be the list of esm files. */ -error_t getLoadOrder(int appid, GList ** order) __attribute__((warn_unused_result)); +error_t order_getLoadOrder(int appid, GList ** order) __attribute__((warn_unused_result)); /** * @brief change the plugin load order of the game * @param appid the appid of the game * @param loadOrder the load order */ -error_t setLoadOrder(int appid, GList * loadOrder) __attribute__((warn_unused_result)); +error_t order_setLoadOrder(int appid, GList * loadOrder) __attribute__((warn_unused_result)); /** * @brief List all dependencies for a esm mod. @@ -31,6 +31,6 @@ error_t setLoadOrder(int appid, GList * loadOrder) __attribute__((warn_unused_re * @param dependencies a pointer to a null Glist *. in which there will be the list of esm files. * free it using g_list_free_full(dependencies, free); */ -error_t getModDependencies(const char * esmPath, GList ** dependencies) __attribute__((warn_unused_result)); +error_t order_getModDependencies(const char * esmPath, GList ** dependencies) __attribute__((warn_unused_result)); #endif diff --git a/src/main.c b/src/main.c index b163327..b9d7752 100644 --- a/src/main.c +++ b/src/main.c @@ -65,7 +65,7 @@ static int usage() { static error_t validateAppId(const char * appIdStr) { GHashTable * gamePaths; - error_t status = search_games(&gamePaths); + error_t status = steam_searchGames(&gamePaths); if(status == ERR_FAILURE) { return ERR_FAILURE; } @@ -78,7 +78,7 @@ static error_t validateAppId(const char * appIdStr) { return -1; } - int gameId = getGameIdFromAppId((int)appid); + int gameId = steam_gameIdFromAppId((int)appid); if(gameId < 0) { fprintf(stderr, "Game is not compatible\n"); return -1; @@ -97,7 +97,7 @@ static int listGames(int argc, char **) { if(argc != 2) return usage(); GHashTable * gamePaths; - error_t status = search_games(&gamePaths); + error_t status = steam_searchGames(&gamePaths); if(status == ERR_FAILURE) { return EXIT_FAILURE; } @@ -126,7 +126,7 @@ static int add(int argc, char ** argv) { return EXIT_FAILURE; } - addMod(argv[3], appid); + install_addMod(argv[3], appid); return EXIT_SUCCESS; } @@ -143,7 +143,7 @@ static int listAllMods(int argc, char ** argv) { char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL); free(home); - GList * mods = listMods(appid); + GList * mods = order_listMods(appid); GList * p_mods = mods; unsigned short index = 0; @@ -330,7 +330,7 @@ static int deploy(int argc, char ** argv) { //it might crash / corrupt game file if the user do it while the game is running //but it's still very unlikely while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0); - enum overlayErrors status = overlayMount(modsToInstall, dataFolder, gameUpperDir, gameWorkDir); + overlay_errors_t status = overlay_mount(modsToInstall, dataFolder, gameUpperDir, gameWorkDir); if(status == SUCESS) { printf("Everything is ready, just launch the game\n"); } else if(status == FAILURE) { @@ -377,16 +377,16 @@ static int setup(int argc, char ** argv) { if(access(gameFolder, F_OK) == 0) { //if the game folder alredy exists just delete it //this will allow the removal of dlcs and language change - delete(gameFolder, true); + file_delete(gameFolder, true); } g_mkdir_with_parents(gameFolder, 0755); //links don't conflict with overlayfs and avoid coping 17Gb of files. //but links require the files to be on the same filesystem - int returnValue = copy(dataFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR | CP_LINK); + int returnValue = file_copy(dataFolder, gameFolder, FILE_CP_RECURSIVE | FILE_CP_NO_TARGET_DIR | FILE_CP_LINK); if(returnValue < 0) { printf("Coping game files. HINT: having the game on the same partition as you home director will make this operation use zero extra space"); - returnValue = copy(dataFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR); + returnValue = file_copy(dataFolder, gameFolder, FILE_CP_RECURSIVE | FILE_CP_NO_TARGET_DIR); if(returnValue < 0) { fprintf(stderr, "Copy failed make sure you have enough space on your device."); free(dataFolder); @@ -394,7 +394,7 @@ static int setup(int argc, char ** argv) { return EXIT_FAILURE; } } - casefold(gameFolder); + file_casefold(gameFolder); free(dataFolder); free(gameFolder); @@ -457,7 +457,7 @@ static int removeMod(int argc, char ** argv) { gchar * filename = g_build_filename(modFolder, mods->data, NULL); - delete(filename, true); + file_delete(filename, true); g_free(filename); g_free(modFolder); @@ -500,13 +500,13 @@ static int fomod(int argc, char ** argv) { char * destination = g_strconcat(mods->data, "__FOMOD", NULL); if(access(destination, F_OK) == 0) { - delete(destination, true); + file_delete(destination, true); } char * modDestination = g_build_filename(modFolder, destination, NULL); char * modPath = g_build_filename(modFolder, mods->data, NULL); //TODO: add error handling - int returnValue = installFOMod(modPath, modDestination); + int returnValue = fomod_installFOMod(modPath, modDestination); free(destination); g_list_free_full(modsFirstPointer, free); @@ -540,7 +540,7 @@ static int swapMod(int argc, char ** argv) { } printf("%d, %d\n", modIdA, modIdB); - return swapPlace(appid, modIdA, modIdB); + return order_swapPlace(appid, modIdA, modIdB); } int main(int argc, char ** argv) { @@ -609,7 +609,6 @@ int main(int argc, char ** argv) { returnValue = swapMod(argc, argv); else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) { - returnValue = EXIT_SUCCESS; #ifdef __clang__ printf("%s: Clang: %d.%d.%d\n", VERSION, __clang_major__, __clang_minor__, __clang_patchlevel__); #elifdef __GNUC__ @@ -624,6 +623,6 @@ int main(int argc, char ** argv) { exit: g_free(configFolder); - freeGameTableSingleton(); + steam_freeGameTable(); return returnValue; } diff --git a/src/order.c b/src/order.c index becfb81..03d935a 100644 --- a/src/order.c +++ b/src/order.c @@ -22,7 +22,7 @@ static gint compareOrder(const void * a, const void * b) { return ModA->modId - ModB->modId; } -GList * listMods(int appid) { +GList * order_listMods(int appid) { char appidStr[10]; sprintf(appidStr, "%d", appid); @@ -94,7 +94,7 @@ GList * listMods(int appid) { } -error_t swapPlace(int appid, int modIdA, int modIdB) { +error_t order_swapPlace(int appid, int modIdA, int modIdB) { char appidStr[10]; sprintf(appidStr, "%d", appid); @@ -102,7 +102,7 @@ error_t swapPlace(int appid, int modIdA, int modIdB) { char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appidStr, NULL); free(home); - GList * list = listMods(appid); + GList * list = order_listMods(appid); GList * listA = list; GList * listB = list; diff --git a/src/order.h b/src/order.h index 15d390d..33f1812 100644 --- a/src/order.h +++ b/src/order.h @@ -15,7 +15,7 @@ * * @return GList of char containing the name of the mod folder in order */ -GList * listMods(int appid); +GList * order_listMods(int appid); /** * @brief Change the mod order by swaping two mod @@ -25,6 +25,6 @@ GList * listMods(int appid); * @param modId * @param modId2 */ -error_t swapPlace(int appid, int modId, int modId2); +error_t order_swapPlace(int appid, int modId, int modId2); #endif diff --git a/src/overlayfs.c b/src/overlayfs.c index 6a1d520..8ba7c2a 100644 --- a/src/overlayfs.c +++ b/src/overlayfs.c @@ -6,11 +6,11 @@ #include "overlayfs.h" #include -enum overlayErrors overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir) { +overlay_errors_t overlay_mount(char ** sources, const char * dest, const char * upperdir, const char * workdir) { char * lowerdir = g_strjoinv(":", sources); char * mountData = g_strjoin("", "lowerdir=", lowerdir, ",workdir=", workdir, ",upperdir=", upperdir, NULL); - enum overlayErrors result = SUCESS; + overlay_errors_t result = SUCESS; if(access("/usr/bin/fuse-overlayfs", F_OK) == 0) { int pid = fork(); diff --git a/src/overlayfs.h b/src/overlayfs.h index 8220f15..7bd95e1 100644 --- a/src/overlayfs.h +++ b/src/overlayfs.h @@ -1,7 +1,7 @@ #ifndef __OVERLAY_H__ #define __OVERLAY_H__ -enum overlayErrors { SUCESS, NOT_INSTALLED, FAILURE }; +typedef enum overlay_errors { SUCESS, NOT_INSTALLED, FAILURE } overlay_errors_t; /** @@ -13,6 +13,6 @@ enum overlayErrors { SUCESS, NOT_INSTALLED, FAILURE }; * @param workdir a directory that will contains only temporary files. * @return int error code */ -enum overlayErrors overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir); +overlay_errors_t overlay_mount(char ** sources, const char * dest, const char * upperdir, const char * workdir); #endif diff --git a/src/steam.c b/src/steam.c index 87ae8d1..f43e1fb 100644 --- a/src/steam.c +++ b/src/steam.c @@ -44,7 +44,7 @@ static int getFiledId(const char * field) { } } -static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) { +static steam_Libraries_t * parseVDF(const char * path, size_t * size, int * status) { FILE * fd = fopen(path, "r"); char * line = NULL; size_t len = 0; @@ -53,7 +53,7 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu bool inQuotes = false; - ValveLibraries_t * libraries = NULL; + steam_Libraries_t * libraries = NULL; *size = 0; //skip the "libraryfolders" label & the first opening brace @@ -90,7 +90,7 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu } else { char * value = strndup(buffer, bufferIndex); - ValveLibraries_t * library = &libraries[*size - 1]; + steam_Libraries_t * library = &libraries[*size - 1]; switch (nextFieldToFill) { case FIELD_PATH: library->path = value; @@ -120,7 +120,7 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu case FIELD_APPS: if(isAppId) { library->appsCount++; - library->apps = realloc(library->apps, library->appsCount * sizeof(ValveApp_t)); + library->apps = realloc(library->apps, library->appsCount * sizeof(steam_App_t)); unsigned int appid = strtoul(value, NULL, 10); library->apps[library->appsCount - 1].appid = appid; } else { @@ -146,8 +146,8 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu braceDepth++; if(braceDepth == 1) { *size += 1; - libraries = realloc(libraries, sizeof(ValveLibraries_t) * (*size)); - memset(&libraries[*size - 1], 0, sizeof(ValveLibraries_t)); + libraries = realloc(libraries, sizeof(steam_Libraries_t) * (*size)); + memset(&libraries[*size - 1], 0, sizeof(steam_Libraries_t)); } break; case '}': @@ -179,7 +179,7 @@ exit: return libraries; } -static void freeLibraries(ValveLibraries_t * libraries, int size) { +static void freeLibraries(steam_Libraries_t * libraries, int size) { for(int i = 0; i < size; i++) { free(libraries[i].path); free(libraries[i].label); @@ -193,17 +193,17 @@ static void freeLibraries(ValveLibraries_t * libraries, int size) { static GHashTable* gameTableSingleton = NULL; -void freeGameTableSingleton() { +void steam_freeGameTable() { if(gameTableSingleton != NULL)g_hash_table_destroy(gameTableSingleton); } -error_t search_games(GHashTable ** p_hashTable) { +error_t steam_searchGames(GHashTable ** p_hashTable) { if(gameTableSingleton != NULL) { *p_hashTable = gameTableSingleton; return ERR_SUCCESS; } - ValveLibraries_t * libraries = NULL; + steam_Libraries_t * libraries = NULL; size_t size = 0; char * home = getHome(); @@ -231,7 +231,7 @@ error_t search_games(GHashTable ** p_hashTable) { //fill the table for(unsigned long i = 0; i < size; i++) { for(unsigned long j = 0; j < libraries[i].appsCount; j++) { - int gameId = getGameIdFromAppId(libraries[i].apps[j].appid); + int gameId = steam_gameIdFromAppId(libraries[i].apps[j].appid); if(gameId >= 0) { int * key = malloc(sizeof(int)); *key = gameId; @@ -247,7 +247,7 @@ error_t search_games(GHashTable ** p_hashTable) { } -int getGameIdFromAppId(u_int32_t appid) { +int steam_gameIdFromAppId(u_int32_t appid) { for(unsigned long k = 0; k < LEN(GAMES_APPIDS); k++) { if(appid == GAMES_APPIDS[k]) { return k; diff --git a/src/steam.h b/src/steam.h index 7866ec1..af5006b 100644 --- a/src/steam.h +++ b/src/steam.h @@ -10,21 +10,21 @@ #include #include -typedef struct ValveApp { +typedef struct steam_App { unsigned int appid; unsigned int update; -} ValveApp_t; +} steam_App_t; -typedef struct ValveLibraries { +typedef struct steam_Libraries { char * path; char * label; char * contentId; unsigned long totalSize; char * update_clean_bytes_tally; char * time_last_update_corruption; - ValveApp_t * apps; + steam_App_t * apps; size_t appsCount; -} ValveLibraries_t; +} steam_Libraries_t; //todo add the older games // order has to be the same as in GAMES_NAMES @@ -52,9 +52,9 @@ _Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Name * @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 */ -error_t search_games(GHashTable** tablePointer); +error_t steam_searchGames(GHashTable** tablePointer); -void freeGameTableSingleton(void); +void steam_freeGameTable(void); /** * @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS @@ -62,6 +62,6 @@ void freeGameTableSingleton(void); * @param appid * @return -1 in case of failure or the index of the game. */ -int getGameIdFromAppId(u_int32_t appid); +int steam_gameIdFromAppId(u_int32_t appid); #endif From 1017d4731234e6f9dfffa611e68eafdb04920b43 Mon Sep 17 00:00:00 2001 From: Marc Date: Mon, 10 Oct 2022 21:01:16 +0200 Subject: [PATCH 18/18] 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);