Merged master into interface
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# This file is a template, and might need editing before it works on your project.
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
||||
|
||||
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
||||
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
||||
# it uses echo commands to simulate the pipeline execution.
|
||||
#
|
||||
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
||||
# Stages run in sequential order, but jobs within stages run in parallel.
|
||||
#
|
||||
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
|
||||
|
||||
before_script:
|
||||
- apt update -y
|
||||
- apt install cmake libglib2.0-dev libgtk-4-dev make gcc git make clang libaudit-dev -y
|
||||
|
||||
|
||||
stages: # List of stages for jobs, and their order of execution
|
||||
- build
|
||||
|
||||
build-job: # This job runs in the build stage, which runs first.
|
||||
stage: build
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- make
|
||||
- echo "Build done"
|
||||
artifacts:
|
||||
paths:
|
||||
- build/ModManager
|
||||
expire_in: 1 week
|
||||
|
||||
+2
-2
@@ -9,7 +9,6 @@ project(ModManager)
|
||||
|
||||
|
||||
set(SOURCES
|
||||
src/main.c
|
||||
src/steam.c
|
||||
src/install.c
|
||||
src/overlayfs.c
|
||||
@@ -24,7 +23,8 @@ set(SOURCES
|
||||
)
|
||||
|
||||
# add the executable
|
||||
add_executable(ModManager ${SOURCES})
|
||||
add_executable(ModManager src/main.c ${SOURCES})
|
||||
|
||||
add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
+32
-1
@@ -4,13 +4,44 @@
|
||||
#ifndef __FILE_H__
|
||||
#define __FILE_H__
|
||||
|
||||
|
||||
//valid copy flags
|
||||
#define cp_DEFAULT 0
|
||||
#define CP_LINK 1
|
||||
#define CP_RECURSIVE 2
|
||||
#define CP_NO_TARGET_DIR 4
|
||||
|
||||
int copy(const char * path, const char * dest, u_int32_t flags);
|
||||
/**
|
||||
* @brief execute the cp command from the source to the dest
|
||||
* @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
|
||||
*/
|
||||
int copy(const char * source, const char * dest, u_int32_t flags);
|
||||
|
||||
/**
|
||||
* @brief execute the cp command from the source to the dest
|
||||
* @param source path to the source files
|
||||
* @param dest path to the destination folder
|
||||
* @param bool enable recursive rm -r
|
||||
* @return status code
|
||||
*/
|
||||
int delete(const char * path, bool recursive);
|
||||
|
||||
/**
|
||||
* @brief Run the mv command
|
||||
* @param source
|
||||
* @param destination
|
||||
* @return mv's exit value
|
||||
*/
|
||||
int move(const char * source, const char * destination);
|
||||
|
||||
/**
|
||||
* @brief Recursively rename all file and folder to lowercase.
|
||||
*
|
||||
* @param folder
|
||||
*/
|
||||
void casefold(const char * folder);
|
||||
|
||||
#endif
|
||||
|
||||
+73
-99
@@ -1,4 +1,3 @@
|
||||
#include "fomod.h"
|
||||
#include <libxml/tree.h>
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlstring.h>
|
||||
@@ -8,11 +7,12 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <glib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fomod.h"
|
||||
#include "file.h"
|
||||
#include "libxml/globals.h"
|
||||
#include "fomod/parser.h"
|
||||
|
||||
int getInputCount(const char * input) {
|
||||
char buff[2];
|
||||
@@ -47,39 +47,6 @@ gint priorityCmp(gconstpointer a, gconstpointer b) {
|
||||
return fileA->priority - fileB->priority;
|
||||
}
|
||||
|
||||
|
||||
char * findFOModFolder(const char * modFolder) {
|
||||
struct dirent * dir;
|
||||
DIR *d = opendir(modFolder);
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if(g_ascii_strcasecmp(dir->d_name, "fomod") == 0) {
|
||||
char * fomodDir = g_build_path("/", modFolder, dir->d_name, NULL);
|
||||
closedir(d);
|
||||
return fomodDir;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char * findFOModFile(const char * fomodFolder) {
|
||||
struct dirent * dir;
|
||||
DIR *d = opendir(fomodFolder);
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if(g_ascii_strcasecmp(dir->d_name, "moduleconfig.xml") == 0) {
|
||||
char * fomodFile = g_build_filename(fomodFolder, dir->d_name, NULL);
|
||||
closedir(d);
|
||||
return fomodFile;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void printfOptionsInOrder(FOModGroup_t group) {
|
||||
for(int i = 0; i < group.pluginCount; i++) {
|
||||
printf("%d, %s\n", i, group.plugins[i].name);
|
||||
@@ -152,18 +119,75 @@ void sortGroup(FOModGroup_t * group) {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: support priority
|
||||
int installFOMod(const char * modFolder, const char * destination) {
|
||||
char * fomodFolder = findFOModFolder(modFolder);
|
||||
if(fomodFolder == NULL) {
|
||||
fprintf(stderr, "Fomod folder not found, are you sure this is a fomod mod ?\n");
|
||||
return EXIT_FAILURE;
|
||||
//TODO: handle error
|
||||
int processFileOperations(GList * pendingFileOperations, const char * modFolder, const char * destination) {
|
||||
pendingFileOperations = g_list_sort(pendingFileOperations, priorityCmp);
|
||||
GList * currentFileOperation = pendingFileOperations;
|
||||
|
||||
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;
|
||||
char * source = g_build_path("/", modFolder, file->source, NULL);
|
||||
|
||||
//fix the / and \ windows - unix paths
|
||||
fixPath(source);
|
||||
|
||||
int copyResult;
|
||||
if(file->isFolder) {
|
||||
copyResult = copy(source, destination, CP_NO_TARGET_DIR | CP_RECURSIVE);
|
||||
} else {
|
||||
copyResult = copy(source, destination, 0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) {
|
||||
for(int condId = 0; condId < fomod->condFilesCount; condId++) {
|
||||
const FOModCondFile_t *condFile = &fomod->condFiles[condId];
|
||||
|
||||
bool areAllFlagsValid = true;
|
||||
|
||||
//checking if all flags are valid
|
||||
for(int flagId = 0; flagId < condFile->flagCount; flagId++) {
|
||||
const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)flagEqual);
|
||||
if(link == NULL) {
|
||||
areAllFlagsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char * fomodFile = findFOModFile(fomodFolder);
|
||||
if(fomodFile == NULL) {
|
||||
if(areAllFlagsValid) {
|
||||
for(int fileId = 0; fileId < condFile->flagCount; fileId++) {
|
||||
const FOModFile_t * file = &(condFile->files[fileId]);
|
||||
|
||||
FOModFile_t * fileCopy = malloc(sizeof(*file));
|
||||
*fileCopy = *file;
|
||||
|
||||
pendingFileOperations = g_list_append(pendingFileOperations, fileCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pendingFileOperations;
|
||||
}
|
||||
|
||||
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);
|
||||
char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL);
|
||||
|
||||
if(access(fomodFile, F_OK) != 0) {
|
||||
fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n");
|
||||
g_free(fomodFolder);
|
||||
g_free(fomodFile);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -172,6 +196,7 @@ int installFOMod(const char * modFolder, const char * destination) {
|
||||
if(returnValue != EXIT_SUCCESS) {
|
||||
return returnValue;
|
||||
}
|
||||
g_free(fomodFile);
|
||||
|
||||
GList * flagList = NULL;
|
||||
GList * pendingFileOperations = NULL;
|
||||
@@ -179,7 +204,7 @@ int installFOMod(const char * modFolder, const char * destination) {
|
||||
sortSteps(&fomod);
|
||||
|
||||
for(int i = 0; i < fomod.stepCount; i++) {
|
||||
FOModStep_t * step = &fomod.steps[i];
|
||||
const FOModStep_t * step = &fomod.steps[i];
|
||||
|
||||
bool validFlags = true;
|
||||
for(int flagId = 0; flagId < step->flagCount; flagId++) {
|
||||
@@ -280,66 +305,15 @@ int installFOMod(const char * modFolder, const char * destination) {
|
||||
}
|
||||
}
|
||||
|
||||
for(int condId = 0; condId < fomod.condFilesCount; condId++) {
|
||||
const FOModCondFile_t *condFile = &fomod.condFiles[condId];
|
||||
|
||||
bool areAllFlagsValid = true;
|
||||
|
||||
//checking if all flags are valid
|
||||
for(int flagId = 0; flagId < condFile->flagCount; flagId++) {
|
||||
const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)flagEqual);
|
||||
if(link == NULL) {
|
||||
areAllFlagsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(areAllFlagsValid) {
|
||||
for(int fileId = 0; fileId < condFile->flagCount; fileId++) {
|
||||
const FOModFile_t * file = &(condFile->files[fileId]);
|
||||
|
||||
FOModFile_t * fileCopy = malloc(sizeof(*file));
|
||||
*fileCopy = *file;
|
||||
|
||||
pendingFileOperations = g_list_append(pendingFileOperations, fileCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pendingFileOperations = g_list_sort(pendingFileOperations, priorityCmp);
|
||||
GList * currentFileOperation = pendingFileOperations;
|
||||
|
||||
while(currentFileOperation != NULL) {
|
||||
//TODO: support destination
|
||||
//TODO: handle error
|
||||
//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;
|
||||
char * source = g_build_path("/", modFolder, file->source, NULL);
|
||||
|
||||
//fix the / and \ windows - unix paths
|
||||
fixPath(source);
|
||||
|
||||
int copyResult;
|
||||
if(file->isFolder) {
|
||||
copyResult = copy(source, destination, CP_NO_TARGET_DIR | CP_RECURSIVE);
|
||||
} else {
|
||||
copyResult = copy(source, destination, 0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
pendingFileOperations = processCondFiles(&fomod, flagList, pendingFileOperations);
|
||||
processFileOperations(pendingFileOperations, modFolder, destination);
|
||||
|
||||
printf("FOMod successfully installed!\n");
|
||||
g_list_free(flagList);
|
||||
g_list_free_full(pendingFileOperations, free);
|
||||
freeFOMod(&fomod);
|
||||
g_free(fomodFolder);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+32
@@ -2,9 +2,41 @@
|
||||
#define __FOMOD_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <glib.h>
|
||||
#include "fomod/parser.h"
|
||||
|
||||
#include "fomod/group.h"
|
||||
#include "fomod/xmlUtil.h"
|
||||
|
||||
/**
|
||||
* @brief Start a terminal based install process for fomod.
|
||||
*
|
||||
* @param modFolder folder of the fomod file.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
* @param fomod a pointer to a fomod obtained by the parser
|
||||
* @param flagList a FOModFlag_t list which contains all of the flag found.
|
||||
* @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));
|
||||
|
||||
/**
|
||||
* @brief FOModFile_t have a priority option and this function execute the file operation while taking this into account.
|
||||
*
|
||||
* @param pendingFileOperations list of FOModFile_t to process
|
||||
* @param modFolder folder of the fomod file.
|
||||
* @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);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+6
-3
@@ -2,6 +2,7 @@
|
||||
#include "libxml/tree.h"
|
||||
#include "xmlUtil.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//Maybe integrate this into the rest of the code instead of freeing after the fact
|
||||
void freeFOMod(FOMod_t * fomod) {
|
||||
@@ -21,11 +22,10 @@ void freeFOMod(FOMod_t * fomod) {
|
||||
free(condFile->requiredFlags);
|
||||
}
|
||||
free(fomod->condFiles);
|
||||
|
||||
free(fomod->moduleImage);
|
||||
free(fomod->moduleName);
|
||||
|
||||
int size = countUntilNull(fomod->requiredInstallFiles);
|
||||
int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **));
|
||||
for(int i = 0; i < size; i++) {
|
||||
free(fomod->requiredInstallFiles[i]);
|
||||
}
|
||||
@@ -46,6 +46,9 @@ void freeFOMod(FOMod_t * fomod) {
|
||||
free(step->requiredFlags);
|
||||
free(step->name);
|
||||
}
|
||||
|
||||
//set every counter to zero and every pointer to null
|
||||
memset(fomod, 0, sizeof(FOMod_t));
|
||||
}
|
||||
|
||||
int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
|
||||
@@ -285,7 +288,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int size = countUntilNull(fomod->requiredInstallFiles) + 2;
|
||||
int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **)) + 2;
|
||||
fomod->requiredInstallFiles = realloc(fomod->requiredInstallFiles, sizeof(char *) * size);
|
||||
//ensure it is null terminated
|
||||
fomod->requiredInstallFiles[size - 1] = NULL;
|
||||
|
||||
@@ -25,7 +25,19 @@ typedef struct FOMod {
|
||||
int condFilesCount;
|
||||
} FOMod_t;
|
||||
|
||||
/**
|
||||
* @brief parse a fomod xml file (found inside the mod folder /fomod/moduleconfig.xml (everything should be lowercase))
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Free content of a fomod file.
|
||||
* @param fomod
|
||||
*/
|
||||
void freeFOMod(FOMod_t * fomod);
|
||||
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -27,10 +27,10 @@ void fixPath(char * path) {
|
||||
}
|
||||
}
|
||||
|
||||
int countUntilNull(void * pointers) {
|
||||
int countUntilNull(void * pointers, size_t typeSize) {
|
||||
int i = 0;
|
||||
while(pointers != NULL) {
|
||||
pointers++;
|
||||
pointers += typeSize;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
|
||||
+16
-1
@@ -8,9 +8,24 @@
|
||||
typedef enum FOModOrder { ASC, DESC, ORD } FOModOrder_t;
|
||||
|
||||
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
|
||||
*/
|
||||
void fixPath(char * path);
|
||||
int countUntilNull(void * pointers);
|
||||
|
||||
/**
|
||||
* @brief Count the number of step before null
|
||||
*
|
||||
* @param pointers pointer to the list
|
||||
* @param typeSize size of each element of the list
|
||||
* @return size
|
||||
*/
|
||||
int countUntilNull(void * pointers, size_t typeSize);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the user home dir regardless if he used sudo.
|
||||
*
|
||||
* @return char* path to the home dir
|
||||
*/
|
||||
char * getHome();
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
#ifndef __INSTALL_H__
|
||||
#define __INSTALL_H__
|
||||
#include <stdlib.h>
|
||||
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
|
||||
|
||||
/**
|
||||
* @brief Add a mod to the folder defined in main.h
|
||||
*
|
||||
* @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);
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef __MACRO_H__
|
||||
#define __MACRO_H__
|
||||
|
||||
//cannot be empty it's made for fixed size array
|
||||
//cannot be empty it's made for fixed size array like the ones in steam.h
|
||||
#define LEN(array) sizeof(array) / sizeof(array[0])
|
||||
|
||||
#endif
|
||||
|
||||
+29
-14
@@ -64,15 +64,12 @@ int usage() {
|
||||
printf("Use --deploy or -d <APPID> to deploy the mods for the game\n");
|
||||
printf("Use --unbind <APPID> to rollback a deployment\n");
|
||||
printf("Use --fomod <APPID> <MODID> to create a new mod using the result from the FOMod\n");
|
||||
//TODO: as bonus
|
||||
//printf("Use --start-game <APPID> to deploy the mods and launch the game through steam\n");
|
||||
//printf("Use --steam in team cmdline options to deploy directly on game startup\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int validateAppId(const char * appIdStr) {
|
||||
//strtoul set EINVAL(after C99) if the string is invalid
|
||||
u_int32_t appid = strtoul(appIdStr, NULL, 10);
|
||||
unsigned long appid = strtoul(appIdStr, NULL, 10);
|
||||
if(errno == EINVAL) {
|
||||
printf("Appid has to be a valid number\n");
|
||||
return -1;
|
||||
@@ -89,7 +86,8 @@ int validateAppId(const char * appIdStr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return appid;
|
||||
//no valid appid goes far enough to justify long
|
||||
return (int)appid;
|
||||
}
|
||||
|
||||
int listGames(int argc, char ** argv) {
|
||||
@@ -100,7 +98,7 @@ int listGames(int argc, char ** argv) {
|
||||
printf("No game found\n");
|
||||
} else {
|
||||
while(gamesIds != NULL) {
|
||||
int * gameIndex = (int*)gamesIds->data;
|
||||
const int * gameIndex = (int*)gamesIds->data;
|
||||
printf("%d: %s\n", GAMES_APPIDS[*gameIndex], GAMES_NAMES[*gameIndex]);
|
||||
gamesIds = g_list_next(gamesIds);
|
||||
}
|
||||
@@ -111,9 +109,9 @@ int listGames(int argc, char ** argv) {
|
||||
|
||||
int add(int argc, char ** argv) {
|
||||
if(argc != 4) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
const char * appIdStr = argv[2];
|
||||
|
||||
u_int32_t appid = validateAppId(appIdStr);
|
||||
int appid = validateAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -168,7 +166,7 @@ int listAllMods(int argc, char ** argv) {
|
||||
int installAndUninstallMod(int argc, char ** argv, bool install) {
|
||||
if(argc != 4) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
u_int32_t appid = validateAppId(appIdStr);
|
||||
int appid = validateAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -176,7 +174,7 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
|
||||
int returnValue = EXIT_SUCCESS;
|
||||
|
||||
//strtoul set EINVAL if the string is invalid
|
||||
u_int32_t modId = strtoul(argv[3], NULL, 10);
|
||||
unsigned long modId = strtoul(argv[3], NULL, 10);
|
||||
if(errno == EINVAL) {
|
||||
printf("ModId has to be a valid number\n");
|
||||
return -1;
|
||||
@@ -185,6 +183,7 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
|
||||
//might crash if no mods were installed
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
free(home);
|
||||
GList * mods = listFilesInFolder(modFolder);
|
||||
GList * modsFirstPointer = mods;
|
||||
|
||||
@@ -208,9 +207,14 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
|
||||
}
|
||||
|
||||
//Create activated file
|
||||
FILE * fd = fopen(modFlag, "w");
|
||||
FILE * fd = fopen(modFlag, "w+");
|
||||
if(fd != NULL) {
|
||||
fwrite("", 1, 1, fd);
|
||||
fclose(fd);
|
||||
} else {
|
||||
printf("Could not interact with the activation file\n");
|
||||
returnValue = EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if(access(modFlag, F_OK) != 0) {
|
||||
printf("The mod is not activated \n");
|
||||
@@ -287,6 +291,8 @@ int deploy(int argc, char ** argv) {
|
||||
free(modFlag);
|
||||
}
|
||||
|
||||
g_free(modFolder);
|
||||
|
||||
//const char * data = "lowerdir=gameFolder,gameFolder2,gameFolder3..."
|
||||
modsToInstall[modCount] = gameFolder;
|
||||
modsToInstall[modCount + 1] = NULL;
|
||||
@@ -297,6 +303,7 @@ int deploy(int argc, char ** argv) {
|
||||
|
||||
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);
|
||||
|
||||
//unmount the game folder
|
||||
//DETACH + FORCE allow us to be sure it will be unload.
|
||||
@@ -317,12 +324,16 @@ int deploy(int argc, char ** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
g_free(steamGameFolder);
|
||||
g_free(gameUpperDir);
|
||||
g_free(gameWorkDir);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int setup(int argc, char ** argv) {
|
||||
if(argc != 3 ) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
const char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
@@ -372,7 +383,7 @@ int setup(int argc, char ** argv) {
|
||||
|
||||
int unbind(int argc, char ** argv) {
|
||||
if(argc != 3 ) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
const char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
@@ -389,7 +400,7 @@ int unbind(int argc, char ** argv) {
|
||||
|
||||
int removeMod(int argc, char ** argv) {
|
||||
if(argc != 4) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
const char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
printf("Invalid appid");
|
||||
@@ -424,6 +435,7 @@ int removeMod(int argc, char ** argv) {
|
||||
delete(filename, true);
|
||||
|
||||
g_free(filename);
|
||||
g_free(modFolder);
|
||||
g_list_free_full(modsFirstPointer, free);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -473,6 +485,9 @@ int fomod(int argc, char ** argv) {
|
||||
|
||||
free(destination);
|
||||
g_list_free_full(modsFirstPointer, free);
|
||||
g_free(modDestination);
|
||||
g_free(modPath);
|
||||
g_free(modFolder);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
// in c "A" "B" is the same as "AB"
|
||||
#define MANAGER_FILES ".local/share/" APP_NAME
|
||||
#define MOD_FOLDER_NAME "MOD_FOLDER"
|
||||
//the folder in which the games file will be linked or copied
|
||||
#define GAME_FOLDER_NAME "GAME_FOLDER"
|
||||
//the director that will contain all modifications to game files while being deployed
|
||||
#define GAME_UPPER_DIR_NAME "UPPER_DIRS"
|
||||
//overlayfs temporary dir.
|
||||
#define GAME_WORK_DIR_NAME "WORK_DIRS"
|
||||
|
||||
@@ -96,6 +96,5 @@ void swapPlace(int appid, int modId, int modId2) {
|
||||
GList * list = listMods(appid);
|
||||
|
||||
|
||||
|
||||
g_list_free(list);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,15 @@
|
||||
* @return GList of char containing the name of the mod folder in order
|
||||
*/
|
||||
GList * listMods(int appid);
|
||||
|
||||
/**
|
||||
* @brief Change the mod order by swaping two mod
|
||||
*
|
||||
* @param appid the game
|
||||
* index of the mod in the mod list.
|
||||
* @param modId
|
||||
* @param modId2
|
||||
*/
|
||||
void swapPlace(int appid, int modId, int modId2);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
#ifndef __OVERLAY_H__
|
||||
#define __OVERLAY_H__
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param sources a null ended table of folder that will overlay of the game files
|
||||
* @param dest the overlayed folder(the game data folder)
|
||||
* @param upperdir the director that will store the changed files
|
||||
* @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);
|
||||
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -223,9 +223,9 @@ GHashTable* search_games(int * status) {
|
||||
}
|
||||
|
||||
|
||||
int getGameIdFromAppId(u_int32_t id) {
|
||||
int getGameIdFromAppId(u_int32_t appid) {
|
||||
for(int k = 0; k < LEN(GAMES_APPIDS); k++) {
|
||||
if(id == GAMES_APPIDS[k]) {
|
||||
if(appid == GAMES_APPIDS[k]) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-1
@@ -36,14 +36,32 @@ const static char * steamLibraries[] = {
|
||||
// order has to be the same as in GAMES_NAMES
|
||||
const static u_int32_t GAMES_APPIDS[] = {
|
||||
489830,
|
||||
22330,
|
||||
377160
|
||||
};
|
||||
|
||||
//the name of the game in the steamapps/common folder
|
||||
const static char * GAMES_NAMES[] = {
|
||||
"Skyrim Special Edition",
|
||||
"Oblivion",
|
||||
"Fallout 4"
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief list all installed games and the paths to the game's files
|
||||
*
|
||||
* @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 *)
|
||||
*/
|
||||
//TODO: flip the return value and parameter
|
||||
GHashTable* search_games(int * status);
|
||||
int getGameIdFromAppId(u_int32_t id);
|
||||
|
||||
/**
|
||||
* @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS
|
||||
*
|
||||
* @param appid
|
||||
* @return -1 in case of failure or the index of the game.
|
||||
*/
|
||||
int getGameIdFromAppId(u_int32_t appid);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user