Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b22c49762 | |||
| b6996bf73b | |||
| c9f7e15e05 | |||
| 8e521eba6e |
+27
-57
@@ -9,8 +9,8 @@ if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wmaybe-uninitialized")
|
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wmaybe-uninitialized")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "-g -Werror -O0")
|
set(CMAKE_C_FLAGS_DEBUG "-g -fsanitize=address -Werror -O0")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "")
|
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 "")
|
||||||
@@ -19,73 +19,43 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
|||||||
project(mod-manager)
|
project(mod-manager)
|
||||||
|
|
||||||
|
|
||||||
set(LIB_SOURCES
|
set(SOURCES
|
||||||
src/lib/steam.c
|
src/steam.c
|
||||||
src/lib/loadOrder.c
|
src/loadOrder.c
|
||||||
src/lib/install.c
|
src/install.c
|
||||||
src/lib/overlayfs.c
|
src/overlayfs.c
|
||||||
src/lib/gameData.c
|
src/getDataPath.c
|
||||||
src/lib/getHome.c
|
src/getHome.c
|
||||||
src/lib/file.c
|
src/file.c
|
||||||
src/lib/order.c
|
src/order.c
|
||||||
src/lib/archives.c
|
src/archives.c
|
||||||
src/lib/fomod.c
|
src/fomod.c
|
||||||
src/lib/deploy.c
|
src/fomod/group.c
|
||||||
src/lib/fomod/group.c
|
src/fomod/xmlUtil.c
|
||||||
src/lib/fomod/xmlUtil.c
|
src/fomod/parser.c
|
||||||
src/lib/fomod/parser.c
|
src/gui/guiMain.c
|
||||||
)
|
|
||||||
|
|
||||||
# Headers files to export in the include folder
|
|
||||||
set(LIB_PUBLIC_HEADERS
|
|
||||||
src/include/constants.h
|
|
||||||
src/include/deploy.h
|
|
||||||
src/include/errorType.h
|
|
||||||
src/include/file.h
|
|
||||||
src/include/fomod.h
|
|
||||||
src/include/gameData.h
|
|
||||||
src/include/getHome.h
|
|
||||||
src/include/install.h
|
|
||||||
src/include/loadOrder.h
|
|
||||||
src/include/order.h
|
|
||||||
src/include/steam.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(CLI_SOURCES
|
|
||||||
src/cli/cli.c
|
|
||||||
src/cli/fomod.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# add the executable
|
# add the executable
|
||||||
add_executable(mod-manager ${CLI_SOURCES})
|
add_executable(mod-manager src/main.c ${SOURCES})
|
||||||
add_library(libmod-manager SHARED ${LIB_SOURCES})
|
|
||||||
set_target_properties(libmod-manager PROPERTIES PUBLIC_HEADER ${LIB_PUBLIC_HEADERS})
|
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_search_module(GLIB REQUIRED glib-2.0)
|
pkg_search_module(GLIB REQUIRED glib-2.0)
|
||||||
pkg_search_module(AUDIT REQUIRED audit)
|
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)
|
||||||
|
|
||||||
target_include_directories(libmod-manager PUBLIC src/include ${GLIB_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${AUDIT_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(libmod-manager ${GLIB_LDFLAGS})
|
|
||||||
target_link_libraries(libmod-manager ${LIBXML_LIBRARIES})
|
|
||||||
target_link_libraries(libmod-manager ${AUDIT_LIBRARIES})
|
|
||||||
|
|
||||||
target_link_libraries(mod-manager libmod-manager)
|
|
||||||
target_include_directories(mod-manager PUBLIC src/include)
|
|
||||||
target_link_libraries(mod-manager ${GLIB_LDFLAGS})
|
|
||||||
target_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS})
|
target_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||||
|
target_include_directories(mod-manager PRIVATE ${LIBXML_INCLUDE_DIRS})
|
||||||
|
target_include_directories(mod-manager PRIVATE ${AUDIT_INCLUDE_DIRS})
|
||||||
|
target_include_directories(mod-manager PRIVATE ${GTK_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|
||||||
install(TARGETS mod-manager OPTIONAL DESTINATION bin)
|
target_link_libraries(mod-manager ${GLIB_LDFLAGS})
|
||||||
|
target_link_libraries(mod-manager ${LIBXML_LIBRARIES})
|
||||||
|
target_link_libraries(mod-manager ${AUDIT_LIBRARIES})
|
||||||
|
target_link_libraries(mod-manager ${GTK_LIBRARIES})
|
||||||
|
|
||||||
install(TARGETS libmod-manager
|
install(TARGETS mod-manager DESTINATION bin)
|
||||||
LIBRARY DESTINATION lib
|
|
||||||
PUBLIC_HEADER DESTINATION include/mod-manager
|
|
||||||
)
|
|
||||||
|
|
||||||
set_property(TARGET mod-manager PROPERTY C_STANDARD 23)
|
set_property(TARGET mod-manager PROPERTY C_STANDARD 23)
|
||||||
set_property(TARGET libmod-manager PROPERTY C_STANDARD 23)
|
|
||||||
|
|
||||||
#avoid having liblibmod-manager
|
|
||||||
set_target_properties(libmod-manager PROPERTIES PREFIX "")
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
* Add tests
|
|
||||||
* Add safe interrupt support
|
|
||||||
* Add a gui in an external binary
|
|
||||||
* make the CLI into a separate binary
|
|
||||||
@@ -6,7 +6,7 @@ pkgdesc="a Mod manager for bethesda games"
|
|||||||
arch=('x86_64') # might work on other archs but i can not try
|
arch=('x86_64') # might work on other archs but i can not try
|
||||||
url='https://gitlab.marcbarbier.fr/Marc/modmanager'
|
url='https://gitlab.marcbarbier.fr/Marc/modmanager'
|
||||||
license=('GPL2')
|
license=('GPL2')
|
||||||
source=( 'git+https://github.com/Marc-Pierre-Barbier/ModManager.git' )
|
source=( 'git+https://gitlab.marcbarbier.fr/Marc/modmanager.git' )
|
||||||
md5sums=( 'SKIP' )
|
md5sums=( 'SKIP' )
|
||||||
|
|
||||||
optdepends=('fuse-overlayfs: special filesystem support' )
|
optdepends=('fuse-overlayfs: special filesystem support' )
|
||||||
|
|||||||
@@ -2,18 +2,17 @@
|
|||||||
|
|
||||||
## Function names
|
## Function names
|
||||||
|
|
||||||
### header functions
|
### 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
|
function should start by the name of their module in camelCase followed by an underscore and the name of the function in camelCase
|
||||||
|
|
||||||
example:
|
example:
|
||||||
steam_searchGames()
|
steam_searchGames()
|
||||||
|
|
||||||
if the function has the same name as the module then it doesn't need to be repeated.
|
|
||||||
|
|
||||||
### static functions
|
### static functions
|
||||||
|
|
||||||
all static functions should be in camelCase and they should not mention their module.
|
there is no rule for now
|
||||||
|
|
||||||
## Struct names
|
## Struct names
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#define APP_NAME "mod-manager CLI"
|
|
||||||
#define VERSION "0.1"
|
|
||||||
-204
@@ -1,204 +0,0 @@
|
|||||||
#include "fomod.h"
|
|
||||||
#include <fomod.h>
|
|
||||||
#include <constants.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
//match the definirion of gcompare func
|
|
||||||
static gint fomod_flagEqual(const fomod_Flag_t * a, const fomod_Flag_t * b) {
|
|
||||||
int nameCmp = strcmp(a->name, b->name);
|
|
||||||
if(nameCmp == 0) {
|
|
||||||
if(strcmp(a->value, b->value) == 0)
|
|
||||||
//is equal
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return nameCmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int getInputCount(const char * input) {
|
|
||||||
char buff[2];
|
|
||||||
buff[1] = '\0';
|
|
||||||
|
|
||||||
int elementCount = 0;
|
|
||||||
bool prevWasValue = false;
|
|
||||||
|
|
||||||
for(int i = 0; input[i] != '\0' && input[i] != '\n'; i++) {
|
|
||||||
buff[0] = input[i];
|
|
||||||
|
|
||||||
//if the character is a valid character
|
|
||||||
if(strstr("0123456789 ", buff) != NULL) {
|
|
||||||
if(input[i] == ' ') prevWasValue = false;
|
|
||||||
else if(!prevWasValue) {
|
|
||||||
prevWasValue = true;
|
|
||||||
elementCount++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return elementCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fomod_printOptionsInOrder(fomod_Group_t group) {
|
|
||||||
for(int i = 0; i < group.pluginCount; i++) {
|
|
||||||
printf("%d, %s\n", i, group.plugins[i].name);
|
|
||||||
printf("%s\n", group.plugins[i].description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error_t fomod_installFOMod(const char * modFolder, const char * destination) {
|
|
||||||
//everything should be lowercase since we use casefold() before calling any install function
|
|
||||||
char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL);
|
|
||||||
char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL);
|
|
||||||
|
|
||||||
printf("%s", fomodFile);
|
|
||||||
if(access(fomodFile, F_OK) != 0) {
|
|
||||||
fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n");
|
|
||||||
g_free(fomodFolder);
|
|
||||||
g_free(fomodFile);
|
|
||||||
return ERR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
FOMod_t fomod;
|
|
||||||
int returnValue = parser_parseFOMod(fomodFile, &fomod);
|
|
||||||
if(returnValue == ERR_FAILURE)
|
|
||||||
return ERR_FAILURE;
|
|
||||||
|
|
||||||
g_free(fomodFile);
|
|
||||||
|
|
||||||
GList * flagList = NULL;
|
|
||||||
GList * pendingFileOperations = NULL;
|
|
||||||
|
|
||||||
for(int i = 0; i < fomod.stepCount; i++) {
|
|
||||||
const FOModStep_t * step = &fomod.steps[i];
|
|
||||||
|
|
||||||
bool validFlags = true;
|
|
||||||
for(int flagId = 0; flagId < step->flagCount; flagId++) {
|
|
||||||
const GList * flagLink = g_list_find_custom(flagList, &step->requiredFlags[flagId], (GCompareFunc)fomod_flagEqual);
|
|
||||||
if(flagLink == NULL) {
|
|
||||||
validFlags = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!validFlags) continue;
|
|
||||||
|
|
||||||
for(int groupId = 0; groupId < step->groupCount; groupId++ ) {
|
|
||||||
fomod_Group_t group = step->groups[groupId];
|
|
||||||
|
|
||||||
u_int8_t min;
|
|
||||||
u_int8_t max;
|
|
||||||
int inputCount = 0;
|
|
||||||
char *inputBuffer = NULL;
|
|
||||||
size_t bufferSize = 0;
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
fomod_printOptionsInOrder(group);
|
|
||||||
switch(group.type) {
|
|
||||||
case ONE_ONLY:
|
|
||||||
printf("Select one :\n");
|
|
||||||
min = 1;
|
|
||||||
max = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ANY:
|
|
||||||
printf("Select any (space separated) (leave empty for nothing) :\n");
|
|
||||||
min = 0;
|
|
||||||
max = (u_int8_t)group.pluginCount;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AT_LEAST_ONE:
|
|
||||||
printf("Select at least one (space separated) (leave empty for nothing) :\n");
|
|
||||||
min = 1;
|
|
||||||
max = (u_int8_t)group.pluginCount;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AT_MOST_ONE:
|
|
||||||
printf("Select one or none (space separated) (leave empty for nothing) :\n");
|
|
||||||
min = 0;
|
|
||||||
max = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ALL:
|
|
||||||
printf("Press enter to continue\n");
|
|
||||||
min = 0;
|
|
||||||
max = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
//never happen;
|
|
||||||
fprintf(stderr, "unexpected type please report this issue %d, %d", group.type, __LINE__);
|
|
||||||
return ERR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(getline(&inputBuffer, &bufferSize, stdin) > 0) {
|
|
||||||
inputCount = getInputCount(inputBuffer);
|
|
||||||
if(inputCount <= max && inputCount >= min) {
|
|
||||||
printf("Continuing\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "Invalid input\n");
|
|
||||||
if(inputBuffer != NULL)free(inputBuffer);
|
|
||||||
inputBuffer = NULL;
|
|
||||||
} else {
|
|
||||||
//TODO: handle error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//processing user input
|
|
||||||
|
|
||||||
GRegex* regex = g_regex_new("\\s*", 0, 0, NULL);
|
|
||||||
char ** choices = g_regex_split(regex, inputBuffer, 0);
|
|
||||||
g_regex_unref(regex);
|
|
||||||
|
|
||||||
for(int choiceId = 0; choices[choiceId] != NULL; choiceId++) {
|
|
||||||
//TODO: safer user input
|
|
||||||
int choice = atoi(choices[choiceId]);
|
|
||||||
fomod_Plugin_t plugin = group.plugins[choice];
|
|
||||||
for(int flagId = 0; flagId < plugin.flagCount; flagId++) {
|
|
||||||
flagList = g_list_append(flagList, &plugin.flags[flagId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//do the install
|
|
||||||
for(int pluginId = 0; pluginId < plugin.fileCount; pluginId++) {
|
|
||||||
const fomod_File_t * file = &plugin.files[pluginId];
|
|
||||||
|
|
||||||
fomod_File_t * fileCopy = malloc(sizeof(fomod_File_t));
|
|
||||||
*fileCopy = *file;
|
|
||||||
|
|
||||||
//changing pathes to lowercase since we used casefold and the pathes in the xml might not like it
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev(choices);
|
|
||||||
if(inputBuffer != NULL)free(inputBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: manage multiple files with the same name
|
|
||||||
|
|
||||||
pendingFileOperations = fomod_processCondFiles(&fomod, flagList, pendingFileOperations);
|
|
||||||
fomod_processFileOperations(&pendingFileOperations, modFolder, destination);
|
|
||||||
|
|
||||||
printf("FOMod successfully installed!\n");
|
|
||||||
g_list_free(flagList);
|
|
||||||
fomod_freeFileOperations(pendingFileOperations);
|
|
||||||
fomod_freeFOMod(&fomod);
|
|
||||||
g_free(fomodFolder);
|
|
||||||
return ERR_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include <fomod.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start a terminal based install process for fomod.
|
|
||||||
*
|
|
||||||
* @param modFolder folder of the fomod file.
|
|
||||||
* @param destination folder of the new mod that contains the result of the fomod process.
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
error_t fomod_installFOMod(const char * modFolder, const char * destination);
|
|
||||||
@@ -150,21 +150,3 @@ const char * file_extractExtension(const char * filePath) {
|
|||||||
const char * file_extractFileName(const char * filePath) {
|
const char * file_extractFileName(const char * filePath) {
|
||||||
return file_extractLastPart(filePath, '/');
|
return file_extractLastPart(filePath, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
GList * file_listFolderContent(const char * path) {
|
|
||||||
GList * list = NULL;
|
|
||||||
DIR *d;
|
|
||||||
struct dirent *dir;
|
|
||||||
d = opendir(path);
|
|
||||||
if (d) {
|
|
||||||
while ((dir = readdir(d)) != NULL) {
|
|
||||||
//removes .. & . from the list
|
|
||||||
if(strcmp(dir->d_name, "..") != 0 && strcmp(dir->d_name, ".") != 0) {
|
|
||||||
list = g_list_append(list, strdup(dir->d_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,8 @@
|
|||||||
#define __FILE_H__
|
#define __FILE_H__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <glib.h>
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include "main.h"
|
||||||
#include "constants.h"
|
|
||||||
|
|
||||||
|
|
||||||
//valid copy flags
|
//valid copy flags
|
||||||
@@ -64,6 +62,4 @@ const char * file_extractExtension(const char * filePath);
|
|||||||
*/
|
*/
|
||||||
const char * file_extractFileName(const char * filePath);
|
const char * file_extractFileName(const char * filePath);
|
||||||
|
|
||||||
|
|
||||||
GList * file_listFolderContent(const char * path);
|
|
||||||
#endif
|
#endif
|
||||||
+405
@@ -0,0 +1,405 @@
|
|||||||
|
#include <libxml/tree.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/xmlstring.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "fomod.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "fomod/fomodTypes.h"
|
||||||
|
#include "fomod/group.h"
|
||||||
|
#include "libxml/globals.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
static int getInputCount(const char * input) {
|
||||||
|
char buff[2];
|
||||||
|
buff[1] = '\0';
|
||||||
|
|
||||||
|
int elementCount = 0;
|
||||||
|
bool prevWasValue = false;
|
||||||
|
|
||||||
|
for(int i = 0; input[i] != '\0' && input[i] != '\n'; i++) {
|
||||||
|
buff[0] = input[i];
|
||||||
|
|
||||||
|
//if the character is a valid character
|
||||||
|
if(strstr("0123456789 ", buff) != NULL) {
|
||||||
|
if(input[i] == ' ') prevWasValue = false;
|
||||||
|
else if(!prevWasValue) {
|
||||||
|
prevWasValue = true;
|
||||||
|
elementCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint priorityCmp(gconstpointer a, gconstpointer b) {
|
||||||
|
const fomod_File_t * fileA = (const fomod_File_t *)a;
|
||||||
|
const fomod_File_t * fileB = (const fomod_File_t *)b;
|
||||||
|
|
||||||
|
return fileB->priority - fileA->priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fomod_printOptionsInOrder(fomod_Group_t group) {
|
||||||
|
for(int i = 0; i < group.pluginCount; i++) {
|
||||||
|
printf("%d, %s\n", i, group.plugins[i].name);
|
||||||
|
printf("%s\n", group.plugins[i].description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint fomod_flagEqual(const fomod_Flag_t * a, const fomod_Flag_t * b) {
|
||||||
|
int nameCmp = strcmp(a->name, b->name);
|
||||||
|
if(nameCmp == 0) {
|
||||||
|
if(strcmp(a->value, b->value) == 0)
|
||||||
|
//is equal
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return nameCmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stepCmpAsc(const void * stepA, const void * stepB) {
|
||||||
|
const FOModStep_t * step1 = (const FOModStep_t *)stepA;
|
||||||
|
const FOModStep_t * step2 = (const FOModStep_t *)stepB;
|
||||||
|
return strcmp(step1->name, step2->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stepCmpDesc(const void * stepA, const void * stepB) {
|
||||||
|
const FOModStep_t * step1 = (const FOModStep_t *)stepA;
|
||||||
|
const FOModStep_t * step2 = (const FOModStep_t *)stepB;
|
||||||
|
return 1 - strcmp(step1->name, step2->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void fomod_sortSteps(FOMod_t * fomod) {
|
||||||
|
switch(fomod->stepOrder) {
|
||||||
|
case ASC:
|
||||||
|
qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpAsc);
|
||||||
|
break;
|
||||||
|
case DESC:
|
||||||
|
qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpDesc);
|
||||||
|
break;
|
||||||
|
case ORD:
|
||||||
|
//ord mean that we keep the curent order, so no need to sort anything.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fomod_groupCmpAsc(const void * stepA, const void * stepB) {
|
||||||
|
const fomod_Group_t * step1 = (const fomod_Group_t *)stepA;
|
||||||
|
const fomod_Group_t * step2 = (const fomod_Group_t *)stepB;
|
||||||
|
return strcmp(step1->name, step2->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fomod_groupCmpDesc(const void * stepA, const void * stepB) {
|
||||||
|
const fomod_Group_t * step1 = (const fomod_Group_t *)stepA;
|
||||||
|
const fomod_Group_t * step2 = (const fomod_Group_t *)stepB;
|
||||||
|
return 1 - strcmp(step1->name, step2->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fomod_sortGroup(fomod_Group_t * group) {
|
||||||
|
switch(group->order) {
|
||||||
|
case ASC:
|
||||||
|
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpAsc);
|
||||||
|
break;
|
||||||
|
case DESC:
|
||||||
|
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpDesc);
|
||||||
|
break;
|
||||||
|
case ORD:
|
||||||
|
//ord mean that we keep the curent order, so no need to sort anything.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle error
|
||||||
|
error_t fomod_processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) {
|
||||||
|
//priority higher a less important and should be processed first.
|
||||||
|
*pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp);
|
||||||
|
GList * currentFileOperation = *pendingFileOperations;
|
||||||
|
|
||||||
|
while(currentFileOperation != NULL) {
|
||||||
|
//TODO: support destination
|
||||||
|
//no using link since priority is made to override files and link is annoying to deal with when overriding files.
|
||||||
|
const fomod_File_t * file = (const fomod_File_t *)currentFileOperation->data;
|
||||||
|
char * source = g_build_path("/", modFolder, file->source, NULL);
|
||||||
|
|
||||||
|
//fix the / and \ windows - unix paths
|
||||||
|
xml_fixPath(source);
|
||||||
|
|
||||||
|
int copyResult;
|
||||||
|
if(file->isFolder) {
|
||||||
|
copyResult = file_copy(source, destination, FILE_CP_NO_TARGET_DIR | FILE_CP_RECURSIVE);
|
||||||
|
} else {
|
||||||
|
copyResult = file_copy(source, destination, 0);
|
||||||
|
}
|
||||||
|
if(copyResult != EXIT_SUCCESS) {
|
||||||
|
fprintf(stderr, "Copy failed, some file might be corrupted\n");
|
||||||
|
}
|
||||||
|
g_free(source);
|
||||||
|
|
||||||
|
currentFileOperation = g_list_next(currentFileOperation);
|
||||||
|
}
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * fomod_processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) {
|
||||||
|
for(int condId = 0; condId < fomod->condFilesCount; condId++) {
|
||||||
|
const fomod_CondFile_t *condFile = &fomod->condFiles[condId];
|
||||||
|
|
||||||
|
bool areAllFlagsValid = true;
|
||||||
|
|
||||||
|
//checking if all flags are valid
|
||||||
|
for(long flagId = 0; flagId < condFile->flagCount; flagId++) {
|
||||||
|
const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)fomod_flagEqual);
|
||||||
|
if(link == NULL) {
|
||||||
|
areAllFlagsValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(areAllFlagsValid) {
|
||||||
|
for(long fileId = 0; fileId < condFile->flagCount; fileId++) {
|
||||||
|
const fomod_File_t * file = &(condFile->files[fileId]);
|
||||||
|
|
||||||
|
fomod_File_t * fileCopy = malloc(sizeof(*file));
|
||||||
|
*fileCopy = *file;
|
||||||
|
|
||||||
|
//changing pathes to lowercase since we used casefold and the pathes in the xml might not like it
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pendingFileOperations;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL);
|
||||||
|
char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL);
|
||||||
|
|
||||||
|
if(access(fomodFile, F_OK) != 0) {
|
||||||
|
fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n");
|
||||||
|
g_free(fomodFolder);
|
||||||
|
g_free(fomodFile);
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
FOMod_t fomod;
|
||||||
|
int returnValue = parser_parseFOMod(fomodFile, &fomod);
|
||||||
|
if(returnValue == ERR_FAILURE)
|
||||||
|
return ERR_FAILURE;
|
||||||
|
|
||||||
|
g_free(fomodFile);
|
||||||
|
|
||||||
|
GList * flagList = NULL;
|
||||||
|
GList * pendingFileOperations = NULL;
|
||||||
|
|
||||||
|
fomod_sortSteps(&fomod);
|
||||||
|
|
||||||
|
for(int i = 0; i < fomod.stepCount; i++) {
|
||||||
|
const FOModStep_t * step = &fomod.steps[i];
|
||||||
|
|
||||||
|
bool validFlags = true;
|
||||||
|
for(int flagId = 0; flagId < step->flagCount; flagId++) {
|
||||||
|
const GList * flagLink = g_list_find_custom(flagList, &step->requiredFlags[flagId], (GCompareFunc)fomod_flagEqual);
|
||||||
|
if(flagLink == NULL) {
|
||||||
|
validFlags = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!validFlags) continue;
|
||||||
|
|
||||||
|
for(int groupId = 0; groupId < step->groupCount; groupId++ ) {
|
||||||
|
fomod_Group_t group = step->groups[groupId];
|
||||||
|
|
||||||
|
fomod_sortGroup(&group);
|
||||||
|
|
||||||
|
u_int8_t min;
|
||||||
|
u_int8_t max;
|
||||||
|
int inputCount = 0;
|
||||||
|
char *inputBuffer = NULL;
|
||||||
|
size_t bufferSize = 0;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
fomod_printOptionsInOrder(group);
|
||||||
|
switch(group.type) {
|
||||||
|
case ONE_ONLY:
|
||||||
|
printf("Select one :\n");
|
||||||
|
min = 1;
|
||||||
|
max = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ANY:
|
||||||
|
printf("Select any (space separated) (leave empty for nothing) :\n");
|
||||||
|
min = 0;
|
||||||
|
max = (u_int8_t)group.pluginCount - 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AT_LEAST_ONE:
|
||||||
|
printf("Select at least one (space separated) (leave empty for nothing) :\n");
|
||||||
|
min = 1;
|
||||||
|
max = (u_int8_t)group.pluginCount - 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AT_MOST_ONE:
|
||||||
|
printf("Select one or none (space separated) (leave empty for nothing) :\n");
|
||||||
|
min = 0;
|
||||||
|
max = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ALL:
|
||||||
|
printf("Press enter to continue\n");
|
||||||
|
min = 0;
|
||||||
|
max = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//never happen;
|
||||||
|
fprintf(stderr, "unexpected type please report this issue %d, %d", group.type, __LINE__);
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(getline(&inputBuffer, &bufferSize, stdin) > 0) {
|
||||||
|
inputCount = getInputCount(inputBuffer);
|
||||||
|
if(inputCount <= max && inputCount >= min) {
|
||||||
|
printf("Continuing\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Invalid input\n");
|
||||||
|
if(inputBuffer != NULL)free(inputBuffer);
|
||||||
|
inputBuffer = NULL;
|
||||||
|
} else {
|
||||||
|
//TODO: handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//processing user input
|
||||||
|
|
||||||
|
GRegex* regex = g_regex_new("\\s*", 0, 0, NULL);
|
||||||
|
char ** choices = g_regex_split(regex, inputBuffer, 0);
|
||||||
|
g_regex_unref(regex);
|
||||||
|
|
||||||
|
for(int choiceId = 0; choices[choiceId] != NULL; choiceId++) {
|
||||||
|
//TODO: safer user input
|
||||||
|
int choice = atoi(choices[choiceId]);
|
||||||
|
fomod_Plugin_t plugin = group.plugins[choice];
|
||||||
|
for(int flagId = 0; flagId < plugin.flagCount; flagId++) {
|
||||||
|
flagList = g_list_append(flagList, &plugin.flags[flagId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//do the install
|
||||||
|
for(int pluginId = 0; pluginId < plugin.fileCount; pluginId++) {
|
||||||
|
const fomod_File_t * file = &plugin.files[pluginId];
|
||||||
|
|
||||||
|
fomod_File_t * fileCopy = malloc(sizeof(fomod_File_t));
|
||||||
|
*fileCopy = *file;
|
||||||
|
|
||||||
|
//changing pathes to lowercase since we used casefold and the pathes in the xml might not like it
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(choices);
|
||||||
|
if(inputBuffer != NULL)free(inputBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: manage multiple files with the same name
|
||||||
|
|
||||||
|
pendingFileOperations = fomod_processCondFiles(&fomod, flagList, pendingFileOperations);
|
||||||
|
fomod_processFileOperations(&pendingFileOperations, modFolder, destination);
|
||||||
|
|
||||||
|
printf("FOMod successfully installed!\n");
|
||||||
|
g_list_free(flagList);
|
||||||
|
fomod_freeFileOperations(pendingFileOperations);
|
||||||
|
fomod_freeFOMod(&fomod);
|
||||||
|
g_free(fomodFolder);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fomod_freeFOMod(FOMod_t * fomod) {
|
||||||
|
for(int i = 0; i < fomod->condFilesCount; i++) {
|
||||||
|
fomod_CondFile_t * condFile = &(fomod->condFiles[i]);
|
||||||
|
for(long fileId = 0; fileId < condFile->fileCount; fileId++) {
|
||||||
|
free(condFile->files[fileId].destination);
|
||||||
|
free(condFile->files[fileId].source);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(long flagId = 0; flagId < condFile->flagCount; flagId++) {
|
||||||
|
fomod_Flag_t * flag = &(condFile->requiredFlags[flagId]);
|
||||||
|
free(flag->name);
|
||||||
|
free(flag->value);
|
||||||
|
}
|
||||||
|
free(condFile->files);
|
||||||
|
free(condFile->requiredFlags);
|
||||||
|
}
|
||||||
|
free(fomod->condFiles);
|
||||||
|
free(fomod->moduleImage);
|
||||||
|
free(fomod->moduleName);
|
||||||
|
|
||||||
|
int size = fomod_countUntilNull(fomod->requiredInstallFiles, sizeof(char **));
|
||||||
|
for(int i = 0; i < size; i++) {
|
||||||
|
free(fomod->requiredInstallFiles[i]);
|
||||||
|
}
|
||||||
|
free(fomod->requiredInstallFiles);
|
||||||
|
|
||||||
|
for(int i = 0; i < fomod->stepCount; i++) {
|
||||||
|
FOModStep_t * step = &fomod->steps[i];
|
||||||
|
for(int groupId = 0; groupId < step->groupCount; groupId++) {
|
||||||
|
fomod_Group_t * group = &step->groups[groupId];
|
||||||
|
grp_freeGroup(group);
|
||||||
|
}
|
||||||
|
for(int flagId = 0; flagId < step->flagCount; flagId++) {
|
||||||
|
fomod_Flag_t * flag = &(step->requiredFlags[flagId]);
|
||||||
|
free(flag->name);
|
||||||
|
free(flag->value);
|
||||||
|
}
|
||||||
|
free(step->groups);
|
||||||
|
free(step->requiredFlags);
|
||||||
|
free(step->name);
|
||||||
|
}
|
||||||
|
free(fomod->steps);
|
||||||
|
|
||||||
|
//set every counter to zero and every pointer to null
|
||||||
|
memset(fomod, 0, sizeof(FOMod_t));
|
||||||
|
}
|
||||||
@@ -1,17 +1,22 @@
|
|||||||
#ifndef __PUBLIC_FOMOD_H__
|
#ifndef __FOMOD_H__
|
||||||
#define __PUBLIC_FOMOD_H__
|
#define __FOMOD_H__
|
||||||
|
|
||||||
#include "errorType.h"
|
#include <stdbool.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "fomodTypes.h"
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "fomod/parser.h"
|
||||||
|
#include "fomod/group.h"
|
||||||
|
#include "fomod/xmlUtil.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief parse a fomod xml file (found inside the mod folder /fomod/moduleconfig.xml (everything should be lowercase))
|
* @brief Start a terminal based install process for fomod.
|
||||||
*
|
*
|
||||||
* @param fomodFile path to the moduleconfig.xml
|
* @param modFolder folder of the fomod file.
|
||||||
* @param fomod pointer to a new FOMod_t
|
* @param destination folder of the new mod that contains the result of the fomod process.
|
||||||
|
* @return int
|
||||||
*/
|
*/
|
||||||
error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod);
|
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.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef __FOMOD_TYPES_H__
|
||||||
|
#define __FOMOD_TYPES_H__
|
||||||
|
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include "xmlUtil.h"
|
||||||
|
|
||||||
|
typedef struct fomod_Flag {
|
||||||
|
char * name;
|
||||||
|
char * value;
|
||||||
|
} fomod_Flag_t;
|
||||||
|
|
||||||
|
typedef struct fomod_File {
|
||||||
|
char * source;
|
||||||
|
char * destination;
|
||||||
|
int priority;
|
||||||
|
bool isFolder;
|
||||||
|
} fomod_File_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct fomod_CondFile {
|
||||||
|
fomod_Flag_t * requiredFlags;
|
||||||
|
unsigned int flagCount;
|
||||||
|
fomod_File_t * files;
|
||||||
|
unsigned int fileCount;
|
||||||
|
} fomod_CondFile_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef __GROUP_H__
|
||||||
|
#define __GROUP_H__
|
||||||
|
|
||||||
|
#include <libxml/parser.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 TypeDescriptor { OPTIONAL, MAYBE_USABLE, NOT_USABLE, REQUIRED, RECOMMENDED } TypeDescriptor_t;
|
||||||
|
|
||||||
|
typedef struct fomod_Plugin {
|
||||||
|
char * description;
|
||||||
|
char * image;
|
||||||
|
fomod_Flag_t * flags;
|
||||||
|
int flagCount;
|
||||||
|
fomod_File_t * files;
|
||||||
|
int fileCount;
|
||||||
|
TypeDescriptor_t type;
|
||||||
|
char * name;
|
||||||
|
} fomod_Plugin_t;
|
||||||
|
|
||||||
|
//combine group and "plugins"
|
||||||
|
typedef struct fomod_Group {
|
||||||
|
fomod_Plugin_t * plugins;
|
||||||
|
int pluginCount;
|
||||||
|
GroupType_t type;
|
||||||
|
char * name;
|
||||||
|
fomod_Order_t order;
|
||||||
|
} fomod_Group_t;
|
||||||
|
|
||||||
|
int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group);
|
||||||
|
void grp_freeGroup(fomod_Group_t * group);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <fomod.h>
|
#include "parser.h"
|
||||||
|
#include "fomodTypes.h"
|
||||||
#include "group.h"
|
#include "group.h"
|
||||||
#include "libxml/tree.h"
|
#include "libxml/tree.h"
|
||||||
#include "xmlUtil.h"
|
#include "xmlUtil.h"
|
||||||
@@ -192,58 +193,6 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stepCmpAsc(const void * stepA, const void * stepB) {
|
|
||||||
const FOModStep_t * step1 = (const FOModStep_t *)stepA;
|
|
||||||
const FOModStep_t * step2 = (const FOModStep_t *)stepB;
|
|
||||||
return strcmp(step1->name, step2->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int stepCmpDesc(const void * stepA, const void * stepB) {
|
|
||||||
const FOModStep_t * step1 = (const FOModStep_t *)stepA;
|
|
||||||
const FOModStep_t * step2 = (const FOModStep_t *)stepB;
|
|
||||||
return 1 - strcmp(step1->name, step2->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fomod_sortSteps(FOMod_t * fomod) {
|
|
||||||
switch(fomod->stepOrder) {
|
|
||||||
case ASC:
|
|
||||||
qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpAsc);
|
|
||||||
break;
|
|
||||||
case DESC:
|
|
||||||
qsort(fomod->steps, fomod->stepCount, sizeof(*fomod->steps), stepCmpDesc);
|
|
||||||
break;
|
|
||||||
case ORD:
|
|
||||||
//ord mean that we keep the curent order, so no need to sort anything.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fomod_groupCmpAsc(const void * stepA, const void * stepB) {
|
|
||||||
const fomod_Group_t * step1 = (const fomod_Group_t *)stepA;
|
|
||||||
const fomod_Group_t * step2 = (const fomod_Group_t *)stepB;
|
|
||||||
return strcmp(step1->name, step2->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fomod_groupCmpDesc(const void * stepA, const void * stepB) {
|
|
||||||
const fomod_Group_t * step1 = (const fomod_Group_t *)stepA;
|
|
||||||
const fomod_Group_t * step2 = (const fomod_Group_t *)stepB;
|
|
||||||
return 1 - strcmp(step1->name, step2->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fomod_sortGroup(fomod_Group_t * group) {
|
|
||||||
switch(group->order) {
|
|
||||||
case ASC:
|
|
||||||
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpAsc);
|
|
||||||
break;
|
|
||||||
case DESC:
|
|
||||||
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), fomod_groupCmpDesc);
|
|
||||||
break;
|
|
||||||
case ORD:
|
|
||||||
//ord mean that we keep the curent order, so no need to sort anything.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error_t parser_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;
|
||||||
@@ -332,14 +281,6 @@ error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
|||||||
cur = cur->next;
|
cur = cur->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
//steps are in a specific order in fomod config. we need to make sure we respected this order
|
|
||||||
fomod_sortSteps(fomod);
|
|
||||||
|
|
||||||
//same for the plugins inside the groups
|
|
||||||
for(int i = 0; i < fomod->stepCount; i++)
|
|
||||||
for(int j = 0; j < fomod->steps[i].groupCount; j++)
|
|
||||||
fomod_sortGroup(&(fomod->steps[i].groups[j]));
|
|
||||||
|
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
return ERR_SUCCESS;
|
return ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef __FOMOD_PARSER_H__
|
||||||
|
#define __FOMOD_PARSER_H__
|
||||||
|
|
||||||
|
|
||||||
|
#include "fomodTypes.h"
|
||||||
|
#include "group.h"
|
||||||
|
#include "../main.h"
|
||||||
|
|
||||||
|
//combine installStep and optionalFileGroups
|
||||||
|
typedef struct FOModStep {
|
||||||
|
fomod_Order_t optionOrder;
|
||||||
|
fomod_Group_t * groups;
|
||||||
|
int groupCount;
|
||||||
|
char * name;
|
||||||
|
fomod_Flag_t * requiredFlags;
|
||||||
|
int flagCount;
|
||||||
|
} FOModStep_t;
|
||||||
|
|
||||||
|
typedef struct FOMod {
|
||||||
|
char * moduleName;
|
||||||
|
char * moduleImage;
|
||||||
|
char ** requiredInstallFiles;
|
||||||
|
fomod_Order_t stepOrder;
|
||||||
|
FOModStep_t * steps;
|
||||||
|
int stepCount;
|
||||||
|
fomod_CondFile_t * condFiles;
|
||||||
|
int condFilesCount;
|
||||||
|
} FOMod_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parse a fomod xml file (found inside the mod folder /fomod/moduleconfig.xml (everything should be lowercase))
|
||||||
|
*
|
||||||
|
* @param fomodFile path to the moduleconfig.xml
|
||||||
|
* @param fomod pointer to a new FOMod_t
|
||||||
|
*/
|
||||||
|
error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <fomod.h>
|
|
||||||
|
|
||||||
|
typedef enum FOModOrder { ASC, DESC, ORD } fomod_Order_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
|
|
||||||
#include <constants.h>
|
#include "getDataPath.h"
|
||||||
#include <gameData.h>
|
#include "main.h"
|
||||||
#include <steam.h>
|
#include "steam.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
error_t gameData_getDataPath(int appid, char ** destination) {
|
error_t getDataPath(int appid, char ** destination) {
|
||||||
GHashTable * gamePaths;
|
GHashTable * gamePaths;
|
||||||
error_t status = steam_searchGames(&gamePaths);
|
error_t status = steam_searchGames(&gamePaths);
|
||||||
if(status == ERR_FAILURE) {
|
if(status == ERR_FAILURE) {
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
#ifndef __GAME_DATA_H__
|
#ifndef __DATA_PATH_H__
|
||||||
#define __GAME_DATA_H__
|
#define __DATA_PATH_H__
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "errorType.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the path to the data folder
|
* @brief Get the path to the data folder
|
||||||
@@ -10,6 +9,7 @@
|
|||||||
* @param destination pointer to a null char * variable. it's value will be allocated with malloc
|
* @param destination pointer to a null char * variable. it's value will be allocated with malloc
|
||||||
* @return error_t
|
* @return error_t
|
||||||
*/
|
*/
|
||||||
error_t gameData_getDataPath(int appid, char ** destination);
|
error_t getDataPath(int appid, char ** destination);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#ifndef __GET_HOME_H__
|
|
||||||
#define __GET_HOME_H__
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the user home dir regardless if he used sudo.
|
* @brief Get the user home dir regardless if he used sudo.
|
||||||
@@ -7,5 +6,3 @@
|
|||||||
* @return char* path to the home dir
|
* @return char* path to the home dir
|
||||||
*/
|
*/
|
||||||
char * getHome(void);
|
char * getHome(void);
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#include "guiMain.h"
|
||||||
|
#include "../steam.h"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
static void print_hello(GtkWidget *widget, gpointer data) {
|
||||||
|
g_print("Hello World\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void activate(GtkApplication *app, gpointer user_data) {
|
||||||
|
GtkWidget *window;
|
||||||
|
GtkWidget *button;
|
||||||
|
GtkWidget *box;
|
||||||
|
|
||||||
|
window = gtk_application_window_new(app);
|
||||||
|
gtk_window_set_title(GTK_WINDOW(window), "Mod Manager");
|
||||||
|
gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
|
||||||
|
|
||||||
|
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
|
gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
|
||||||
|
gtk_widget_set_valign(box, GTK_ALIGN_CENTER);
|
||||||
|
|
||||||
|
gtk_window_set_child(GTK_WINDOW(window), box);
|
||||||
|
|
||||||
|
button = gtk_button_new_with_label("Hello World");
|
||||||
|
|
||||||
|
g_signal_connect(button, "clicked", G_CALLBACK(print_hello), NULL);
|
||||||
|
g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_window_destroy), window);
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(box), button);
|
||||||
|
|
||||||
|
gtk_widget_show(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
int guiMain(int argc, char **argv) {
|
||||||
|
|
||||||
|
GtkApplication *app;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
app = gtk_application_new("fr.marcbarbier.modmanager", G_APPLICATION_DEFAULT_FLAGS);
|
||||||
|
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
||||||
|
status = g_application_run(G_APPLICATION(app), 1, argv);
|
||||||
|
g_object_unref(app);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef __GUIMAIN_H__
|
||||||
|
#define __GUIMAIN_H__
|
||||||
|
|
||||||
|
int guiMain(int argc, char **argv);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef __DEPLOY_H__
|
|
||||||
#define __DEPLOY_H__
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
typedef enum deploymentErrors { INVALID_APPID, GAME_NOT_FOUND, OK, CANNOT_MOUNT, FUSE_NOT_INSTALLED, BUG } deploymentErrors_t;
|
|
||||||
|
|
||||||
[[nodiscard]] deploymentErrors_t deploy(char * appIdStr, GList ** ignoredMods);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef __ERROR_TYPE_H__
|
|
||||||
#define __ERROR_TYPE_H__
|
|
||||||
|
|
||||||
typedef enum { ERR_SUCCESS, ERR_FAILURE } error_t;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
#ifndef __FOMOD_TYPES_H__
|
|
||||||
#define __FOMOD_TYPES_H__
|
|
||||||
|
|
||||||
#include "stdbool.h"
|
|
||||||
|
|
||||||
typedef enum GroupType_t { ONE_ONLY, ANY, AT_LEAST_ONE, AT_MOST_ONE, ALL } GroupType_t;
|
|
||||||
typedef enum TypeDescriptor { OPTIONAL, MAYBE_USABLE, NOT_USABLE, REQUIRED, RECOMMENDED } TypeDescriptor_t;
|
|
||||||
typedef enum FOModOrder { ASC, DESC, ORD } fomod_Order_t;
|
|
||||||
|
|
||||||
typedef struct fomod_Flag {
|
|
||||||
char * name;
|
|
||||||
char * value;
|
|
||||||
} fomod_Flag_t;
|
|
||||||
|
|
||||||
typedef struct fomod_File {
|
|
||||||
char * source;
|
|
||||||
char * destination;
|
|
||||||
int priority;
|
|
||||||
bool isFolder;
|
|
||||||
} fomod_File_t;
|
|
||||||
|
|
||||||
typedef struct fomod_CondFile {
|
|
||||||
fomod_Flag_t * requiredFlags;
|
|
||||||
unsigned int flagCount;
|
|
||||||
fomod_File_t * files;
|
|
||||||
unsigned int fileCount;
|
|
||||||
} fomod_CondFile_t;
|
|
||||||
|
|
||||||
typedef struct fomod_Plugin {
|
|
||||||
char * description;
|
|
||||||
char * image;
|
|
||||||
fomod_Flag_t * flags;
|
|
||||||
int flagCount;
|
|
||||||
fomod_File_t * files;
|
|
||||||
int fileCount;
|
|
||||||
TypeDescriptor_t type;
|
|
||||||
char * name;
|
|
||||||
} fomod_Plugin_t;
|
|
||||||
|
|
||||||
//combine group and "plugins"
|
|
||||||
typedef struct fomod_Group {
|
|
||||||
fomod_Plugin_t * plugins;
|
|
||||||
int pluginCount;
|
|
||||||
GroupType_t type;
|
|
||||||
char * name;
|
|
||||||
fomod_Order_t order;
|
|
||||||
} fomod_Group_t;
|
|
||||||
|
|
||||||
//combine installStep and optionalFileGroups
|
|
||||||
typedef struct FOModStep {
|
|
||||||
fomod_Order_t optionOrder;
|
|
||||||
fomod_Group_t * groups;
|
|
||||||
int groupCount;
|
|
||||||
char * name;
|
|
||||||
fomod_Flag_t * requiredFlags;
|
|
||||||
int flagCount;
|
|
||||||
} FOModStep_t;
|
|
||||||
|
|
||||||
typedef struct FOMod {
|
|
||||||
char * moduleName;
|
|
||||||
char * moduleImage;
|
|
||||||
char ** requiredInstallFiles;
|
|
||||||
fomod_Order_t stepOrder;
|
|
||||||
FOModStep_t * steps;
|
|
||||||
int stepCount;
|
|
||||||
fomod_CondFile_t * condFiles;
|
|
||||||
int condFilesCount;
|
|
||||||
} FOMod_t;
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -4,9 +4,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include <install.h>
|
#include "install.h"
|
||||||
|
#include "main.h"
|
||||||
#include <constants.h>
|
|
||||||
#include "archives.h"
|
#include "archives.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ error_t install_addMod(char * filePath, int appId) {
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * configFolder = g_build_filename(getenv("HOME"), MODLIB_WORKING_DIR, 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) {
|
||||||
fprintf(stderr, "Could not create mod folder");
|
fprintf(stderr, "Could not create mod folder");
|
||||||
resultError = ERR_FAILURE;
|
resultError = ERR_FAILURE;
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
#ifndef __INSTALL_H__
|
#ifndef __INSTALL_H__
|
||||||
#define __INSTALL_H__
|
#define __INSTALL_H__
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errorType.h>
|
#include "main.h"
|
||||||
|
|
||||||
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
|
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <errorType.h>
|
|
||||||
#include <deploy.h>
|
|
||||||
#include <steam.h>
|
|
||||||
#include <constants.h>
|
|
||||||
|
|
||||||
#include "gameData.h"
|
|
||||||
#include "getHome.h"
|
|
||||||
#include "file.h"
|
|
||||||
#include "order.h"
|
|
||||||
#include "overlayfs.h"
|
|
||||||
#include "install.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
deploymentErrors_t deploy(char * appIdStr, GList ** ignoredMods) {
|
|
||||||
int appid = steam_parseAppId(appIdStr);
|
|
||||||
if(appid < 0) {
|
|
||||||
return INVALID_APPID;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * home = getHome();
|
|
||||||
char * gameFolder = g_build_filename(home, MODLIB_WORKING_DIR, GAME_FOLDER_NAME, appIdStr, NULL);
|
|
||||||
|
|
||||||
//is the game present in the disk
|
|
||||||
if(access(gameFolder, F_OK) != 0) {
|
|
||||||
free(home);
|
|
||||||
g_free(gameFolder);
|
|
||||||
return GAME_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * dataFolder = NULL;
|
|
||||||
error_t error = gameData_getDataPath(appid, &dataFolder);
|
|
||||||
if(error != ERR_SUCCESS) {
|
|
||||||
free(home);
|
|
||||||
return GAME_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
//unmount everything beforhand
|
|
||||||
//might crash if no mods were installed
|
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
|
||||||
|
|
||||||
GList * mods = order_listMods(appid);
|
|
||||||
//since the priority is by alphabetical order
|
|
||||||
//and we need to bind the least important first
|
|
||||||
//and the most important last
|
|
||||||
//we have to reverse the list
|
|
||||||
mods = g_list_reverse(mods);
|
|
||||||
|
|
||||||
//probably over allocating but this doesn't matter that much
|
|
||||||
char * modsToInstall[g_list_length(mods) + 2];
|
|
||||||
int modCount = 0;
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
if(mods == NULL)break;
|
|
||||||
char * modName = (char *)mods->data;
|
|
||||||
char * modPath = g_build_path("/", modFolder, modName, NULL);
|
|
||||||
char * modFlag = g_build_filename(modPath, INSTALLED_FLAG_FILE, NULL);
|
|
||||||
|
|
||||||
//if the mod is not marked to be deployed then don't
|
|
||||||
if(access(modFlag, F_OK) != 0) {
|
|
||||||
*ignoredMods = g_list_append(*ignoredMods, modName);
|
|
||||||
mods = g_list_next(mods);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//this is made to leave the first case empty so i can put lowerdir in it before feeding it to g_strjoinv
|
|
||||||
modsToInstall[modCount] = modPath;
|
|
||||||
modCount += 1;
|
|
||||||
|
|
||||||
mods = g_list_next(mods);
|
|
||||||
free(modFlag);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(modFolder);
|
|
||||||
|
|
||||||
//const char * data = "lowerdir=gameFolder,gameFolder2,gameFolder3..."
|
|
||||||
modsToInstall[modCount] = gameFolder;
|
|
||||||
modsToInstall[modCount + 1] = NULL;
|
|
||||||
|
|
||||||
char * gameUpperDir = g_build_filename(home, MODLIB_WORKING_DIR, GAME_UPPER_DIR_NAME, appIdStr, NULL);
|
|
||||||
char * gameWorkDir = g_build_filename(home, MODLIB_WORKING_DIR, GAME_WORK_DIR_NAME, appIdStr, NULL);
|
|
||||||
free(home);
|
|
||||||
|
|
||||||
//unmount the game folder
|
|
||||||
//DETACH + FORCE allow us to be sure it will be unload.
|
|
||||||
//it might crash / corrupt game file if the user do it while the game is running
|
|
||||||
//but it's still very unlikely
|
|
||||||
while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0);
|
|
||||||
overlay_errors_t status = overlay_mount(modsToInstall, dataFolder, gameUpperDir, gameWorkDir);
|
|
||||||
if(status == SUCESS) {
|
|
||||||
return OK;
|
|
||||||
} else if(status == FAILURE) {
|
|
||||||
return CANNOT_MOUNT;
|
|
||||||
} else if(status == NOT_INSTALLED) {
|
|
||||||
return FUSE_NOT_INSTALLED;
|
|
||||||
} else {
|
|
||||||
return BUG;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(dataFolder);
|
|
||||||
g_free(gameUpperDir);
|
|
||||||
g_free(gameWorkDir);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
-165
@@ -1,165 +0,0 @@
|
|||||||
#include <libxml/tree.h>
|
|
||||||
#include <libxml/parser.h>
|
|
||||||
#include <libxml/xmlstring.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <fomod.h>
|
|
||||||
#include "file.h"
|
|
||||||
#include "libxml/globals.h"
|
|
||||||
#include <constants.h>
|
|
||||||
|
|
||||||
#include "fomod/group.h"
|
|
||||||
#include "fomod/xmlUtil.h"
|
|
||||||
|
|
||||||
static gint priorityCmp(gconstpointer a, gconstpointer b) {
|
|
||||||
const fomod_File_t * fileA = (const fomod_File_t *)a;
|
|
||||||
const fomod_File_t * fileB = (const fomod_File_t *)b;
|
|
||||||
|
|
||||||
return fileB->priority - fileA->priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
//match the definirion of gcompare func
|
|
||||||
static gint fomod_flagEqual(const fomod_Flag_t * a, const fomod_Flag_t * b) {
|
|
||||||
int nameCmp = strcmp(a->name, b->name);
|
|
||||||
if(nameCmp == 0) {
|
|
||||||
if(strcmp(a->value, b->value) == 0)
|
|
||||||
//is equal
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return nameCmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: handle error
|
|
||||||
error_t fomod_processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) {
|
|
||||||
//priority higher a less important and should be processed first.
|
|
||||||
*pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp);
|
|
||||||
GList * currentFileOperation = *pendingFileOperations;
|
|
||||||
|
|
||||||
while(currentFileOperation != NULL) {
|
|
||||||
//TODO: support destination
|
|
||||||
//no using link since priority is made to override files and link is annoying to deal with when overriding files.
|
|
||||||
const fomod_File_t * file = (const fomod_File_t *)currentFileOperation->data;
|
|
||||||
char * source = g_build_path("/", modFolder, file->source, NULL);
|
|
||||||
|
|
||||||
//fix the / and \ windows - unix paths
|
|
||||||
xml_fixPath(source);
|
|
||||||
|
|
||||||
int copyResult;
|
|
||||||
if(file->isFolder) {
|
|
||||||
copyResult = file_copy(source, destination, FILE_CP_NO_TARGET_DIR | FILE_CP_RECURSIVE);
|
|
||||||
} else {
|
|
||||||
copyResult = file_copy(source, destination, 0);
|
|
||||||
}
|
|
||||||
if(copyResult != EXIT_SUCCESS) {
|
|
||||||
fprintf(stderr, "Copy failed, some file might be corrupted\n");
|
|
||||||
}
|
|
||||||
g_free(source);
|
|
||||||
|
|
||||||
currentFileOperation = g_list_next(currentFileOperation);
|
|
||||||
}
|
|
||||||
return ERR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
GList * fomod_processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) {
|
|
||||||
for(int condId = 0; condId < fomod->condFilesCount; condId++) {
|
|
||||||
const fomod_CondFile_t *condFile = &fomod->condFiles[condId];
|
|
||||||
|
|
||||||
bool areAllFlagsValid = true;
|
|
||||||
|
|
||||||
//checking if all flags are valid
|
|
||||||
for(long flagId = 0; flagId < condFile->flagCount; flagId++) {
|
|
||||||
const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)fomod_flagEqual);
|
|
||||||
if(link == NULL) {
|
|
||||||
areAllFlagsValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(areAllFlagsValid) {
|
|
||||||
for(long fileId = 0; fileId < condFile->flagCount; fileId++) {
|
|
||||||
const fomod_File_t * file = &(condFile->files[fileId]);
|
|
||||||
|
|
||||||
fomod_File_t * fileCopy = malloc(sizeof(*file));
|
|
||||||
*fileCopy = *file;
|
|
||||||
|
|
||||||
//changing pathes to lowercase since we used casefold and the pathes in the xml might not like it
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pendingFileOperations;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef __GROUP_H__
|
|
||||||
#define __GROUP_H__
|
|
||||||
|
|
||||||
#include <libxml/parser.h>
|
|
||||||
#include "xmlUtil.h"
|
|
||||||
#include <fomod.h>
|
|
||||||
|
|
||||||
int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group);
|
|
||||||
void grp_freeGroup(fomod_Group_t * group);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <constants.h>
|
|
||||||
|
|
||||||
#include "loadOrder.h"
|
#include "loadOrder.h"
|
||||||
|
#include "main.h"
|
||||||
#include "steam.h"
|
#include "steam.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
@@ -9,7 +8,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "gameData.h"
|
#include "getDataPath.h"
|
||||||
|
|
||||||
//TODO: detect if the game is running
|
//TODO: detect if the game is running
|
||||||
//TODO: deploy the game
|
//TODO: deploy the game
|
||||||
@@ -22,7 +21,7 @@ error_t order_listPlugins(int appid, GList ** plugins) {
|
|||||||
sprintf(appidStr, "%d", appid);
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
char * dataFolder = NULL;
|
char * dataFolder = NULL;
|
||||||
error_t error = gameData_getDataPath(appid, &dataFolder);
|
error_t error = getDataPath(appid, &dataFolder);
|
||||||
if(error != ERR_SUCCESS) {
|
if(error != ERR_SUCCESS) {
|
||||||
return ERR_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#define __LOAD_ORDER_H__
|
#define __LOAD_ORDER_H__
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <errorType.h>
|
#include "main.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief find all plugins in the data folder of the game
|
* @brief find all plugins in the data folder of the game
|
||||||
+175
-62
@@ -8,24 +8,20 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <libaudit.h>
|
#include <libaudit.h>
|
||||||
|
|
||||||
#include <gameData.h>
|
#include "getDataPath.h"
|
||||||
#include <loadOrder.h>
|
#include "loadOrder.h"
|
||||||
#include <deploy.h>
|
#include "overlayfs.h"
|
||||||
#include <steam.h>
|
#include "install.h"
|
||||||
#include <install.h>
|
#include "getHome.h"
|
||||||
#include <getHome.h>
|
#include "steam.h"
|
||||||
#include <steam.h>
|
#include "main.h"
|
||||||
#include <file.h>
|
#include "file.h"
|
||||||
#include <fomod.h>
|
|
||||||
#include <order.h>
|
|
||||||
#include <constants.h>
|
|
||||||
#include <deploy.h>
|
|
||||||
#include <errorType.h>
|
|
||||||
|
|
||||||
#include "cli.h"
|
|
||||||
#include "fomod.h"
|
#include "fomod.h"
|
||||||
|
#include "order.h"
|
||||||
|
|
||||||
|
#include "gui/guiMain.h"
|
||||||
|
|
||||||
|
GHashTable * gamePaths;
|
||||||
#define ENABLED_COLOR "\033[0;32m"
|
#define ENABLED_COLOR "\033[0;32m"
|
||||||
#define DISABLED_COLOR "\033[0;31m"
|
#define DISABLED_COLOR "\033[0;31m"
|
||||||
|
|
||||||
@@ -33,6 +29,24 @@ static bool isRoot() {
|
|||||||
return getuid() == 0;
|
return getuid() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GList * listFilesInFolder(const char * path) {
|
||||||
|
GList * list = NULL;
|
||||||
|
DIR *d;
|
||||||
|
struct dirent *dir;
|
||||||
|
d = opendir(path);
|
||||||
|
if (d) {
|
||||||
|
while ((dir = readdir(d)) != NULL) {
|
||||||
|
//removes .. & . from the list
|
||||||
|
if(strcmp(dir->d_name, "..") != 0 && strcmp(dir->d_name, ".") != 0) {
|
||||||
|
list = g_list_append(list, strdup(dir->d_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
static int usage() {
|
static int usage() {
|
||||||
printf("Use --list-games or -l to list compatible games\n");
|
printf("Use --list-games or -l to list compatible games\n");
|
||||||
|
|
||||||
@@ -56,6 +70,36 @@ static int usage() {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10);
|
||||||
|
if(errno == EINVAL || strtoulSentinel == appIdStr) {
|
||||||
|
fprintf(stderr, "Appid has to be a valid number\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gameId = steam_gameIdFromAppId((int)appid);
|
||||||
|
if(gameId < 0) {
|
||||||
|
fprintf(stderr, "Game is not compatible\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!g_hash_table_contains(gamePaths, &gameId)) {
|
||||||
|
fprintf(stderr, "Game not found\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//no valid appid goes far enough to justify long
|
||||||
|
return (int)appid;
|
||||||
|
}
|
||||||
|
|
||||||
static int listGames(int argc, char **) {
|
static int listGames(int argc, char **) {
|
||||||
if(argc != 2) return usage();
|
if(argc != 2) return usage();
|
||||||
|
|
||||||
@@ -84,7 +128,7 @@ 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];
|
||||||
|
|
||||||
int appid = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -96,14 +140,14 @@ static int add(int argc, char ** argv) {
|
|||||||
static 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 = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if( appid < 0) {
|
if( appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//might crash if no mods were installed
|
//might crash if no mods were installed
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
|
|
||||||
GList * mods = order_listMods(appid);
|
GList * mods = order_listMods(appid);
|
||||||
@@ -142,7 +186,7 @@ static int listAllMods(int argc, char ** argv) {
|
|||||||
static 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 = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -158,9 +202,9 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) {
|
|||||||
|
|
||||||
//might crash if no mods were installed
|
//might crash if no mods were installed
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
GList * mods = order_listMods(appid);
|
GList * mods = listFilesInFolder(modFolder);
|
||||||
GList * modsFirstPointer = mods;
|
GList * modsFirstPointer = mods;
|
||||||
|
|
||||||
for(unsigned long i = 0; i < modId; i++) {
|
for(unsigned long i = 0; i < modId; i++) {
|
||||||
@@ -214,63 +258,129 @@ exit:
|
|||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static error_t cli_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];
|
||||||
int appid = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
GList * ignoredMods = NULL;
|
char * home = getHome();
|
||||||
|
char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL);
|
||||||
|
|
||||||
deploymentErrors_t errors = deploy(appIdStr, &ignoredMods);
|
if(access(gameFolder, F_OK) != 0) {
|
||||||
switch(errors) {
|
free(home);
|
||||||
//we prevalidate the appid so it is a bug
|
g_free(gameFolder);
|
||||||
default:
|
printf("Please run '%s --setup %d' first", APP_NAME, appid);
|
||||||
case INVALID_APPID:
|
return EXIT_FAILURE;
|
||||||
case BUG:
|
|
||||||
fprintf(stderr, "A bug was detected during the deployment\n");
|
|
||||||
return ERR_FAILURE;
|
|
||||||
case CANNOT_MOUNT:
|
|
||||||
fprintf(stderr, "Failed to mount the game files");
|
|
||||||
return ERR_FAILURE;
|
|
||||||
case FUSE_NOT_INSTALLED:
|
|
||||||
fprintf(stderr, "This software requires fuse-overlayfs to be installed");
|
|
||||||
return ERR_FAILURE;
|
|
||||||
case GAME_NOT_FOUND:
|
|
||||||
fprintf(stderr, "Could not find the game files");
|
|
||||||
return ERR_FAILURE;
|
|
||||||
case OK:
|
|
||||||
printf("Success");
|
|
||||||
return ERR_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//unmount everything beforhand
|
||||||
|
//might crash if no mods were installed
|
||||||
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
|
|
||||||
|
GList * mods = listFilesInFolder(modFolder);
|
||||||
|
//since the priority is by alphabetical order
|
||||||
|
//and we need to bind the least important first
|
||||||
|
//and the most important last
|
||||||
|
//we have to reverse the list
|
||||||
|
mods = g_list_reverse(mods);
|
||||||
|
|
||||||
|
//probably over allocating but this doesn't matter that much
|
||||||
|
char * modsToInstall[g_list_length(mods) + 2];
|
||||||
|
int modCount = 0;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if(mods == NULL)break;
|
||||||
|
char * modName = (char *)mods->data;
|
||||||
|
char * modPath = g_build_path("/", modFolder, modName, NULL);
|
||||||
|
char * modFlag = g_build_filename(modPath, INSTALLED_FLAG_FILE, NULL);
|
||||||
|
|
||||||
|
//if the mod is not marked to be deployed then don't
|
||||||
|
if(access(modFlag, F_OK) != 0) {
|
||||||
|
printf("ignoring mod %s\n", modName);
|
||||||
|
mods = g_list_next(mods);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//this is made to leave the first case empty so i can put lowerdir in it before feeding it to g_strjoinv
|
||||||
|
printf("Installing %s\n", modName);
|
||||||
|
modsToInstall[modCount] = modPath;
|
||||||
|
modCount += 1;
|
||||||
|
|
||||||
|
mods = g_list_next(mods);
|
||||||
|
free(modFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(modFolder);
|
||||||
|
|
||||||
|
//const char * data = "lowerdir=gameFolder,gameFolder2,gameFolder3..."
|
||||||
|
modsToInstall[modCount] = gameFolder;
|
||||||
|
modsToInstall[modCount + 1] = NULL;
|
||||||
|
printf("Mounting the overlay\n");
|
||||||
|
|
||||||
|
char * dataFolder = 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 * gameWorkDir = g_build_filename(home, MANAGER_FILES, GAME_WORK_DIR_NAME, appIdStr, NULL);
|
||||||
|
free(home);
|
||||||
|
|
||||||
|
//unmount the game folder
|
||||||
|
//DETACH + FORCE allow us to be sure it will be unload.
|
||||||
|
//it might crash / corrupt game file if the user do it while the game is running
|
||||||
|
//but it's still very unlikely
|
||||||
|
while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0);
|
||||||
|
overlay_errors_t status = overlay_mount(modsToInstall, dataFolder, gameUpperDir, gameWorkDir);
|
||||||
|
if(status == SUCESS) {
|
||||||
|
printf("Everything is ready, just launch the game\n");
|
||||||
|
} else if(status == FAILURE) {
|
||||||
|
fprintf(stderr, "Could not mount the mods overlay\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if(status == NOT_INSTALLED) {
|
||||||
|
fprintf(stderr, "Please install fuse-overlayfs\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "%d bug detected, please report this.\n", __LINE__);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(dataFolder);
|
||||||
|
g_free(gameUpperDir);
|
||||||
|
g_free(gameWorkDir);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static 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 = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
|
|
||||||
char * gameUpperDir = g_build_filename(home, MODLIB_WORKING_DIR, 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, MODLIB_WORKING_DIR, GAME_WORK_DIR_NAME, appIdStr, NULL);
|
char * gameWorkDir = g_build_filename(home, MANAGER_FILES, GAME_WORK_DIR_NAME, appIdStr, NULL);
|
||||||
g_mkdir_with_parents(gameUpperDir, 0755);
|
g_mkdir_with_parents(gameUpperDir, 0755);
|
||||||
g_mkdir_with_parents(gameWorkDir, 0755);
|
g_mkdir_with_parents(gameWorkDir, 0755);
|
||||||
free(gameUpperDir);
|
free(gameUpperDir);
|
||||||
free(gameWorkDir);
|
free(gameWorkDir);
|
||||||
|
|
||||||
char * dataFolder = NULL;
|
char * dataFolder = NULL;
|
||||||
error_t error = gameData_getDataPath(appid, &dataFolder);
|
error_t error = getDataPath(appid, &dataFolder);
|
||||||
if(error != ERR_SUCCESS) {
|
if(error != ERR_SUCCESS) {
|
||||||
return ERR_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * gameFolder = g_build_filename(home, MODLIB_WORKING_DIR, 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
|
||||||
@@ -303,13 +413,13 @@ static int setup(int argc, char ** argv) {
|
|||||||
static 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 = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * dataFolder = NULL;
|
char * dataFolder = NULL;
|
||||||
error_t error = gameData_getDataPath(appid, &dataFolder);
|
error_t error = getDataPath(appid, &dataFolder);
|
||||||
if(error != ERR_SUCCESS) {
|
if(error != ERR_SUCCESS) {
|
||||||
return ERR_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -323,7 +433,7 @@ static int unbind(int argc, char ** argv) {
|
|||||||
static 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 = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -338,9 +448,9 @@ static int removeMod(int argc, char ** argv) {
|
|||||||
|
|
||||||
//might crash if no mods were installed
|
//might crash if no mods were installed
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
GList * mods = order_listMods(appid);
|
GList * mods = listFilesInFolder(modFolder);
|
||||||
GList * modsFirstPointer = mods;
|
GList * modsFirstPointer = mods;
|
||||||
|
|
||||||
for(unsigned long i = 0; i < modId; i++) {
|
for(unsigned long i = 0; i < modId; i++) {
|
||||||
@@ -365,7 +475,7 @@ static int removeMod(int argc, char ** argv) {
|
|||||||
static 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 = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -380,9 +490,9 @@ static int fomod(int argc, char ** argv) {
|
|||||||
|
|
||||||
//might crash if no mods were installed
|
//might crash if no mods were installed
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
GList * mods = order_listMods(appid);
|
GList * mods = listFilesInFolder(modFolder);
|
||||||
GList * modsFirstPointer = mods;
|
GList * modsFirstPointer = mods;
|
||||||
|
|
||||||
for(unsigned long i = 0; i < modId; i++) {
|
for(unsigned long i = 0; i < modId; i++) {
|
||||||
@@ -416,7 +526,7 @@ static int fomod(int argc, char ** argv) {
|
|||||||
static int swapMod(int argc, char ** argv) {
|
static int swapMod(int argc, char ** argv) {
|
||||||
if(argc != 5) return usage();
|
if(argc != 5) return usage();
|
||||||
char * appIdStr = argv[2];
|
char * appIdStr = argv[2];
|
||||||
int appid = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -444,7 +554,7 @@ static int printLoadOrder(int argc, char ** argv) {
|
|||||||
if(argc != 3) return usage();
|
if(argc != 3) return usage();
|
||||||
|
|
||||||
char * appIdStr = argv[2];
|
char * appIdStr = argv[2];
|
||||||
int appid = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -473,7 +583,7 @@ static int printPlugins(int argc, char ** argv) {
|
|||||||
if(argc != 3) return usage();
|
if(argc != 3) return usage();
|
||||||
|
|
||||||
char * appIdStr = argv[2];
|
char * appIdStr = argv[2];
|
||||||
int appid = steam_parseAppId(appIdStr);
|
int appid = validateAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -506,7 +616,7 @@ int main(int argc, char ** argv) {
|
|||||||
|
|
||||||
int returnValue = EXIT_SUCCESS;
|
int returnValue = EXIT_SUCCESS;
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * configFolder = g_build_filename(home, MODLIB_WORKING_DIR, NULL);
|
char * configFolder = g_build_filename(home, MANAGER_FILES, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
if(access(configFolder, F_OK) != 0) {
|
if(access(configFolder, F_OK) != 0) {
|
||||||
|
|
||||||
@@ -544,7 +654,7 @@ int main(int argc, char ** argv) {
|
|||||||
returnValue = installAndUninstallMod(argc, argv, false);
|
returnValue = installAndUninstallMod(argc, argv, false);
|
||||||
|
|
||||||
else if(strcmp(argv[1], "--bind") == 0 || strcmp(argv[1], "-d") == 0)
|
else if(strcmp(argv[1], "--bind") == 0 || strcmp(argv[1], "-d") == 0)
|
||||||
returnValue = cli_deploy(argc, argv);
|
returnValue = deploy(argc, argv);
|
||||||
|
|
||||||
else if(strcmp(argv[1], "--unbind") == 0)
|
else if(strcmp(argv[1], "--unbind") == 0)
|
||||||
returnValue = unbind(argc, argv);
|
returnValue = unbind(argc, argv);
|
||||||
@@ -558,6 +668,9 @@ int main(int argc, char ** 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)
|
||||||
returnValue = removeMod(argc, argv);
|
returnValue = removeMod(argc, argv);
|
||||||
|
|
||||||
|
else if(strcmp(argv[1], "--gui") == 0)
|
||||||
|
returnValue = guiMain(argc, argv);
|
||||||
|
|
||||||
else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0)
|
else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0)
|
||||||
returnValue = swapMod(argc, argv);
|
returnValue = swapMod(argc, argv);
|
||||||
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
#ifndef __MAIN_H__
|
#ifndef __MAIN_H__
|
||||||
#define __MAIN_H__
|
#define __MAIN_H__
|
||||||
|
|
||||||
#define MODLIB_NAME "mod-manager"
|
#include <glib.h>
|
||||||
|
|
||||||
//no function should ever be put here, only keep important macros and globals here
|
//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 MODLIB_WORKING_DIR ".local/share/" MODLIB_NAME
|
#define MANAGER_FILES ".local/share/" APP_NAME
|
||||||
#define MOD_FOLDER_NAME "MOD_FOLDER"
|
#define MOD_FOLDER_NAME "MOD_FOLDER"
|
||||||
//the folder in which the games file will be linked or copied
|
//the folder in which the games file will be linked or copied
|
||||||
#define GAME_FOLDER_NAME "GAME_FOLDER"
|
#define GAME_FOLDER_NAME "GAME_FOLDER"
|
||||||
@@ -15,5 +17,8 @@
|
|||||||
//overlayfs temporary dir.
|
//overlayfs temporary dir.
|
||||||
#define GAME_WORK_DIR_NAME "WORK_DIRS"
|
#define GAME_WORK_DIR_NAME "WORK_DIRS"
|
||||||
|
|
||||||
#define MODLIB_VERSION "0.1"
|
#define VERSION "0.1"
|
||||||
|
|
||||||
|
typedef enum { ERR_SUCCESS, ERR_FAILURE } error_t;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -3,8 +3,7 @@
|
|||||||
#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
|
||||||
#include <constants.h>
|
#include "main.h"
|
||||||
|
|
||||||
#include "getHome.h"
|
#include "getHome.h"
|
||||||
|
|
||||||
typedef struct Mod {
|
typedef struct Mod {
|
||||||
@@ -28,7 +27,7 @@ GList * order_listMods(int appid) {
|
|||||||
sprintf(appidStr, "%d", appid);
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appidStr, NULL);
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appidStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
|
|
||||||
GList * list = NULL;
|
GList * list = NULL;
|
||||||
@@ -100,7 +99,7 @@ error_t order_swapPlace(int appid, int modIdA, int modIdB) {
|
|||||||
sprintf(appidStr, "%d", appid);
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appidStr, NULL);
|
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appidStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
|
|
||||||
GList * list = order_listMods(appid);
|
GList * list = order_listMods(appid);
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#define __ORDER_H__
|
#define __ORDER_H__
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <errorType.h>
|
#include "main.h"
|
||||||
|
|
||||||
#define ORDER_FILE "__ORDER__"
|
#define ORDER_FILE "__ORDER__"
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ overlay_errors_t overlay_mount(char ** sources, const char * dest, const char *
|
|||||||
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
|
||||||
exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-o", mountData, dest, NULL));
|
exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-r", "-o", mountData, dest, NULL));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
int returnValue = 0;
|
int returnValue = 0;
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "steam.h"
|
#include "steam.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "getHome.h"
|
#include "getHome.h"
|
||||||
|
#include "main.h"
|
||||||
#include <constants.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -257,33 +255,3 @@ int steam_gameIdFromAppId(u_int32_t appid) {
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int steam_parseAppId(const char * appIdStr) {
|
|
||||||
GHashTable * gamePaths;
|
|
||||||
error_t status = steam_searchGames(&gamePaths);
|
|
||||||
if(status == ERR_FAILURE) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * strtoulSentinel;
|
|
||||||
//strtoul set EINVAL(after C99) if the string is invalid
|
|
||||||
unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10);
|
|
||||||
if(errno == EINVAL || strtoulSentinel == appIdStr) {
|
|
||||||
fprintf(stderr, "Appid has to be a valid number\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gameId = steam_gameIdFromAppId((int)appid);
|
|
||||||
if(gameId < 0) {
|
|
||||||
fprintf(stderr, "Game is not compatible\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!g_hash_table_contains(gamePaths, &gameId)) {
|
|
||||||
fprintf(stderr, "Game not found\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//no valid appid goes far enough to justify long
|
|
||||||
return (int)appid;
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
#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>
|
||||||
#include <errorType.h>
|
|
||||||
|
|
||||||
typedef struct steam_App {
|
typedef struct steam_App {
|
||||||
unsigned int appid;
|
unsigned int appid;
|
||||||
@@ -30,6 +32,7 @@ static const u_int32_t GAMES_APPIDS[] = {
|
|||||||
489830,
|
489830,
|
||||||
22330,
|
22330,
|
||||||
377160,
|
377160,
|
||||||
|
72850,
|
||||||
22320
|
22320
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,11 +41,11 @@ static const char * GAMES_NAMES[] = {
|
|||||||
"Skyrim Special Edition",
|
"Skyrim Special Edition",
|
||||||
"Oblivion",
|
"Oblivion",
|
||||||
"Fallout 4",
|
"Fallout 4",
|
||||||
|
"Skyrim",
|
||||||
"Morrowind"
|
"Morrowind"
|
||||||
};
|
};
|
||||||
|
|
||||||
_Static_assert(sizeof(GAMES_NAMES) / sizeof(GAMES_NAMES[0]) == sizeof(GAMES_APPIDS) / sizeof(GAMES_APPIDS[0]),
|
_Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Names doesn't match");
|
||||||
"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
|
||||||
@@ -63,12 +66,4 @@ void steam_freeGameTable(void);
|
|||||||
*/
|
*/
|
||||||
int steam_gameIdFromAppId(u_int32_t appid);
|
int steam_gameIdFromAppId(u_int32_t appid);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parse the steamId string
|
|
||||||
*
|
|
||||||
* @param appIdStr steamId string
|
|
||||||
* @return int -1 if failures or an int containing the steamId
|
|
||||||
*/
|
|
||||||
int steam_parseAppId(const char * appIdStr);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user