Merge branch 'master' of ssh.marcbarbier.fr:Marc/modmanager into interface

This commit is contained in:
Marc
2022-10-13 10:38:55 +02:00
32 changed files with 1323 additions and 650 deletions
+27 -15
View File
@@ -1,20 +1,34 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
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 "-g")
# generate the compile_commands for vscode / clang # generate the compile_commands for vscode / clang
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
# set the project name # set the project name
project(ModManager) project(mod-manager)
set(SOURCES set(SOURCES
src/steam.c src/steam.c
src/loadOrder.c
src/install.c src/install.c
src/overlayfs.c src/overlayfs.c
src/getDataPath.c
src/getHome.c src/getHome.c
src/file.c src/file.c
src/order.c src/order.c
src/archives.c
src/fomod.c src/fomod.c
src/fomod/group.c src/fomod/group.c
src/fomod/xmlUtil.c src/fomod/xmlUtil.c
@@ -23,9 +37,7 @@ set(SOURCES
) )
# add the executable # add the executable
add_executable(ModManager src/main.c ${SOURCES}) add_executable(mod-manager src/main.c ${SOURCES})
add_compile_options(-Wall -Wextra -pedantic -Werror)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_search_module(GLIB REQUIRED glib-2.0) pkg_search_module(GLIB REQUIRED glib-2.0)
@@ -33,17 +45,17 @@ pkg_search_module(AUDIT REQUIRED audit)
pkg_search_module(LIBXML REQUIRED libxml-2.0) pkg_search_module(LIBXML REQUIRED libxml-2.0)
pkg_search_module(GTK REQUIRED gtk4) pkg_search_module(GTK REQUIRED gtk4)
target_include_directories(ModManager PRIVATE ${GLIB_INCLUDE_DIRS}) target_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS})
target_include_directories(ModManager PRIVATE ${LIBXML_INCLUDE_DIRS}) target_include_directories(mod-manager PRIVATE ${LIBXML_INCLUDE_DIRS})
target_include_directories(ModManager PRIVATE ${AUDIT_INCLUDE_DIRS}) target_include_directories(mod-manager PRIVATE ${AUDIT_INCLUDE_DIRS})
target_include_directories(ModManager PRIVATE ${GTK_INCLUDE_DIRS}) target_include_directories(mod-manager PRIVATE ${GTK_INCLUDE_DIRS})
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
target_link_libraries(ModManager ${GLIB_LDFLAGS}) target_link_libraries(mod-manager ${GLIB_LDFLAGS})
target_link_libraries(ModManager ${LIBXML_LIBRARIES}) target_link_libraries(mod-manager ${LIBXML_LIBRARIES})
target_link_libraries(ModManager ${AUDIT_LIBRARIES}) target_link_libraries(mod-manager ${AUDIT_LIBRARIES})
target_link_libraries(ModManager ${GTK_LIBRARIES}) target_link_libraries(mod-manager ${GTK_LIBRARIES})
install(TARGETS ModManager DESTINATION bin) install(TARGETS mod-manager DESTINATION bin)
set_property(TARGET mod-manager PROPERTY C_STANDARD 23)
+26
View File
@@ -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
}
+19
View File
@@ -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
+96
View File
@@ -0,0 +1,96 @@
#include "archives.h"
#include "file.h"
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <glib.h>
int archive_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 archive_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 archive_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.
file_casefold(outdir);
return returnValue;
}
}
+30
View File
@@ -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 archive_unzip(char * path, char * outdir);
/**
* @brief Execute the unrar command
* @param path path to archive
* @param outdir output director
* @return int return code
*/
int archive_unrar(char * path, char * outdir);
/**
* @brief Execute the 7z command
* @param path path to archive
* @param outdir output director
* @return int return code
*/
int archive_un7z(char * path, const char * outdir);
#endif
+53 -22
View File
@@ -8,7 +8,7 @@
#include <glib.h> #include <glib.h>
#include "file.h" #include "file.h"
u_int32_t countSetBits(u_int32_t n) { static u_int32_t countSetBits(u_int32_t n) {
// base case // base case
if (n == 0) if (n == 0)
return 0; return 0;
@@ -19,28 +19,30 @@ u_int32_t countSetBits(u_int32_t n) {
//TODO: add interruption support //TODO: add interruption support
//simplest way to copy a file in c(linux) //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); int flagCount = countSetBits(flags);
if(flagCount > 3) { if(flagCount > 3) {
printf("Invalid flags for cp command\n"); fprintf(stderr, "Invalid flags for cp command\n");
return -1; return -1;
} }
//flags + cp + path + dest //flags + cp + path + dest
const char * args[flagCount + 4]; char * args[flagCount + 4];
args[0] = "/bin/cp"; args[0] = "/bin/cp";
args[1] = path; args[1] = alloca((strlen(path) + 1) * sizeof(char));
args[2] = dest; strcpy(args[1], path);
args[2] = alloca((strlen(dest) + 1) * sizeof(char));
strcpy(args[2], dest);
int argIndex = 3; int argIndex = 3;
if(flags & CP_LINK) { if(flags & FILE_CP_LINK) {
args[argIndex] = "--link"; args[argIndex] = "--link";
argIndex += 1; argIndex += 1;
} }
if(flags & CP_RECURSIVE) { if(flags & FILE_CP_RECURSIVE) {
args[argIndex] = "-r"; args[argIndex] = "-r";
argIndex += 1; argIndex += 1;
} }
if(flags & CP_NO_TARGET_DIR) { if(flags & FILE_CP_NO_TARGET_DIR) {
args[argIndex] = "-T"; args[argIndex] = "-T";
argIndex += 1; argIndex += 1;
} }
@@ -50,7 +52,7 @@ int copy(const char * path, const char * dest, u_int32_t flags) {
int pid = fork(); int pid = fork();
if(pid == 0) { if(pid == 0) {
//discard the const. since we are in a fork we don't care. //discard the const. since we are in a fork we don't care.
execv("/bin/cp", (char **)args); execv("/bin/cp", args);
return 0; return 0;
} else { } else {
int returnValue; int returnValue;
@@ -59,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(); int pid = fork();
if(pid == 0) { if(pid == 0) {
if(recursive) { if(recursive) {
@@ -75,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(); int pid = fork();
if(pid == 0) { if(pid == 0) {
execl("/bin/mv", "/bin/mv", source, destination, NULL); execl("/bin/mv", "/bin/mv", source, destination, NULL);
@@ -90,7 +92,7 @@ int move(const char * source, const char * destination) {
//rename a folder and all subfolder and files to lowercase //rename a folder and all subfolder and files to lowercase
//TODO: error handling //TODO: error handling
void casefold(const char * folder) { void file_casefold(const char * folder) {
DIR * d = opendir(folder); DIR * d = opendir(folder);
struct dirent *dir; struct dirent *dir;
if (d) { if (d) {
@@ -98,24 +100,53 @@ void casefold(const char * folder) {
//removes .. & . from the list //removes .. & . from the list
if(strcmp(dir->d_name, "..") != 0 && strcmp(dir->d_name, ".") != 0) { if(strcmp(dir->d_name, "..") != 0 && strcmp(dir->d_name, ".") != 0) {
//only look at ascii and hope for the best. //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 * destinationName = g_ascii_strdown(dir->d_name, -1);
gchar * destination = g_build_path("/", folder,destinationName, NULL); gchar * destination = g_build_path("/", folder,destinationName, NULL);
int result = move(file, destination);
if(result != EXIT_SUCCESS) {
printf("Move failed: %s => %s \n", dir->d_name, destinationName);
}
g_free(destinationName);
g_free(destination);
if(dir->d_type == DT_DIR) { if(strcmp(destinationName, dir->d_name) != 0) {
casefold(file);
int result = file_move(file, destination);
if(result != EXIT_SUCCESS) {
fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName);
}
} }
g_free(file); g_free(file);
g_free(destinationName);
if(dir->d_type == DT_DIR) {
file_casefold(destination);
}
g_free(destination);
} }
} }
closedir(d); closedir(d);
} }
} }
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--) {
if(filePath[i] == delimeter) {
index = i + 1;
break;
}
}
if(index <= 0 || index == length) return NULL;
return &filePath[index];
}
const char * file_extractExtension(const char * filePath) {
return file_extractLastPart(filePath, '.');
}
const char * file_extractFileName(const char * filePath) {
return file_extractLastPart(filePath, '/');
}
+31 -13
View File
@@ -1,33 +1,34 @@
#include <stdbool.h>
#include <sys/types.h>
#ifndef __FILE_H__ #ifndef __FILE_H__
#define __FILE_H__ #define __FILE_H__
#include <stdbool.h>
#include <sys/types.h>
#include "main.h"
//valid copy flags //valid copy flags
#define cp_DEFAULT 0 #define FILE_CP_DEFAULT 0
#define CP_LINK 1 #define FILE_CP_LINK 1
#define CP_RECURSIVE 2 #define FILE_CP_RECURSIVE 2
#define CP_NO_TARGET_DIR 4 #define FILE_CP_NO_TARGET_DIR 4
/** /**
* @brief execute the cp command from the source to the dest * @brief execute the cp command from the source to the dest
* @param source path to the source files * @param source path to the source files
* @param dest path to the destination folder * @param dest path to the destination folder
* @param flags refer to the "valid copy flags" use them like this CP_LINK | CP_RECURSIVE * @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); int file_copy(const char * source, const char * dest, u_int32_t flags);
/** /**
* @brief execute the cp command from the source to the dest * @brief execute the cp command from the source to the dest
* @param source path to the source files * @param source path to the source files
* @param dest path to the destination folder * @param dest path to the destination folder
* @param bool enable recursive rm -r * @param bool enable recursive rm -r
* @return status code * @return rm return value
*/ */
int delete(const char * path, bool recursive); int file_delete(const char * path, bool recursive);
/** /**
* @brief Run the mv command * @brief Run the mv command
@@ -35,13 +36,30 @@ int delete(const char * path, bool recursive);
* @param destination * @param destination
* @return mv's exit value * @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. * @brief Recursively rename all file and folder to lowercase.
* *
* @param folder * @param folder
*/ */
void casefold(const char * folder); void file_casefold(const char * folder);
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 * 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 * file_extractFileName(const char * filePath);
#endif #endif
+142 -56
View File
@@ -12,16 +12,19 @@
#include "fomod.h" #include "fomod.h"
#include "file.h" #include "file.h"
#include "fomod/fomodTypes.h"
#include "fomod/group.h"
#include "libxml/globals.h" #include "libxml/globals.h"
#include "main.h"
int getInputCount(const char * input) { static int getInputCount(const char * input) {
char buff[2]; char buff[2];
buff[1] = '\0'; buff[1] = '\0';
int elementCount = 0; int elementCount = 0;
bool prevWasValue = false; 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]; buff[0] = input[i];
//if the character is a valid character //if the character is a valid character
@@ -40,21 +43,21 @@ int getInputCount(const char * input) {
return elementCount; return elementCount;
} }
gint priorityCmp(gconstpointer a, gconstpointer b) { static gint priorityCmp(gconstpointer a, gconstpointer b) {
const FOModFile_t * fileA = (const FOModFile_t *)a; const fomod_File_t * fileA = (const fomod_File_t *)a;
const FOModFile_t * fileB = (const FOModFile_t *)b; const fomod_File_t * fileB = (const fomod_File_t *)b;
return fileA->priority - fileB->priority; return fileB->priority - fileA->priority;
} }
void printfOptionsInOrder(FOModGroup_t group) { static void fomod_printOptionsInOrder(fomod_Group_t group) {
for(int i = 0; i < group.pluginCount; i++) { for(int i = 0; i < group.pluginCount; i++) {
printf("%d, %s\n", i, group.plugins[i].name); printf("%d, %s\n", i, group.plugins[i].name);
printf("%s\n", group.plugins[i].description); printf("%s\n", group.plugins[i].description);
} }
} }
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); int nameCmp = strcmp(a->name, b->name);
if(nameCmp == 0) { if(nameCmp == 0) {
if(strcmp(a->value, b->value) == 0) if(strcmp(a->value, b->value) == 0)
@@ -66,20 +69,20 @@ gint flagEqual(const FOModFlag_t * a, const FOModFlag_t * b) {
return nameCmp; 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 * step1 = (const FOModStep_t *)stepA;
const FOModStep_t * step2 = (const FOModStep_t *)stepB; const FOModStep_t * step2 = (const FOModStep_t *)stepB;
return strcmp(step1->name, step2->name); 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 * step1 = (const FOModStep_t *)stepA;
const FOModStep_t * step2 = (const FOModStep_t *)stepB; const FOModStep_t * step2 = (const FOModStep_t *)stepB;
return 1 - strcmp(step1->name, step2->name); return 1 - strcmp(step1->name, step2->name);
} }
void sortSteps(FOMod_t * fomod) { static void fomod_sortSteps(FOMod_t * fomod) {
switch(fomod->stepOrder) { switch(fomod->stepOrder) {
case ASC: case ASC:
qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpAsc); qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpAsc);
@@ -93,25 +96,25 @@ void sortSteps(FOMod_t * fomod) {
} }
} }
int groupCmpAsc(const void * stepA, const void * stepB) { static int fomod_groupCmpAsc(const void * stepA, const void * stepB) {
const FOModGroup_t * step1 = (const FOModGroup_t *)stepA; const fomod_Group_t * step1 = (const fomod_Group_t *)stepA;
const FOModGroup_t * step2 = (const FOModGroup_t *)stepB; const fomod_Group_t * step2 = (const fomod_Group_t *)stepB;
return strcmp(step1->name, step2->name); return strcmp(step1->name, step2->name);
} }
int groupCmpDesc(const void * stepA, const void * stepB) { static int fomod_groupCmpDesc(const void * stepA, const void * stepB) {
const FOModGroup_t * step1 = (const FOModGroup_t *)stepA; const fomod_Group_t * step1 = (const fomod_Group_t *)stepA;
const FOModGroup_t * step2 = (const FOModGroup_t *)stepB; const fomod_Group_t * step2 = (const fomod_Group_t *)stepB;
return 1 - strcmp(step1->name, step2->name); return 1 - strcmp(step1->name, step2->name);
} }
void sortGroup(FOModGroup_t * group) { static void fomod_sortGroup(fomod_Group_t * group) {
switch(group->order) { switch(group->order) {
case ASC: case ASC:
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpAsc); qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpAsc);
break; break;
case DESC: case DESC:
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpDesc); qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpDesc);
break; break;
case ORD: case ORD:
//ord mean that we keep the curent order, so no need to sort anything. //ord mean that we keep the curent order, so no need to sort anything.
@@ -120,45 +123,45 @@ void sortGroup(FOModGroup_t * group) {
} }
//TODO: handle error //TODO: handle error
int processFileOperations(GList * pendingFileOperations, const char * modFolder, const char * destination) { error_t fomod_processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) {
pendingFileOperations = g_list_sort(pendingFileOperations, priorityCmp); //priority higher a less important and should be processed first.
GList * currentFileOperation = pendingFileOperations; *pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp);
GList * currentFileOperation = *pendingFileOperations;
while(currentFileOperation != NULL) { while(currentFileOperation != NULL) {
//TODO: support destination //TODO: support destination
//no using link since priority is made to override files and link is annoying to deal with when overriding files. //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); char * source = g_build_path("/", modFolder, file->source, NULL);
//fix the / and \ windows - unix paths //fix the / and \ windows - unix paths
fixPath(source); xml_fixPath(source);
int copyResult; int copyResult;
if(file->isFolder) { 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 { } else {
copyResult = copy(source, destination, 0); copyResult = file_copy(source, destination, 0);
} }
if(copyResult != EXIT_SUCCESS) { if(copyResult != EXIT_SUCCESS) {
fprintf(stderr, "Copy failed, some file might be corrupted\n"); fprintf(stderr, "Copy failed, some file might be corrupted\n");
} }
printf("source: %s, destination: %s\n", source, destination);
g_free(source); g_free(source);
currentFileOperation = g_list_next(currentFileOperation); currentFileOperation = g_list_next(currentFileOperation);
} }
return EXIT_SUCCESS; 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++) { 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; bool areAllFlagsValid = true;
//checking if all flags are valid //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); const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)fomod_flagEqual);
if(link == NULL) { if(link == NULL) {
areAllFlagsValid = false; areAllFlagsValid = false;
break; break;
@@ -166,12 +169,19 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin
} }
if(areAllFlagsValid) { 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]); const fomod_File_t * file = &(condFile->files[fileId]);
FOModFile_t * fileCopy = malloc(sizeof(*file)); fomod_File_t * fileCopy = malloc(sizeof(*file));
*fileCopy = *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); pendingFileOperations = g_list_append(pendingFileOperations, fileCopy);
} }
} }
@@ -179,7 +189,19 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin
return pendingFileOperations; return pendingFileOperations;
} }
int installFOMod(const char * modFolder, const char * destination) { void fomod_freeFileOperations(GList * fileOperations) {
GList * fileOperationsStart = fileOperations;
while(fileOperations != NULL) {
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);
}
g_list_free_full(fileOperationsStart, free);
}
error_t fomod_installFOMod(const char * modFolder, const char * destination) {
//everything should be lowercase since we use casefold() before calling any install function //everything should be lowercase since we use casefold() before calling any install function
char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL); char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL);
char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL); char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL);
@@ -188,27 +210,27 @@ int installFOMod(const char * modFolder, const char * destination) {
fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n"); fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n");
g_free(fomodFolder); g_free(fomodFolder);
g_free(fomodFile); g_free(fomodFile);
return EXIT_FAILURE; return ERR_FAILURE;
} }
FOMod_t fomod; FOMod_t fomod;
int returnValue = parseFOMod(fomodFile, &fomod); int returnValue = parser_parseFOMod(fomodFile, &fomod);
if(returnValue != EXIT_SUCCESS) { if(returnValue == ERR_FAILURE)
return returnValue; return ERR_FAILURE;
}
g_free(fomodFile); g_free(fomodFile);
GList * flagList = NULL; GList * flagList = NULL;
GList * pendingFileOperations = NULL; GList * pendingFileOperations = NULL;
sortSteps(&fomod); fomod_sortSteps(&fomod);
for(int i = 0; i < fomod.stepCount; i++) { for(int i = 0; i < fomod.stepCount; i++) {
const FOModStep_t * step = &fomod.steps[i]; const FOModStep_t * step = &fomod.steps[i];
bool validFlags = true; bool validFlags = true;
for(int flagId = 0; flagId < step->flagCount; flagId++) { 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) { if(flagLink == NULL) {
validFlags = false; validFlags = false;
break; break;
@@ -218,9 +240,9 @@ int installFOMod(const char * modFolder, const char * destination) {
if(!validFlags) continue; if(!validFlags) continue;
for(int groupId = 0; groupId < step->groupCount; groupId++ ) { 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 min;
u_int8_t max; u_int8_t max;
@@ -229,7 +251,7 @@ int installFOMod(const char * modFolder, const char * destination) {
size_t bufferSize = 0; size_t bufferSize = 0;
while(true) { while(true) {
printfOptionsInOrder(group); fomod_printOptionsInOrder(group);
switch(group.type) { switch(group.type) {
case ONE_ONLY: case ONE_ONLY:
printf("Select one :\n"); printf("Select one :\n");
@@ -260,6 +282,10 @@ int installFOMod(const char * modFolder, const char * destination) {
min = 0; min = 0;
max = 0; max = 0;
break; break;
default:
//never happen;
fprintf(stderr, "unexpected type please report this issue %d, %d", group.type, __LINE__);
return ERR_FAILURE;
} }
@@ -278,24 +304,36 @@ int installFOMod(const char * modFolder, const char * destination) {
} }
} }
//processing user input
GRegex* regex = g_regex_new("\\s*", 0, 0, NULL); GRegex* regex = g_regex_new("\\s*", 0, 0, NULL);
char ** choices = g_regex_split(regex, inputBuffer, 0); char ** choices = g_regex_split(regex, inputBuffer, 0);
g_regex_unref(regex); g_regex_unref(regex);
for(int choiceId = 0; choices[choiceId] != NULL; choiceId++) { for(int choiceId = 0; choices[choiceId] != NULL; choiceId++) {
//TODO: safer user input
int choice = atoi(choices[choiceId]); 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++) { for(int flagId = 0; flagId < plugin.flagCount; flagId++) {
flagList = g_list_append(flagList, &plugin.flags[flagId]); flagList = g_list_append(flagList, &plugin.flags[flagId]);
} }
//do the install //do the install
for(int pluginId = 0; pluginId < plugin.fileCount; pluginId++) { 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(*file)); fomod_File_t * fileCopy = malloc(sizeof(fomod_File_t));
*fileCopy = *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 = 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); pendingFileOperations = g_list_append(pendingFileOperations, fileCopy);
} }
} }
@@ -305,15 +343,63 @@ int installFOMod(const char * modFolder, const char * destination) {
} }
} }
//TODO: manage multiple files with the same name
pendingFileOperations = fomod_processCondFiles(&fomod, flagList, pendingFileOperations);
pendingFileOperations = processCondFiles(&fomod, flagList, pendingFileOperations); fomod_processFileOperations(&pendingFileOperations, modFolder, destination);
processFileOperations(pendingFileOperations, modFolder, destination);
printf("FOMod successfully installed!\n"); printf("FOMod successfully installed!\n");
g_list_free(flagList); g_list_free(flagList);
g_list_free_full(pendingFileOperations, free); fomod_freeFileOperations(pendingFileOperations);
freeFOMod(&fomod); fomod_freeFOMod(&fomod);
g_free(fomodFolder); g_free(fomodFolder);
return EXIT_SUCCESS; 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));
} }
+17 -4
View File
@@ -3,8 +3,9 @@
#include <stdbool.h> #include <stdbool.h>
#include <glib.h> #include <glib.h>
#include "fomod/parser.h"
#include "main.h"
#include "fomod/parser.h"
#include "fomod/group.h" #include "fomod/group.h"
#include "fomod/xmlUtil.h" #include "fomod/xmlUtil.h"
@@ -15,7 +16,7 @@
* @param destination folder of the new mod that contains the result of the fomod process. * @param destination folder of the new mod that contains the result of the fomod process.
* @return int * @return int
*/ */
int 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. * @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.
@@ -25,7 +26,7 @@ int 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) * @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) * @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. * @brief FOModFile_t have a priority option and this function execute the file operation while taking this into account.
@@ -35,8 +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. * @param destination folder of the new mod that contains the result of the process.
* @return error code * @return error code
*/ */
int 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 fomod_freeFileOperations(GList * fileOperations);
/**
* @brief Free content of a fomod file.
* @param fomod
*/
void fomod_freeFOMod(FOMod_t * fomod);
#endif #endif
+8 -8
View File
@@ -4,25 +4,25 @@
#include "stdbool.h" #include "stdbool.h"
#include "xmlUtil.h" #include "xmlUtil.h"
typedef struct FOModFlag { typedef struct fomod_Flag {
char * name; char * name;
char * value; char * value;
} FOModFlag_t; } fomod_Flag_t;
typedef struct FOModFile { typedef struct fomod_File {
char * source; char * source;
char * destination; char * destination;
int priority; int priority;
bool isFolder; bool isFolder;
} FOModFile_t; } fomod_File_t;
typedef struct FOModCondFile { typedef struct fomod_CondFile {
FOModFlag_t * requiredFlags; fomod_Flag_t * requiredFlags;
unsigned int flagCount; unsigned int flagCount;
FOModFile_t * files; fomod_File_t * files;
unsigned int fileCount; unsigned int fileCount;
} FOModCondFile_t; } fomod_CondFile_t;
#endif #endif
+33 -32
View File
@@ -1,9 +1,10 @@
#include "group.h" #include "group.h"
#include "fomodTypes.h"
#include "xmlUtil.h" #include "xmlUtil.h"
#include "string.h" #include "string.h"
#include <stdlib.h> #include <stdlib.h>
GroupType_t getGroupType(const char * type) { static GroupType_t getGroupType(const char * type) {
if(strcmp(type, "SelectAtLeastOne") == 0) { if(strcmp(type, "SelectAtLeastOne") == 0) {
return AT_LEAST_ONE; return AT_LEAST_ONE;
} else if(strcmp(type, "SelectAtMostOne") == 0) { } else if(strcmp(type, "SelectAtMostOne") == 0) {
@@ -19,7 +20,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) { if(strcmp(descriptor, "Optional") == 0) {
return OPTIONAL; return OPTIONAL;
} else if(strcmp(descriptor, "Required") == 0) { } else if(strcmp(descriptor, "Required") == 0) {
@@ -35,10 +36,11 @@ 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; if(group->pluginCount == 0) return;
for(int pluginId = 0; pluginId < group->pluginCount; pluginId++) { 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) { if(plugin->fileCount > 0) {
for(int i = 0; i < plugin->fileCount; i++) { for(int i = 0; i < plugin->fileCount; i++) {
free(plugin->files[i].destination); free(plugin->files[i].destination);
@@ -64,10 +66,10 @@ void freeGroup(FOModGroup_t * group) {
group->pluginCount = 0; group->pluginCount = 0;
} }
int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { static int parseConditionFlags(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) {
xmlNodePtr flagNode = nodeElement->children; xmlNodePtr flagNode = nodeElement->children;
while(flagNode != NULL) { while(flagNode != NULL) {
if(!validateNode(&flagNode, true, "flag", NULL)) { if(!xml_validateNode(&flagNode, true, "flag", NULL)) {
if(plugin->flagCount > 0) { if(plugin->flagCount > 0) {
free(plugin->flags); free(plugin->flags);
} }
@@ -76,12 +78,12 @@ int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
if(flagNode == NULL)continue; if(flagNode == NULL)continue;
plugin->flagCount += 1; 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->name = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "name"));
flag->value = freeAndDup(xmlNodeGetContent(flagNode)); flag->value = xml_freeAndDup(xmlNodeGetContent(flagNode));
flagNode = flagNode->next; flagNode = flagNode->next;
} }
@@ -89,11 +91,10 @@ int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { static int parseGroupFiles(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) {
FOModFile_t * files = NULL;
xmlNodePtr fileNode = nodeElement->children; xmlNodePtr fileNode = nodeElement->children;
while(fileNode != NULL) { while(fileNode != NULL) {
if(!validateNode(&fileNode, true, "folder", "file", NULL)) { if(!xml_validateNode(&fileNode, true, "folder", "file", NULL)) {
fprintf(stderr, "Unexpected node in files"); fprintf(stderr, "Unexpected node in files");
//TODO: free //TODO: free
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -103,11 +104,11 @@ int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
plugin->fileCount += 1; plugin->fileCount += 1;
plugin->files = realloc(plugin->files, (plugin->fileCount + 1) * sizeof(FOModFile_t)); plugin->files = realloc(plugin->files, (plugin->fileCount + 1) * sizeof(fomod_File_t));
FOModFile_t * file = &plugin->files[plugin->fileCount - 1]; fomod_File_t * file = &plugin->files[plugin->fileCount - 1];
file->destination = freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "destination")); file->destination = xml_freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "destination"));
file->source = freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "source")); file->source = xml_freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "source"));
//TODO: test if it's a number //TODO: test if it's a number
xmlChar * priority = xmlGetProp(fileNode, (const xmlChar *) "priority"); xmlChar * priority = xmlGetProp(fileNode, (const xmlChar *) "priority");
@@ -123,18 +124,18 @@ int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) { static int parseNodeElement(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) {
if(xmlStrcmp(nodeElement->name, (const xmlChar *) "description") == 0) { 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) { } 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) { } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "conditionFlags") == 0) {
return parseConditionFlags(plugin, nodeElement); return parseConditionFlags(plugin, nodeElement);
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "files") == 0) { } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "files") == 0) {
return parseGroupFiles(plugin, nodeElement); return parseGroupFiles(plugin, nodeElement);
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "typeDescriptor") == 0) { } else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "typeDescriptor") == 0) {
xmlNodePtr typeNode = nodeElement->children; xmlNodePtr typeNode = nodeElement->children;
if(!validateNode(&typeNode, true, "type", NULL)) { if(!xml_validateNode(&typeNode, true, "type", NULL)) {
fprintf(stderr, "Unexpected node in typeDescriptor"); fprintf(stderr, "Unexpected node in typeDescriptor");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -145,29 +146,29 @@ int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group) { int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group) {
xmlNodePtr pluginsNode = groupNode->children; xmlNodePtr pluginsNode = groupNode->children;
if(!validateNode(&pluginsNode, true, "plugins", NULL)) { if(!xml_validateNode(&pluginsNode, true, "plugins", NULL)) {
return EXIT_FAILURE; 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"); xmlChar * type = xmlGetProp(groupNode, (const xmlChar *) "type");
group->type = getGroupType((const char *)type); group->type = getGroupType((const char *)type);
xmlFree(type); xmlFree(type);
char * order = (char *) xmlGetProp(pluginsNode, (const xmlChar *) "order"); char * order = (char *) xmlGetProp(pluginsNode, (const xmlChar *) "order");
group->order = getFOModOrder(order); group->order = fomod_getOrder(order);
xmlFree(order); xmlFree(order);
FOModPlugin_t * plugins = NULL; fomod_Plugin_t * plugins = NULL;
int pluginCount = 0; int pluginCount = 0;
xmlNodePtr currentPlugin = pluginsNode->children; xmlNodePtr currentPlugin = pluginsNode->children;
while(currentPlugin != NULL) { while(currentPlugin != NULL) {
if(!validateNode(&currentPlugin, true, "plugin", NULL)) { if(!xml_validateNode(&currentPlugin, true, "plugin", NULL)) {
//TODO handle error; //TODO handle error;
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -177,12 +178,12 @@ int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group) {
pluginCount += 1; pluginCount += 1;
plugins = realloc(plugins, pluginCount * sizeof(FOModPlugin_t)); plugins = realloc(plugins, pluginCount * sizeof(fomod_Plugin_t));
FOModPlugin_t * plugin = &plugins[pluginCount - 1]; fomod_Plugin_t * plugin = &plugins[pluginCount - 1];
//initialise everything to 0 and null pointers //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; xmlNodePtr nodeElement = currentPlugin->children;
while(nodeElement != NULL) { 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. //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->plugins = plugins;
group->pluginCount = pluginCount; group->pluginCount = pluginCount;
freeGroup(group); grp_freeGroup(group);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
+11 -10
View File
@@ -3,32 +3,33 @@
#include <libxml/parser.h> #include <libxml/parser.h>
#include "fomodTypes.h" #include "fomodTypes.h"
#include "xmlUtil.h"
typedef enum GroupType_t { ONE_ONLY, ANY, AT_LEAST_ONE, AT_MOST_ONE, ALL } GroupType_t; 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 enum TypeDescriptor { OPTIONAL, MAYBE_USABLE, NOT_USABLE, REQUIRED, RECOMMENDED } TypeDescriptor_t;
typedef struct FOModPlugin { typedef struct fomod_Plugin {
char * description; char * description;
char * image; char * image;
FOModFlag_t * flags; fomod_Flag_t * flags;
int flagCount; int flagCount;
FOModFile_t * files; fomod_File_t * files;
int fileCount; int fileCount;
TypeDescriptor_t type; TypeDescriptor_t type;
char * name; char * name;
} FOModPlugin_t; } fomod_Plugin_t;
//combine group and "plugins" //combine group and "plugins"
typedef struct FOModGroup { typedef struct fomod_Group {
FOModPlugin_t * plugins; fomod_Plugin_t * plugins;
int pluginCount; int pluginCount;
GroupType_t type; GroupType_t type;
char * name; char * name;
FOModOrder_t order; fomod_Order_t order;
} FOModGroup_t; } fomod_Group_t;
int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group); int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group);
void freeGroup(FOModGroup_t * group); void grp_freeGroup(fomod_Group_t * group);
#endif #endif
+47 -91
View File
@@ -1,62 +1,17 @@
#include "parser.h" #include "parser.h"
#include "fomodTypes.h"
#include "group.h"
#include "libxml/tree.h" #include "libxml/tree.h"
#include "xmlUtil.h" #include "xmlUtil.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
//Maybe integrate this into the rest of the code instead of freeing after the fact static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
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++) {
free(condFile->files[fileId].destination);
free(condFile->files[fileId].source);
}
for(int 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);
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->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) {
xmlNodePtr requiredFlagsNode = node->children; xmlNodePtr requiredFlagsNode = node->children;
while (requiredFlagsNode != NULL) { while (requiredFlagsNode != NULL) {
if(!validateNode(&requiredFlagsNode, true, "flagDependency", NULL)) { if(!xml_validateNode(&requiredFlagsNode, true, "flagDependency", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -65,10 +20,10 @@ int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
if(requiredFlagsNode == NULL)break; if(requiredFlagsNode == NULL)break;
step->flagCount += 1; step->flagCount += 1;
step->requiredFlags = realloc(step->requiredFlags, step->flagCount * sizeof(FOModFlag_t)); step->requiredFlags = realloc(step->requiredFlags, step->flagCount * sizeof(fomod_Flag_t));
FOModFlag_t * flag = &(step->requiredFlags[step->flagCount - 1]); fomod_Flag_t * flag = &(step->requiredFlags[step->flagCount - 1]);
flag->name = freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "flag")); flag->name = xml_freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "flag"));
flag->value = freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "value")); flag->value = xml_freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "value"));
requiredFlagsNode = requiredFlagsNode->next; requiredFlagsNode = requiredFlagsNode->next;
} }
@@ -76,13 +31,13 @@ int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
return EXIT_SUCCESS; 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"); xmlChar * optionOrder = xmlGetProp(node, (const xmlChar *)"order");
step->optionOrder = getFOModOrder((char *)optionOrder); step->optionOrder = fomod_getOrder((char *)optionOrder);
xmlFree(optionOrder); xmlFree(optionOrder);
xmlNodePtr group = node->children; xmlNodePtr group = node->children;
while(group != NULL) { while(group != NULL) {
if(!validateNode(&group, true, "group", NULL)) { if(!xml_validateNode(&group, true, "group", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -91,8 +46,8 @@ int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) {
if(group == NULL)break; if(group == NULL)break;
step->groupCount += 1; step->groupCount += 1;
step->groups = realloc(step->groups, step->groupCount * sizeof(FOModGroup_t)); step->groups = realloc(step->groups, step->groupCount * sizeof(fomod_Group_t));
int status = parseGroup(group, &step->groups[step->groupCount - 1]); int status = grp_parseGroup(group, &step->groups[step->groupCount - 1]);
if(status != EXIT_SUCCESS) { if(status != EXIT_SUCCESS) {
//TODO: handle error //TODO: handle error
@@ -105,14 +60,14 @@ int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) { static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) {
FOModStep_t * steps = NULL; FOModStep_t * steps = NULL;
*stepCount = 0; *stepCount = 0;
xmlNodePtr stepNode = installStepsNode->children; xmlNodePtr stepNode = installStepsNode->children;
while(stepNode != NULL) { while(stepNode != NULL) {
//skipping the text node //skipping the text node
if(!validateNode(&stepNode, true, "installStep", NULL)) { if(!xml_validateNode(&stepNode, true, "installStep", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -124,7 +79,7 @@ FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) {
steps = realloc(steps, *stepCount * sizeof(FOModStep_t)); steps = realloc(steps, *stepCount * sizeof(FOModStep_t));
FOModStep_t * step = &steps[*stepCount - 1]; 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->requiredFlags = NULL;
step->flagCount = 0; step->flagCount = 0;
step->groupCount = 0; step->groupCount = 0;
@@ -146,10 +101,10 @@ FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCount) {
return steps; return steps;
} }
int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) { static int parseDependencies(xmlNodePtr node, fomod_CondFile_t * condFile) {
xmlNodePtr flagNode = node->children; xmlNodePtr flagNode = node->children;
if(!validateNode(&flagNode, true, "flagDependency", NULL)) { if(!xml_validateNode(&flagNode, true, "flagDependency", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -157,32 +112,32 @@ int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) {
while(flagNode != NULL) { while(flagNode != NULL) {
condFile->flagCount += 1; condFile->flagCount += 1;
condFile->requiredFlags = realloc(condFile->requiredFlags, condFile->flagCount * sizeof(FOModFlag_t)); condFile->requiredFlags = realloc(condFile->requiredFlags, condFile->flagCount * sizeof(fomod_Flag_t));
FOModFlag_t * flag = &(condFile->requiredFlags[condFile->flagCount - 1]); fomod_Flag_t * flag = &(condFile->requiredFlags[condFile->flagCount - 1]);
flag->name = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "flag")); flag->name = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "flag"));
flag->value = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "value")); flag->value = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "value"));
flagNode = flagNode->next; flagNode = flagNode->next;
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) { static int parseFiles(xmlNodePtr node, fomod_CondFile_t * condFile) {
xmlNodePtr filesNode = node->children; xmlNodePtr filesNode = node->children;
while(filesNode != NULL) { while(filesNode != NULL) {
if(!validateNode(&filesNode, true, "folder", "file", NULL)) { if(!xml_validateNode(&filesNode, true, "folder", "file", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
condFile->fileCount += 1; condFile->fileCount += 1;
condFile->files = realloc(condFile->files, condFile->fileCount * sizeof(FOModFile_t)); condFile->files = realloc(condFile->files, condFile->fileCount * sizeof(fomod_File_t));
FOModFile_t * flag = &(condFile->files[condFile->fileCount - 1]); fomod_File_t * flag = &(condFile->files[condFile->fileCount - 1]);
flag->source = freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "source")); flag->source = xml_freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "source"));
flag->destination = freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "destination")); flag->destination = xml_freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "destination"));
flag->priority = 0; flag->priority = 0;
flag->isFolder = xmlStrcmp(filesNode->name, (xmlChar *) "folder") == 0; flag->isFolder = xmlStrcmp(filesNode->name, (xmlChar *) "folder") == 0;
@@ -192,10 +147,10 @@ int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) { static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
xmlNodePtr patterns = node->children; xmlNodePtr patterns = node->children;
if(patterns != NULL) { if(patterns != NULL) {
if(!validateNode(&patterns, true, "patterns", NULL)) { if(!xml_validateNode(&patterns, true, "patterns", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -204,7 +159,7 @@ int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
while(currentPattern != NULL) { while(currentPattern != NULL) {
xmlNodePtr patternChild = currentPattern->children; xmlNodePtr patternChild = currentPattern->children;
if(!validateNode(&patternChild, true, "pattern", NULL)) { if(!xml_validateNode(&patternChild, true, "pattern", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
return EXIT_FAILURE; return EXIT_FAILURE;
@@ -212,8 +167,8 @@ int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
while(patternChild != NULL) { while(patternChild != NULL) {
fomod->condFilesCount += 1; fomod->condFilesCount += 1;
fomod->condFiles = realloc(fomod->condFiles, fomod->condFilesCount * sizeof(FOModCondFile_t)); fomod->condFiles = realloc(fomod->condFiles, fomod->condFilesCount * sizeof(fomod_CondFile_t));
FOModCondFile_t * condFile = &(fomod->condFiles[fomod->condFilesCount - 1]); fomod_CondFile_t * condFile = &(fomod->condFiles[fomod->condFilesCount - 1]);
condFile->fileCount = 0; condFile->fileCount = 0;
condFile->files = NULL; condFile->files = NULL;
@@ -238,7 +193,7 @@ int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int parseFOMod(const char * fomodFile, FOMod_t* fomod) { error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod) {
xmlDocPtr doc; xmlDocPtr doc;
xmlNodePtr cur; xmlNodePtr cur;
@@ -255,7 +210,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
if(doc == NULL) { if(doc == NULL) {
fprintf(stderr, "Document is not a valid xml file\n"); fprintf(stderr, "Document is not a valid xml file\n");
return EXIT_FAILURE; return ERR_FAILURE;
} }
@@ -263,12 +218,13 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
if(cur == NULL) { if(cur == NULL) {
fprintf(stderr, "emptyDocument"); fprintf(stderr, "emptyDocument");
xmlFreeDoc(doc); xmlFreeDoc(doc);
return EXIT_FAILURE; return ERR_FAILURE;
} }
if(xmlStrcmp(cur->name, (const xmlChar *) "config") != 0) { 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); 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; cur = cur->children;
@@ -277,22 +233,22 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
//might cause some issues. when will c finally support utf-8 //might cause some issues. when will c finally support utf-8
fomod->moduleName = (char *)cur->content; fomod->moduleName = (char *)cur->content;
} else if(xmlStrcmp(cur->name, (const xmlChar *) "moduleImage") == 0) { } 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) { } else if(xmlStrcmp(cur->name, (const xmlChar *)"requiredInstallFiles") == 0) {
//TODO: support non empty destination. //TODO: support non empty destination.
xmlNodePtr requiredInstallFile = cur->children; xmlNodePtr requiredInstallFile = cur->children;
while(requiredInstallFile != NULL) { while(requiredInstallFile != NULL) {
if(validateNode(&requiredInstallFile, true, "folder", "file", NULL)) { if(xml_validateNode(&requiredInstallFile, true, "folder", "file", NULL)) {
//TODO: handle error //TODO: handle error
printf("%d\n", __LINE__); printf("%d\n", __LINE__);
exit(EXIT_FAILURE); 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); fomod->requiredInstallFiles = realloc(fomod->requiredInstallFiles, sizeof(char *) * size);
//ensure it is null terminated //ensure it is null terminated
fomod->requiredInstallFiles[size - 1] = NULL; 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; requiredInstallFile = cur->children;
} }
@@ -300,11 +256,11 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
if(fomod->steps != NULL) { if(fomod->steps != NULL) {
fprintf(stderr, "Multiple 'installSteps' tags"); fprintf(stderr, "Multiple 'installSteps' tags");
//TODO: handle error //TODO: handle error
return EXIT_FAILURE; return ERR_FAILURE;
} }
xmlChar * stepOrder = xmlGetProp(cur, (xmlChar *)"order"); xmlChar * stepOrder = xmlGetProp(cur, (xmlChar *)"order");
fomod->stepOrder = getFOModOrder((char *)stepOrder); fomod->stepOrder = fomod_getOrder((char *)stepOrder);
xmlFree(stepOrder); xmlFree(stepOrder);
int stepCount = 0; int stepCount = 0;
@@ -314,7 +270,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
fprintf(stderr, "Failed to parse the install steps\n"); fprintf(stderr, "Failed to parse the install steps\n");
//TODO: manage the error properly //TODO: manage the error properly
return EXIT_FAILURE; return ERR_FAILURE;
} }
fomod->steps = steps; fomod->steps = steps;
@@ -326,5 +282,5 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
} }
xmlFreeDoc(doc); xmlFreeDoc(doc);
return EXIT_SUCCESS; return ERR_SUCCESS;
} }
+8 -13
View File
@@ -2,15 +2,17 @@
#define __FOMOD_PARSER_H__ #define __FOMOD_PARSER_H__
#include "fomodTypes.h"
#include "group.h" #include "group.h"
#include "../main.h"
//combine installStep and optionalFileGroups //combine installStep and optionalFileGroups
typedef struct FOModStep { typedef struct FOModStep {
FOModOrder_t optionOrder; fomod_Order_t optionOrder;
FOModGroup_t * groups; fomod_Group_t * groups;
int groupCount; int groupCount;
char * name; char * name;
FOModFlag_t * requiredFlags; fomod_Flag_t * requiredFlags;
int flagCount; int flagCount;
} FOModStep_t; } FOModStep_t;
@@ -18,10 +20,10 @@ typedef struct FOMod {
char * moduleName; char * moduleName;
char * moduleImage; char * moduleImage;
char ** requiredInstallFiles; char ** requiredInstallFiles;
FOModOrder_t stepOrder; fomod_Order_t stepOrder;
FOModStep_t * steps; FOModStep_t * steps;
int stepCount; int stepCount;
FOModCondFile_t * condFiles; fomod_CondFile_t * condFiles;
int condFilesCount; int condFilesCount;
} FOMod_t; } FOMod_t;
@@ -30,14 +32,7 @@ typedef struct FOMod {
* *
* @param fomodFile path to the moduleconfig.xml * @param fomodFile path to the moduleconfig.xml
* @param fomod pointer to a new FOMod_t * @param fomod pointer to a new FOMod_t
* @return error code.
*/ */
int parseFOMod(const char * fomodFile, FOMod_t* fomod); error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod);
/**
* @brief Free content of a fomod file.
* @param fomod
*/
void freeFOMod(FOMod_t * fomod);
#endif #endif
+9 -7
View File
@@ -1,14 +1,15 @@
#include "xmlUtil.h" #include "xmlUtil.h"
#include <string.h> #include <string.h>
#include <sys/types.h>
char * freeAndDup(xmlChar * line) { char * xml_freeAndDup(xmlChar * line) {
char * free = strdup((const char *) line); char * free = strdup((const char *) line);
xmlFree(line); xmlFree(line);
return free; return free;
} }
FOModOrder_t getFOModOrder(const char * order) { fomod_Order_t fomod_getOrder(const char * order) {
if(order == NULL || strcmp(order, "Ascending") == 0) { if(order == NULL || strcmp(order, "Ascending") == 0) {
return ASC; return ASC;
} else if(strcmp(order, "Explicit") == 0) { } else if(strcmp(order, "Explicit") == 0) {
@@ -20,17 +21,18 @@ FOModOrder_t getFOModOrder(const char * order) {
} }
//replace \ in the path by / //replace \ in the path by /
void fixPath(char * path) { void xml_fixPath(char * path) {
while(*path != '\0') { while(*path != '\0') {
if(*path == '\\')*path = '/'; if(*path == '\\')*path = '/';
path++; path++;
} }
} }
int countUntilNull(void * pointers, size_t typeSize) { int fomod_countUntilNull(void * pointers, size_t typeSize) {
int i = 0; int i = 0;
while(pointers != NULL) { char * arithmetic = (char *)pointers;
pointers += typeSize; while(arithmetic != NULL) {
arithmetic += typeSize;
i++; i++;
} }
return i; return i;
@@ -38,7 +40,7 @@ int countUntilNull(void * pointers, size_t typeSize) {
//names cannot contain false //names cannot contain false
//need to be null terminated //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; va_list namesPtr;
while(*node != NULL && xmlStrcmp((*node)->name, (const xmlChar *)"text") == 0) { while(*node != NULL && xmlStrcmp((*node)->name, (const xmlChar *)"text") == 0) {
+18 -6
View File
@@ -5,19 +5,31 @@
#include <stdbool.h> #include <stdbool.h>
typedef enum FOModOrder { ASC, DESC, ORD } FOModOrder_t; typedef enum FOModOrder { ASC, DESC, ORD } fomod_Order_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 xml_validateNode(xmlNodePtr * node, bool skipText, const char * names, ...);
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 * @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 \ * @brief replace / by \
* @param path * @param path
*/ */
void fixPath(char * path); void xml_fixPath(char * path);
/** /**
* @brief Count the number of step before null * @brief Count the number of step before null
@@ -26,6 +38,6 @@ void fixPath(char * path);
* @param typeSize size of each element of the list * @param typeSize size of each element of the list
* @return size * @return size
*/ */
int countUntilNull(void * pointers, size_t typeSize); int fomod_countUntilNull(void * pointers, size_t typeSize);
#endif #endif
+51
View File
@@ -0,0 +1,51 @@
#include "getDataPath.h"
#include "main.h"
#include "steam.h"
#include <glib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
error_t getDataPath(int appid, char ** destination) {
GHashTable * gamePaths;
error_t status = steam_searchGames(&gamePaths);
if(status == ERR_FAILURE) {
return ERR_FAILURE;
}
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;
}
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(dataFolderNew);
}
g_free(dataFolderNew);
g_free(dataFolderOld);
if(*destination == NULL) return ERR_FAILURE;
else return ERR_SUCCESS;
}
+15
View File
@@ -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
+2 -1
View File
@@ -1,8 +1,9 @@
#include <libaudit.h> #include <libaudit.h>
#include <pwd.h> #include <pwd.h>
#include <string.h> #include <string.h>
#include "getHome.h"
char * getHome() { char * getHome(void) {
//not getting home from the env enable us to use sudo //not getting home from the env enable us to use sudo
struct passwd *pw = getpwuid(audit_getloginuid()); struct passwd *pw = getpwuid(audit_getloginuid());
return strdup(pw->pw_dir); return strdup(pw->pw_dir);
+1 -1
View File
@@ -5,4 +5,4 @@
* *
* @return char* path to the home dir * @return char* path to the home dir
*/ */
char * getHome(); char * getHome(void);
+20 -128
View File
@@ -1,144 +1,31 @@
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include <glib.h> #include <glib.h>
#include "install.h" #include "install.h"
#include "main.h" #include "main.h"
#include "archives.h"
#include "file.h" #include "file.h"
int unzip(char * path, char * outdir) { error_t install_addMod(char * filePath, int appId) {
char * const args[] = { error_t resultError = ERR_SUCCESS;
"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) {
printf("\nFailed to decompress archive\n");
returnValue = EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
}
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) {
printf("\nFailed to decompress archive\n");
returnValue = EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
}
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) {
printf("\nFailed to decompress archive\n");
returnValue = EXIT_FAILURE;
}
//make everything lowercase since 7z don't have an argument for that.
casefold(outdir);
return EXIT_SUCCESS;
}
}
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--) {
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, '/');
}
int addMod(char * filePath, int appId) {
int returnValue = EXIT_SUCCESS;
if (access(filePath, F_OK) != 0) { if (access(filePath, F_OK) != 0) {
printf("File not found\n"); fprintf(stderr, "File not found\n");
returnValue = EXIT_FAILURE; resultError = ERR_FAILURE;
goto exit; goto exit;
} }
char * configFolder = g_build_filename(getenv("HOME"), MANAGER_FILES, NULL); char * configFolder = g_build_filename(getenv("HOME"), MANAGER_FILES, NULL);
if(g_mkdir_with_parents(configFolder, 0755) < 0) { if(g_mkdir_with_parents(configFolder, 0755) < 0) {
printf("Could not create mod folder"); fprintf(stderr, "Could not create mod folder");
returnValue = EXIT_FAILURE; resultError = ERR_FAILURE;
goto exit2; goto exit2;
} }
const char * filename = extractFileName(filePath); const char * filename = file_extractFileName(filePath);
const char * extension = extractExtension(filename); const char * extension = file_extractExtension(filename);
char * lowercaseExtension = g_ascii_strdown(extension, -1); char * lowercaseExtension = g_ascii_strdown(extension, -1);
char appIdStr[20]; char appIdStr[20];
@@ -146,18 +33,23 @@ int addMod(char * filePath, int appId) {
char * outdir = g_build_filename(configFolder, MOD_FOLDER_NAME, appIdStr, filename, NULL); char * outdir = g_build_filename(configFolder, MOD_FOLDER_NAME, appIdStr, filename, NULL);
g_mkdir_with_parents(outdir, 0755); g_mkdir_with_parents(outdir, 0755);
int returnValue = EXIT_SUCCESS;
printf("Adding mod, this process can be slow depending on your hardware\n"); printf("Adding mod, this process can be slow depending on your hardware\n");
if(strcmp(lowercaseExtension, "rar") == 0) { if(strcmp(lowercaseExtension, "rar") == 0) {
returnValue = unrar(filePath, outdir); returnValue = archive_unrar(filePath, outdir);
} else if (strcmp(lowercaseExtension, "zip") == 0) { } else if (strcmp(lowercaseExtension, "zip") == 0) {
returnValue = unzip(filePath, outdir); returnValue = archive_unzip(filePath, outdir);
} else if (strcmp(lowercaseExtension, "7z") == 0) { } else if (strcmp(lowercaseExtension, "7z") == 0) {
returnValue = un7z(filePath, outdir); returnValue = archive_un7z(filePath, outdir);
} else { } 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; returnValue = EXIT_FAILURE;
} }
if(returnValue == EXIT_FAILURE)
resultError = ERR_FAILURE;
printf("Done\n"); printf("Done\n");
free(lowercaseExtension); free(lowercaseExtension);
@@ -165,5 +57,5 @@ int addMod(char * filePath, int appId) {
exit2: exit2:
free(configFolder); free(configFolder);
exit: exit:
return returnValue; return resultError;
} }
+2 -2
View File
@@ -1,6 +1,7 @@
#ifndef __INSTALL_H__ #ifndef __INSTALL_H__
#define __INSTALL_H__ #define __INSTALL_H__
#include <stdlib.h> #include <stdlib.h>
#include "main.h"
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__" #define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
/** /**
@@ -8,8 +9,7 @@
* *
* @param filePath path to the mod file * @param filePath path to the mod file
* @param appId game for which the mod is destined to be used with. * @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 install_addMod(char * filePath, int appId);
#endif #endif
+216
View File
@@ -0,0 +1,216 @@
#include "loadOrder.h"
#include "main.h"
#include "steam.h"
#include "file.h"
#include <dirent.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "getDataPath.h"
//TODO: detect if the game is running
//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;
char appidStr[appidStrLen];
sprintf(appidStr, "%d", appid);
char * dataFolder = NULL;
error_t error = getDataPath(appid, &dataFolder);
if(error != ERR_SUCCESS) {
return ERR_FAILURE;
}
//esp && esm files are loadable
DIR * d = opendir(dataFolder);
struct dirent *dir;
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;
error_t status = steam_searchGames(&gamePaths);
if(status == ERR_FAILURE) {
return ERR_FAILURE;
}
GList * l_plugins = NULL;
error_t error = order_listPlugins(appid, &l_plugins);
if(error == ERR_FAILURE)
return ERR_FAILURE;
int gameId = steam_gameIdFromAppId(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, &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, R_OK) == 0) {
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;
removeCRLF_CR_LF(line);
l_currentLoadOrder = g_list_append(l_currentLoadOrder, strdup(line));
}
if(line != NULL)free(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 order_setLoadOrder(int appid, GList * loadOrder) {
GHashTable * gamePaths;
error_t status = steam_searchGames(&gamePaths);
if(status == ERR_FAILURE) {
return ERR_FAILURE;
}
int gameId = steam_gameIdFromAppId(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, &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");
while(loadOrder != NULL) {
fwrite(loadOrder->data, sizeof(char), strlen(loadOrder->data), f_loadOrder);
loadOrder = g_list_next(loadOrder);
}
fclose(f_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 order_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;
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef __LOAD_ORDER_H__
#define __LOAD_ORDER_H__
#include <glib.h>
#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 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 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 order_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 order_getModDependencies(const char * esmPath, GList ** dependencies) __attribute__((warn_unused_result));
#endif
+236 -146
View File
@@ -1,6 +1,5 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <glib.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
@@ -9,6 +8,8 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <libaudit.h> #include <libaudit.h>
#include "getDataPath.h"
#include "loadOrder.h"
#include "overlayfs.h" #include "overlayfs.h"
#include "install.h" #include "install.h"
#include "getHome.h" #include "getHome.h"
@@ -21,12 +22,14 @@
#include "gui/guiMain.h" #include "gui/guiMain.h"
GHashTable * gamePaths; GHashTable * gamePaths;
#define ENABLED_COLOR "\033[0;32m"
#define DISABLED_COLOR "\033[0;31m"
bool isRoot() { static bool isRoot() {
return getuid() == 0; return getuid() == 0;
} }
GList * listFilesInFolder(const char * path) { static GList * listFilesInFolder(const char * path) {
GList * list = NULL; GList * list = NULL;
DIR *d; DIR *d;
struct dirent *dir; struct dirent *dir;
@@ -44,45 +47,52 @@ GList * listFilesInFolder(const char * path) {
return list; return list;
} }
int noRoot() { static int usage() {
printf("Don't run this argument as root\n");
return EXIT_FAILURE;
}
int needRoot() {
printf("Root is needed to bind with the game files\n");
return EXIT_FAILURE;
}
int usage() {
printf("Use --list-games or -l to list compatible games\n"); printf("Use --list-games or -l to list compatible games\n");
printf("Use --bind or -d <APPID> to deploy the mods for the game\n");
printf("Use --unbind <APPID> to rollback a deployment\n");
printf("Use --add or -a <APPID> <FILENAME> to add a mod to a game\n"); printf("Use --add or -a <APPID> <FILENAME> to add a mod to a game\n");
printf("Use --remove or -r <APPID> <MODID> to remove a mod\n");
printf("Use --fomod <APPID> <MODID> to create a new mod using the result from the FOMod\n");
printf("Use --list-mods or -m <APPID> to list all mods for a game\n"); printf("Use --list-mods or -m <APPID> to list all mods for a game\n");
printf("Use --install or -i <APPID> <MODID> to add a mod to a game\n"); printf("Use --install or -i <APPID> <MODID> to add a mod to a game\n");
printf("Use --uninstall or -u <APPID> <MODID> to uninstall a mod from a game\n"); printf("Use --uninstall or -u <APPID> <MODID> to uninstall a mod from a game\n");
printf("Use --remove or -r <APPID> <MODID> to remove a mod\n");
printf("Use --deploy or -d <APPID> to deploy the mods for the game\n"); printf("Use --swap <APPID> <MODID A> MODID B> to swap two mod in the loading order\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"); printf("Use --version or -v to get the version number\n");
printf("Use --show-load-order <APPID>\n");
printf("Use --list-plugins <APPID>\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int validateAppId(const char * appIdStr) { static error_t validateAppId(const char * appIdStr) {
GHashTable * gamePaths;
error_t status = steam_searchGames(&gamePaths);
if(status == ERR_FAILURE) {
return ERR_FAILURE;
}
char * strtoulSentinel;
//strtoul set EINVAL(after C99) if the string is invalid //strtoul set EINVAL(after C99) if the string is invalid
unsigned long appid = strtoul(appIdStr, NULL, 10); unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10);
if(errno == EINVAL) { 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; return -1;
} }
int gameId = getGameIdFromAppId(appid); int gameId = steam_gameIdFromAppId((int)appid);
if(gameId < 0) { if(gameId < 0) {
printf("Game is not compatible\n"); fprintf(stderr, "Game is not compatible\n");
return -1; return -1;
} }
if(!g_hash_table_contains(gamePaths, &gameId)) { if(!g_hash_table_contains(gamePaths, &gameId)) {
printf("Game not found\n"); fprintf(stderr, "Game not found\n");
return -1; return -1;
} }
@@ -90,8 +100,15 @@ int validateAppId(const char * appIdStr) {
return (int)appid; return (int)appid;
} }
int listGames(int argc, char ** argv) { static int listGames(int argc, char **) {
if(argc != 2) return usage(); if(argc != 2) return usage();
GHashTable * gamePaths;
error_t status = steam_searchGames(&gamePaths);
if(status == ERR_FAILURE) {
return EXIT_FAILURE;
}
GList * gamesIds = g_hash_table_get_keys(gamePaths); GList * gamesIds = g_hash_table_get_keys(gamePaths);
GList * gamesIdsFirstPointer = gamesIds; GList * gamesIdsFirstPointer = gamesIds;
if(g_list_length(gamesIds) == 0) { if(g_list_length(gamesIds) == 0) {
@@ -104,10 +121,10 @@ int listGames(int argc, char ** argv) {
} }
} }
g_list_free(gamesIdsFirstPointer); g_list_free(gamesIdsFirstPointer);
return EXIT_SUCCESS; return EXIT_FAILURE;
} }
int add(int argc, char ** argv) { static int add(int argc, char ** argv) {
if(argc != 4) return usage(); if(argc != 4) return usage();
const char * appIdStr = argv[2]; const char * appIdStr = argv[2];
@@ -116,11 +133,11 @@ int add(int argc, char ** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
addMod(argv[3], appid); install_addMod(argv[3], appid);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int listAllMods(int argc, char ** argv) { static int listAllMods(int argc, char ** argv) {
if(argc != 3) return usage(); if(argc != 3) return usage();
char * appIdStr = argv[2]; char * appIdStr = argv[2];
int appid = validateAppId(appIdStr); int appid = validateAppId(appIdStr);
@@ -133,7 +150,8 @@ int listAllMods(int argc, char ** argv) {
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL); char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
free(home); free(home);
GList * mods = listMods(appid); GList * mods = order_listMods(appid);
GList * p_mods = mods;
unsigned short index = 0; unsigned short index = 0;
printf("Id | Installed | Name\n"); printf("Id | Installed | Name\n");
@@ -142,18 +160,20 @@ int listAllMods(int argc, char ** argv) {
char * modPath = g_build_filename(modFolder, modName, INSTALLED_FLAG_FILE, NULL); char * modPath = g_build_filename(modFolder, modName, INSTALLED_FLAG_FILE, NULL);
if(access(modPath, F_OK) == 0) { if(access(modPath, F_OK) == 0) {
printf("\033[0;32m %d | ✓ | %s\n", index, modName); printf(ENABLED_COLOR " %d | ✓ | %s\n", index, modName);
} else { } else {
printf("\033[0;31m %d | ✕ | %s\n", index, modName); printf(DISABLED_COLOR " %d | ✕ | %s\n", index, modName);
} }
g_free(modPath);
index++; index++;
mods = g_list_next(mods); mods = g_list_next(mods);
} }
free(modFolder); free(modFolder);
free(mods); g_list_free_full(p_mods, free);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@@ -163,7 +183,7 @@ int listAllMods(int argc, char ** argv) {
* this will just flag the mod as needed to be deployed * this will just flag the mod as needed to be deployed
* it's the deploying process that handle the rest * 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(); if(argc != 4) return usage();
char * appIdStr = argv[2]; char * appIdStr = argv[2];
int appid = validateAppId(appIdStr); int appid = validateAppId(appIdStr);
@@ -176,7 +196,7 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
//strtoul set EINVAL if the string is invalid //strtoul set EINVAL if the string is invalid
unsigned long modId = strtoul(argv[3], NULL, 10); unsigned long modId = strtoul(argv[3], NULL, 10);
if(errno == EINVAL) { if(errno == EINVAL) {
printf("ModId has to be a valid number\n"); fprintf(stderr, "ModId has to be a valid number\n");
return -1; return -1;
} }
@@ -187,12 +207,12 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
GList * mods = listFilesInFolder(modFolder); GList * mods = listFilesInFolder(modFolder);
GList * modsFirstPointer = mods; GList * modsFirstPointer = mods;
for(int i = 0; i < modId; i++) { for(unsigned long i = 0; i < modId; i++) {
mods = g_list_next(mods); mods = g_list_next(mods);
} }
if(mods == NULL) { if(mods == NULL) {
printf("Mod not found\n"); fprintf(stderr, "Mod not found\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -201,7 +221,7 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
if(install) { if(install) {
if(access(modFlag, F_OK) == 0) { if(access(modFlag, F_OK) == 0) {
printf("The mod is already activated \n"); fprintf(stderr, "The mod is already activated \n");
returnValue = EXIT_SUCCESS; returnValue = EXIT_SUCCESS;
goto exit; goto exit;
} }
@@ -223,8 +243,8 @@ int installAndUninstallMod(int argc, char ** argv, bool install) {
} }
if(unlink(modFlag) != 0) { if(unlink(modFlag) != 0) {
printf("Error could not disable the mod.\n"); fprintf(stderr, "Error could not disable the mod.\n");
printf("please remove this file '%s' as root\n", modFlag); fprintf(stderr, "please remove this file '%s'\n", modFlag);
returnValue = EXIT_FAILURE; returnValue = EXIT_FAILURE;
goto exit; goto exit;
} }
@@ -238,7 +258,7 @@ exit:
return returnValue; return returnValue;
} }
int deploy(int argc, char ** argv) { static int deploy(int argc, char ** argv) {
if(argc != 3) return usage(); if(argc != 3) return usage();
char * appIdStr = argv[2]; char * appIdStr = argv[2];
@@ -251,7 +271,10 @@ int deploy(int argc, char ** argv) {
char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL); char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL);
if(access(gameFolder, F_OK) != 0) { if(access(gameFolder, F_OK) != 0) {
free(home);
g_free(gameFolder);
printf("Please run '%s --setup %d' first", APP_NAME, appid); printf("Please run '%s --setup %d' first", APP_NAME, appid);
return EXIT_FAILURE;
} }
//unmount everything beforhand //unmount everything beforhand
@@ -297,9 +320,13 @@ int deploy(int argc, char ** argv) {
modsToInstall[modCount] = gameFolder; modsToInstall[modCount] = gameFolder;
modsToInstall[modCount + 1] = NULL; modsToInstall[modCount + 1] = NULL;
printf("Mounting the overlay\n"); printf("Mounting the overlay\n");
int gameId = getGameIdFromAppId(appid);
const char * path = g_hash_table_lookup(gamePaths, &gameId); char * dataFolder = NULL;
char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); error_t error = getDataPath(appid, &dataFolder);
if(error != ERR_SUCCESS) {
free(home);
return ERR_FAILURE;
}
char * gameUpperDir = g_build_filename(home, MANAGER_FILES, GAME_UPPER_DIR_NAME, appIdStr, 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); char * gameWorkDir = g_build_filename(home, MANAGER_FILES, GAME_WORK_DIR_NAME, appIdStr, NULL);
@@ -309,36 +336,35 @@ int deploy(int argc, char ** argv) {
//DETACH + FORCE allow us to be sure it will be unload. //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 //it might crash / corrupt game file if the user do it while the game is running
//but it's still very unlikely //but it's still very unlikely
while(umount2(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0); while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0);
int status = overlayMount(modsToInstall, steamGameFolder, gameUpperDir, gameWorkDir); overlay_errors_t status = overlay_mount(modsToInstall, dataFolder, gameUpperDir, gameWorkDir);
if(status == 0) { if(status == SUCESS) {
printf("Everything is ready, just launch the game\n"); printf("Everything is ready, just launch the game\n");
} else if(status == -1) { } else if(status == FAILURE) {
printf("Could not mount the mods overlay, try to install fuse-overlay\n"); fprintf(stderr, "Could not mount the mods overlay\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} else if(status == -2) { } else if(status == NOT_INSTALLED) {
printf("Could not mount the mods overlay\n"); fprintf(stderr, "Please install fuse-overlayfs\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} else { } 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; return EXIT_FAILURE;
} }
g_free(steamGameFolder); g_free(dataFolder);
g_free(gameUpperDir); g_free(gameUpperDir);
g_free(gameWorkDir); g_free(gameWorkDir);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int setup(int argc, char ** argv) { static int setup(int argc, char ** argv) {
if(argc != 3 ) return usage(); if(argc != 3 ) return usage();
const char * appIdStr = argv[2]; const char * appIdStr = argv[2];
int appid = validateAppId(appIdStr); int appid = validateAppId(appIdStr);
if(appid < 0) { if(appid < 0) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int gameId = getGameIdFromAppId(appid);
char * home = getHome(); char * home = getHome();
char * gameUpperDir = g_build_filename(home, MANAGER_FILES, GAME_UPPER_DIR_NAME, appIdStr, NULL); char * gameUpperDir = g_build_filename(home, MANAGER_FILES, GAME_UPPER_DIR_NAME, appIdStr, NULL);
@@ -348,69 +374,75 @@ int setup(int argc, char ** argv) {
free(gameUpperDir); free(gameUpperDir);
free(gameWorkDir); free(gameWorkDir);
const char * path = g_hash_table_lookup(gamePaths, &gameId); char * dataFolder = NULL;
char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL); error_t error = getDataPath(appid, &dataFolder);
if(error != ERR_SUCCESS) {
return ERR_FAILURE;
}
char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL); char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL);
if(access(gameFolder, F_OK) == 0) { if(access(gameFolder, F_OK) == 0) {
//if the game folder alredy exists just delete it //if the game folder alredy exists just delete it
//this will allow the removal of dlcs and language change //this will allow the removal of dlcs and language change
delete(gameFolder, true); file_delete(gameFolder, true);
} }
g_mkdir_with_parents(gameFolder, 0755); g_mkdir_with_parents(gameFolder, 0755);
//links don't conflict with overlayfs and avoid coping 17Gb of files. //links don't conflict with overlayfs and avoid coping 17Gb of files.
//but links require the files to be on the same filesystem //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 = file_copy(dataFolder, gameFolder, FILE_CP_RECURSIVE | FILE_CP_NO_TARGET_DIR | FILE_CP_LINK);
if(returnValue < 0) { 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"); 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 = file_copy(dataFolder, gameFolder, FILE_CP_RECURSIVE | FILE_CP_NO_TARGET_DIR);
if(returnValue < 0) { if(returnValue < 0) {
fprintf(stderr, "Copy failed make sure you have enough space on your device."); fprintf(stderr, "Copy failed make sure you have enough space on your device.");
free(steamGameFolder); free(dataFolder);
free(gameFolder); free(gameFolder);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
casefold(gameFolder); file_casefold(gameFolder);
free(steamGameFolder); free(dataFolder);
free(gameFolder); free(gameFolder);
printf("Done\n"); printf("Done\n");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int unbind(int argc, char ** argv) { static int unbind(int argc, char ** argv) {
if(argc != 3 ) return usage(); if(argc != 3 ) return usage();
const char * appIdStr = argv[2]; const char * appIdStr = argv[2];
int appid = validateAppId(appIdStr); int appid = validateAppId(appIdStr);
if(appid < 0) { if(appid < 0) {
return EXIT_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);
while(umount2(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0); char * dataFolder = NULL;
error_t error = getDataPath(appid, &dataFolder);
if(error != ERR_SUCCESS) {
return ERR_FAILURE;
}
free(steamGameFolder); while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0);
free(dataFolder);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int removeMod(int argc, char ** argv) { static int removeMod(int argc, char ** argv) {
if(argc != 4) return usage(); if(argc != 4) return usage();
const char * appIdStr = argv[2]; const char * appIdStr = argv[2];
int appid = validateAppId(appIdStr); int appid = validateAppId(appIdStr);
if(appid < 0) { if(appid < 0) {
printf("Invalid appid"); fprintf(stderr, "Invalid appid");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
//strtoul set EINVAL if the string is invalid //strtoul set EINVAL if the string is invalid
unsigned long modId = strtoul(argv[3], NULL, 10); unsigned long modId = strtoul(argv[3], NULL, 10);
if(errno == EINVAL) { 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; return EXIT_FAILURE;
} }
@@ -421,18 +453,18 @@ int removeMod(int argc, char ** argv) {
GList * mods = listFilesInFolder(modFolder); GList * mods = listFilesInFolder(modFolder);
GList * modsFirstPointer = mods; GList * modsFirstPointer = mods;
for(int i = 0; i < modId; i++) { for(unsigned long i = 0; i < modId; i++) {
mods = g_list_next(mods); mods = g_list_next(mods);
} }
if(mods == NULL) { if(mods == NULL) {
printf("Mod not found\n"); fprintf(stderr, "Mod not found\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
gchar * filename = g_build_filename(modFolder, mods->data, NULL); gchar * filename = g_build_filename(modFolder, mods->data, NULL);
delete(filename, true); file_delete(filename, true);
g_free(filename); g_free(filename);
g_free(modFolder); g_free(modFolder);
@@ -440,19 +472,19 @@ int removeMod(int argc, char ** argv) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int fomod(int argc, char ** argv) { static int fomod(int argc, char ** argv) {
if(argc != 4) return usage(); if(argc != 4) return usage();
char * appIdStr = argv[2]; char * appIdStr = argv[2];
int appid = validateAppId(appIdStr); int appid = validateAppId(appIdStr);
if(appid < 0) { if(appid < 0) {
printf("Invalid appid"); fprintf(stderr, "Invalid appid");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
//strtoul set EINVAL if the string is invalid //strtoul set EINVAL if the string is invalid
unsigned long modId = strtoul(argv[3], NULL, 10); unsigned long modId = strtoul(argv[3], NULL, 10);
if(errno == EINVAL) { if(errno == EINVAL) {
printf("Modid has to be a valid number\n"); fprintf(stderr, "Modid has to be a valid number\n");
return -1; return -1;
} }
@@ -463,25 +495,25 @@ int fomod(int argc, char ** argv) {
GList * mods = listFilesInFolder(modFolder); GList * mods = listFilesInFolder(modFolder);
GList * modsFirstPointer = mods; GList * modsFirstPointer = mods;
for(int i = 0; i < modId; i++) { for(unsigned long i = 0; i < modId; i++) {
mods = g_list_next(mods); mods = g_list_next(mods);
} }
if(mods == NULL) { if(mods == NULL) {
printf("Mod not found\n"); fprintf(stderr, "Mod not found\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
char * destination = g_strconcat(mods->data, "__FOMOD", NULL); char * destination = g_strconcat(mods->data, "__FOMOD", NULL);
if(access(destination, F_OK) == 0) { if(access(destination, F_OK) == 0) {
delete(destination, true); file_delete(destination, true);
} }
char * modDestination = g_build_filename(modFolder, destination, NULL); char * modDestination = g_build_filename(modFolder, destination, NULL);
char * modPath = g_build_filename(modFolder, mods->data, NULL); char * modPath = g_build_filename(modFolder, mods->data, NULL);
//TODO: add error handling //TODO: add error handling
int returnValue = installFOMod(modPath, modDestination); int returnValue = fomod_installFOMod(modPath, modDestination);
free(destination); free(destination);
g_list_free_full(modsFirstPointer, free); g_list_free_full(modsFirstPointer, free);
@@ -491,24 +523,98 @@ int fomod(int argc, char ** argv) {
return returnValue; return returnValue;
} }
int main(int argc, char ** argv) { static int swapMod(int argc, char ** argv) {
if(argc < 2 ) return usage(); if(argc != 5) return usage();
char * appIdStr = argv[2];
if(audit_getloginuid() == 0) { int appid = validateAppId(appIdStr);
printf("Root user should not be using this\n"); if(appid < 0) {
fprintf(stderr, "Invalid appid");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int searchStatus;
gamePaths = search_games(&searchStatus);
int returnValue = EXIT_SUCCESS; char * sentinel;
if(searchStatus == EXIT_FAILURE) { //no mod will go over the MAX_INT value
returnValue = EXIT_FAILURE; int modIdA = (int)strtoul(argv[3], &sentinel, 10);
printf("Error while looking up libraries, do you have steam installed ?"); if(errno == EINVAL || sentinel == argv[3]) {
goto exit; 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]) {
fprintf(stderr, "Modid B has to be a valid number\n");
return -1;
} }
printf("%d, %d\n", modIdA, modIdB);
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();
if(audit_getloginuid() == 0 || isRoot()) {
fprintf(stderr, "Please don't run this software as root\n");
return EXIT_FAILURE;
}
int returnValue = EXIT_SUCCESS;
char * home = getHome(); char * home = getHome();
char * configFolder = g_build_filename(home, MANAGER_FILES, NULL); char * configFolder = g_build_filename(home, MANAGER_FILES, NULL);
free(home); free(home);
@@ -519,92 +625,76 @@ int main(int argc, char ** argv) {
//such as problem with access rights and taking the risk of //such as problem with access rights and taking the risk of
//running system as root (which is a big security issue) //running system as root (which is a big security issue)
if(getuid() == 0) { 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; returnValue = EXIT_FAILURE;
goto exit2; goto exit;
} else { } else {
//leading 0 == octal //leading 0 == octal
int i = g_mkdir_with_parents(configFolder, 0755); int i = g_mkdir_with_parents(configFolder, 0755);
if(i < 0) { 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 exit;
} }
} }
} }
if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0)
if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0) {
if(isRoot())
returnValue = noRoot();
else
returnValue = listGames(argc, argv); returnValue = listGames(argc, argv);
} else if(strcmp(argv[1], "--add") == 0 || strcmp(argv[1], "-a") == 0) { else if(strcmp(argv[1], "--add") == 0 || strcmp(argv[1], "-a") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = add(argc, argv); returnValue = add(argc, argv);
} else if(strcmp(argv[1], "--list-mods") == 0 || strcmp(argv[1], "-m") == 0) { else if(strcmp(argv[1], "--list-mods") == 0 || strcmp(argv[1], "-m") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = listAllMods(argc, argv); returnValue = listAllMods(argc, argv);
} else if(strcmp(argv[1], "--install") == 0 || strcmp(argv[1], "-i") == 0) { else if(strcmp(argv[1], "--install") == 0 || strcmp(argv[1], "-i") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = installAndUninstallMod(argc, argv, true); returnValue = installAndUninstallMod(argc, argv, true);
} else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0) { else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = installAndUninstallMod(argc, argv, false); 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)
if(!isRoot())
returnValue = needRoot();
else
returnValue = deploy(argc, argv); returnValue = deploy(argc, argv);
} else if(strcmp(argv[1], "--unbind") == 0){ else if(strcmp(argv[1], "--unbind") == 0)
if(!isRoot())
returnValue = needRoot();
else
returnValue = unbind(argc, argv); returnValue = unbind(argc, argv);
} else if(strcmp(argv[1], "--setup") == 0) { else if(strcmp(argv[1], "--setup") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = setup(argc, argv); returnValue = setup(argc, argv);
} else if(strcmp(argv[1], "--fomod") == 0){
if(isRoot()) else if(strcmp(argv[1], "--fomod") == 0)
returnValue = noRoot();
else
returnValue = fomod(argc, argv); returnValue = fomod(argc, argv);
} else if(strcmp(argv[1], "--remove") == 0 || strcmp(argv[1], "-r") == 0) { else if(strcmp(argv[1], "--remove") == 0 || strcmp(argv[1], "-r") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = removeMod(argc, argv); returnValue = removeMod(argc, argv);
} else if(strcmp(argv[1], "--gui") == 0) { else if(strcmp(argv[1], "--gui") == 0)
if(isRoot())
returnValue = noRoot();
else
returnValue = guiMain(argc, argv); returnValue = guiMain(argc, 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__);
#elifdef __GNUC__
printf("%s: GCC: %d.%d.%d\n", VERSION, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#else
printf("%s: unknown compiler\n", VERSION);
#endif
} else { } else {
usage(); usage();
returnValue = EXIT_FAILURE; returnValue = EXIT_FAILURE;
} }
exit2:
g_free(configFolder);
g_hash_table_destroy(gamePaths);
exit: exit:
g_free(configFolder);
steam_freeGameTable();
return returnValue; return returnValue;
} }
+13 -2
View File
@@ -1,6 +1,11 @@
//no function should ever be put here, only keep important macros here #ifndef __MAIN_H__
#define __MAIN_H__
#define APP_NAME "modmanager" #include <glib.h>
//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 //relative to home the url is not preprocessed so ../ might create some issues
// in c "A" "B" is the same as "AB" // in c "A" "B" is the same as "AB"
#define MANAGER_FILES ".local/share/" APP_NAME #define MANAGER_FILES ".local/share/" APP_NAME
@@ -11,3 +16,9 @@
#define GAME_UPPER_DIR_NAME "UPPER_DIRS" #define GAME_UPPER_DIR_NAME "UPPER_DIRS"
//overlayfs temporary dir. //overlayfs temporary dir.
#define GAME_WORK_DIR_NAME "WORK_DIRS" #define GAME_WORK_DIR_NAME "WORK_DIRS"
#define VERSION "0.1"
typedef enum { ERR_SUCCESS, ERR_FAILURE } error_t;
#endif
+50 -5
View File
@@ -1,4 +1,5 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include "order.h" #include "order.h"
//no function are declared in main it's just macros //no function are declared in main it's just macros
@@ -11,7 +12,7 @@ typedef struct Mod {
char * name; char * name;
} Mod_t; } 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 * ModA = a;
const Mod_t * ModB = b; const Mod_t * ModB = b;
@@ -21,7 +22,7 @@ gint compareOrder(const void * a, const void * b) {
return ModA->modId - ModB->modId; return ModA->modId - ModB->modId;
} }
GList * listMods(int appid) { GList * order_listMods(int appid) {
char appidStr[10]; char appidStr[10];
sprintf(appidStr, "%d", appid); sprintf(appidStr, "%d", appid);
@@ -50,6 +51,7 @@ GList * listMods(int appid) {
Mod_t * mod = alloca(sizeof(Mod_t)); Mod_t * mod = alloca(sizeof(Mod_t));
mod->modId = modId; mod->modId = modId;
//we are going to return this value so no alloca here
mod->name = strdup(dir->d_name); mod->name = strdup(dir->d_name);
//strdup but on the stack //strdup but on the stack
//add one for the \0 //add one for the \0
@@ -92,9 +94,52 @@ GList * listMods(int appid) {
} }
void swapPlace(int appid, int modId, int modId2) { error_t order_swapPlace(int appid, int modIdA, int modIdB) {
GList * list = listMods(appid); 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);
g_list_free(list); GList * list = order_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) {
fprintf(stderr, "Invalid modId\n");
return ERR_FAILURE;
}
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");
g_free(modAFolder);
g_free(modBFolder);
if(fileA == NULL || fileB == NULL) {
fprintf(stderr, "Error could not open order file\n");
return ERR_FAILURE;
}
fprintf(fileA, "%d", modIdB);
fprintf(fileB, "%d", modIdA);
fclose(fileA);
fclose(fileB);
g_list_free_full(list, free);
return ERR_SUCCESS;
} }
+3 -2
View File
@@ -2,6 +2,7 @@
#define __ORDER_H__ #define __ORDER_H__
#include <glib.h> #include <glib.h>
#include "main.h"
#define ORDER_FILE "__ORDER__" #define ORDER_FILE "__ORDER__"
@@ -14,7 +15,7 @@
* *
* @return GList of char containing the name of the mod folder in order * @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 * @brief Change the mod order by swaping two mod
@@ -24,6 +25,6 @@ GList * listMods(int appid);
* @param modId * @param modId
* @param modId2 * @param modId2
*/ */
void swapPlace(int appid, int modId, int modId2); error_t order_swapPlace(int appid, int modId, int modId2);
#endif #endif
+7 -17
View File
@@ -6,16 +6,13 @@
#include "overlayfs.h" #include "overlayfs.h"
#include <stdio.h> #include <stdio.h>
int 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 * lowerdir = g_strjoinv(":", sources);
char * mountData = g_strjoin("", "lowerdir=", lowerdir, ",workdir=", workdir, ",upperdir=", upperdir, NULL); 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) {
overlay_errors_t result = SUCESS;
if(access("/usr/bin/fuse-overlayfs", F_OK) == 0) {
int pid = fork(); int pid = fork();
if(pid == 0) { if(pid == 0) {
//exit is used to get the return value when using waitpid //exit is used to get the return value when using waitpid
@@ -24,21 +21,14 @@ int overlayMount(char ** sources, const char * dest, const char * upperdir, cons
int returnValue = 0; int returnValue = 0;
waitpid(pid, &returnValue, 0); waitpid(pid, &returnValue, 0);
free(lowerdir);
free(mountData);
if(returnValue != 0) { if(returnValue != 0) {
return -2; result = FAILURE;
} }
return 0;
} }
} else { } else {
free(lowerdir); result = NOT_INSTALLED;
free(mountData);
return -1;
}
} }
free(lowerdir); free(lowerdir);
free(mountData); free(mountData);
return 0; return result;
} }
+4 -1
View File
@@ -1,6 +1,9 @@
#ifndef __OVERLAY_H__ #ifndef __OVERLAY_H__
#define __OVERLAY_H__ #define __OVERLAY_H__
typedef enum overlay_errors { SUCESS, NOT_INSTALLED, FAILURE } overlay_errors_t;
/** /**
* @brief Overlayfs is what make it possible to deploy to the game files without altering anything. * @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. * 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. * @param workdir a directory that will contains only temporary files.
* @return int error code * @return int error code
*/ */
int 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 #endif
+47 -23
View File
@@ -1,6 +1,7 @@
#include "steam.h" #include "steam.h"
#include "macro.h" #include "macro.h"
#include "getHome.h" #include "getHome.h"
#include "main.h"
#include <unistd.h> #include <unistd.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
@@ -12,7 +13,16 @@
enum FieldIds { FIELD_PATH, FIELD_LABEL, FIELD_CONTENT_ID, FIELD_TOTAL_SIZE, FIELD_CLEAN_BYTES, FIELD_CORRUPTION, FIELD_APPS }; enum FieldIds { FIELD_PATH, FIELD_LABEL, FIELD_CONTENT_ID, FIELD_TOTAL_SIZE, FIELD_CLEAN_BYTES, FIELD_CORRUPTION, FIELD_APPS };
int getFiledId(const char * field) { // 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 //replace with a hash + switch
if(strcmp(field, "path") == 0) { if(strcmp(field, "path") == 0) {
return FIELD_PATH; return FIELD_PATH;
@@ -29,22 +39,21 @@ int getFiledId(const char * field) {
} else if(strcmp(field, "apps") == 0) { } else if(strcmp(field, "apps") == 0) {
return FIELD_APPS; return FIELD_APPS;
} else { } else {
printf("unknown field"); fprintf(stderr, "unknown field");
return -1; return -1;
} }
} }
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"); FILE * fd = fopen(path, "r");
char * line = NULL; char * line = NULL;
size_t len = 0; size_t len = 0;
ssize_t read;
*status = EXIT_SUCCESS; *status = EXIT_SUCCESS;
bool inQuotes = false; bool inQuotes = false;
ValveLibraries_t * libraries = NULL; steam_Libraries_t * libraries = NULL;
*size = 0; *size = 0;
//skip the "libraryfolders" label & the first opening brace //skip the "libraryfolders" label & the first opening brace
@@ -81,7 +90,7 @@ ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) {
} else { } else {
char * value = strndup(buffer, bufferIndex); char * value = strndup(buffer, bufferIndex);
ValveLibraries_t * library = &libraries[*size - 1]; steam_Libraries_t * library = &libraries[*size - 1];
switch (nextFieldToFill) { switch (nextFieldToFill) {
case FIELD_PATH: case FIELD_PATH:
library->path = value; library->path = value;
@@ -111,14 +120,17 @@ ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) {
case FIELD_APPS: case FIELD_APPS:
if(isAppId) { if(isAppId) {
library->appsCount++; 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); unsigned int appid = strtoul(value, NULL, 10);
library->apps[library->appsCount - 1].appid = appid; library->apps[library->appsCount - 1].appid = appid;
} else { } else {
library->apps[library->appsCount - 1].update = strtoul(value, NULL, 10); library->apps[library->appsCount - 1].update = strtoul(value, NULL, 10);
} }
free(value);
isAppId = !isAppId; isAppId = !isAppId;
break; break;
default:
free(value);
} }
//field apps is using braces so the syntax is different //field apps is using braces so the syntax is different
if(nextFieldToFill != FIELD_APPS)nextFieldToFill = -1; if(nextFieldToFill != FIELD_APPS)nextFieldToFill = -1;
@@ -134,13 +146,14 @@ ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) {
braceDepth++; braceDepth++;
if(braceDepth == 1) { if(braceDepth == 1) {
*size += 1; *size += 1;
libraries = realloc(libraries, sizeof(ValveLibraries_t) * (*size)); libraries = realloc(libraries, sizeof(steam_Libraries_t) * (*size));
memset(&libraries[*size - 1], 0, sizeof(ValveLibraries_t)); memset(&libraries[*size - 1], 0, sizeof(steam_Libraries_t));
} }
break; break;
case '}': case '}':
if(inQuotes) { if(inQuotes) {
printf("Syntax error in VDF\n"); fprintf(stderr, "Syntax error in VDF\n");
//TODO: fix this leak
free(libraries); free(libraries);
libraries = NULL; libraries = NULL;
*status = EXIT_FAILURE; *status = EXIT_FAILURE;
@@ -166,7 +179,7 @@ exit:
return libraries; return libraries;
} }
void freeLibraries(ValveLibraries_t * libraries, int size) { static void freeLibraries(steam_Libraries_t * libraries, int size) {
for(int i = 0; i < size; i++) { for(int i = 0; i < size; i++) {
free(libraries[i].path); free(libraries[i].path);
free(libraries[i].label); free(libraries[i].label);
@@ -178,13 +191,23 @@ void freeLibraries(ValveLibraries_t * libraries, int size) {
free(libraries); free(libraries);
} }
GHashTable* search_games(int * status) { static GHashTable* gameTableSingleton = NULL;
ValveLibraries_t * libraries = NULL;
void steam_freeGameTable() {
if(gameTableSingleton != NULL)g_hash_table_destroy(gameTableSingleton);
}
error_t steam_searchGames(GHashTable ** p_hashTable) {
if(gameTableSingleton != NULL) {
*p_hashTable = gameTableSingleton;
return ERR_SUCCESS;
}
steam_Libraries_t * libraries = NULL;
size_t size = 0; size_t size = 0;
char * home = getHome(); 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); char * path = g_build_filename(home, steamLibraries[i], "steamapps/libraryfolders.vdf", NULL);
if (access(path, F_OK) == 0) { if (access(path, F_OK) == 0) {
int parserStatus; int parserStatus;
@@ -200,16 +223,15 @@ GHashTable* search_games(int * status) {
free(home); free(home);
if(libraries == NULL) { if(libraries == NULL) {
*status = EXIT_FAILURE; return ERR_FAILURE;
return NULL;
} }
GHashTable* table = g_hash_table_new_full(g_int_hash, g_int_equal, free, free); GHashTable* table = g_hash_table_new_full(g_int_hash, g_int_equal, free, free);
//fill the table //fill the table
for(int i = 0; i < size; i++) { for(unsigned long i = 0; i < size; i++) {
for(int j = 0; j < libraries[i].appsCount; j++) { 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) { if(gameId >= 0) {
int * key = malloc(sizeof(int)); int * key = malloc(sizeof(int));
*key = gameId; *key = gameId;
@@ -219,12 +241,14 @@ GHashTable* search_games(int * status) {
} }
freeLibraries(libraries, size); freeLibraries(libraries, size);
return table; *p_hashTable = table;
gameTableSingleton = table;
return ERR_SUCCESS;
} }
int getGameIdFromAppId(u_int32_t appid) { int steam_gameIdFromAppId(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]) { if(appid == GAMES_APPIDS[k]) {
return k; return k;
} }
+20 -20
View File
@@ -1,62 +1,62 @@
#ifndef __STEAM_H__ #ifndef __STEAM_H__
#define __STEAM_H__ #define __STEAM_H__
#include "macro.h"
#include "main.h"
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <glib.h> #include <glib.h>
typedef struct ValveApp { typedef struct steam_App {
unsigned int appid; unsigned int appid;
unsigned int update; unsigned int update;
} ValveApp_t; } steam_App_t;
typedef struct ValveLibraries { typedef struct steam_Libraries {
char * path; char * path;
char * label; char * label;
char * contentId; char * contentId;
unsigned long totalSize; unsigned long totalSize;
char * update_clean_bytes_tally; char * update_clean_bytes_tally;
char * time_last_update_corruption; char * time_last_update_corruption;
ValveApp_t * apps; steam_App_t * apps;
size_t appsCount; size_t appsCount;
} ValveLibraries_t; } steam_Libraries_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 //todo add the older games
// order has to be the same as in GAMES_NAMES // 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, 489830,
22330, 22330,
377160, 377160,
72850, 72850,
22320
}; };
//the name of the game in the steamapps/common folder //the name of the game in the steamapps/common folder
const static char * GAMES_NAMES[] = { static const char * GAMES_NAMES[] = {
"Skyrim Special Edition", "Skyrim Special Edition",
"Oblivion", "Oblivion",
"Fallout 4", "Fallout 4",
"Skyrim", "Skyrim",
"Morrowind"
}; };
_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 * @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 * @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 gameId(int) => path(char *) to the corresponding steam library
*/ */
//TODO: flip the return value and parameter error_t steam_searchGames(GHashTable** tablePointer);
GHashTable* search_games(int * status);
void steam_freeGameTable(void);
/** /**
* @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS * @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS
@@ -64,6 +64,6 @@ GHashTable* search_games(int * status);
* @param appid * @param appid
* @return -1 in case of failure or the index of the game. * @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 #endif