Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fce1c2912c | |||
| 0c2b3afa9b | |||
| 327486d897 |
+58
-22
@@ -9,8 +9,8 @@ if(CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
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")
|
||||
set(CMAKE_C_FLAGS_DEBUG "-g -Werror -O0")
|
||||
set(CMAKE_C_FLAGS_RELEASE "")
|
||||
|
||||
# generate the compile_commands for vscode / clang
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||
@@ -19,37 +19,73 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||
project(mod-manager)
|
||||
|
||||
|
||||
set(SOURCES
|
||||
src/steam.c
|
||||
src/loadOrder.c
|
||||
src/install.c
|
||||
src/overlayfs.c
|
||||
src/getDataPath.c
|
||||
src/getHome.c
|
||||
src/file.c
|
||||
src/order.c
|
||||
src/archives.c
|
||||
src/fomod.c
|
||||
src/fomod/group.c
|
||||
src/fomod/xmlUtil.c
|
||||
src/fomod/parser.c
|
||||
set(LIB_SOURCES
|
||||
src/lib/steam.c
|
||||
src/lib/loadOrder.c
|
||||
src/lib/install.c
|
||||
src/lib/overlayfs.c
|
||||
src/lib/gameData.c
|
||||
src/lib/getHome.c
|
||||
src/lib/file.c
|
||||
src/lib/order.c
|
||||
src/lib/archives.c
|
||||
src/lib/fomod.c
|
||||
src/lib/deploy.c
|
||||
src/lib/fomod/group.c
|
||||
src/lib/fomod/xmlUtil.c
|
||||
src/lib/fomod/parser.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_executable(mod-manager src/main.c ${SOURCES})
|
||||
add_executable(mod-manager ${CLI_SOURCES})
|
||||
add_library(libmod-manager SHARED ${LIB_SOURCES})
|
||||
set_target_properties(libmod-manager PROPERTIES PUBLIC_HEADER ${LIB_PUBLIC_HEADERS})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLIB REQUIRED glib-2.0)
|
||||
pkg_search_module(AUDIT REQUIRED audit)
|
||||
pkg_search_module(LIBXML REQUIRED libxml-2.0)
|
||||
|
||||
target_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${AUDIT_INCLUDE_DIRS})
|
||||
|
||||
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_link_libraries(mod-manager ${LIBXML_LIBRARIES})
|
||||
target_link_libraries(mod-manager ${AUDIT_LIBRARIES})
|
||||
target_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||
|
||||
install(TARGETS mod-manager DESTINATION bin)
|
||||
|
||||
install(TARGETS mod-manager OPTIONAL DESTINATION bin)
|
||||
|
||||
install(TARGETS libmod-manager
|
||||
LIBRARY DESTINATION lib
|
||||
PUBLIC_HEADER DESTINATION include/mod-manager
|
||||
)
|
||||
|
||||
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 "")
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
* 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
|
||||
url='https://gitlab.marcbarbier.fr/Marc/modmanager'
|
||||
license=('GPL2')
|
||||
source=( 'git+https://gitlab.marcbarbier.fr/Marc/modmanager.git' )
|
||||
source=( 'git+https://github.com/Marc-Pierre-Barbier/ModManager.git' )
|
||||
md5sums=( 'SKIP' )
|
||||
|
||||
optdepends=('fuse-overlayfs: special filesystem support' )
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
|
||||
## Function names
|
||||
|
||||
### shared functions
|
||||
### header 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()
|
||||
|
||||
if the function has the same name as the module then it doesn't need to be repeated.
|
||||
|
||||
### static functions
|
||||
|
||||
there is no rule for now
|
||||
all static functions should be in camelCase and they should not mention their module.
|
||||
|
||||
## Struct names
|
||||
|
||||
+62
-169
@@ -8,16 +8,23 @@
|
||||
#include <sys/wait.h>
|
||||
#include <libaudit.h>
|
||||
|
||||
#include "getDataPath.h"
|
||||
#include "loadOrder.h"
|
||||
#include "overlayfs.h"
|
||||
#include "install.h"
|
||||
#include "getHome.h"
|
||||
#include "steam.h"
|
||||
#include "main.h"
|
||||
#include "file.h"
|
||||
#include <gameData.h>
|
||||
#include <loadOrder.h>
|
||||
#include <deploy.h>
|
||||
#include <steam.h>
|
||||
#include <install.h>
|
||||
#include <getHome.h>
|
||||
#include <steam.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 "order.h"
|
||||
|
||||
|
||||
#define ENABLED_COLOR "\033[0;32m"
|
||||
#define DISABLED_COLOR "\033[0;31m"
|
||||
@@ -26,24 +33,6 @@ static bool isRoot() {
|
||||
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() {
|
||||
printf("Use --list-games or -l to list compatible games\n");
|
||||
|
||||
@@ -67,36 +56,6 @@ static int usage() {
|
||||
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 **) {
|
||||
if(argc != 2) return usage();
|
||||
|
||||
@@ -125,7 +84,7 @@ static int add(int argc, char ** argv) {
|
||||
if(argc != 4) return usage();
|
||||
const char * appIdStr = argv[2];
|
||||
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -137,14 +96,14 @@ static int add(int argc, char ** argv) {
|
||||
static int listAllMods(int argc, char ** argv) {
|
||||
if(argc != 3) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if( appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//might crash if no mods were installed
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
free(home);
|
||||
|
||||
GList * mods = order_listMods(appid);
|
||||
@@ -183,7 +142,7 @@ static int listAllMods(int argc, char ** argv) {
|
||||
static int installAndUninstallMod(int argc, char ** argv, bool install) {
|
||||
if(argc != 4) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -199,9 +158,9 @@ static int installAndUninstallMod(int argc, char ** argv, bool install) {
|
||||
|
||||
//might crash if no mods were installed
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
free(home);
|
||||
GList * mods = listFilesInFolder(modFolder);
|
||||
GList * mods = order_listMods(appid);
|
||||
GList * modsFirstPointer = mods;
|
||||
|
||||
for(unsigned long i = 0; i < modId; i++) {
|
||||
@@ -255,129 +214,63 @@ exit:
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
static int deploy(int argc, char ** argv) {
|
||||
static error_t cli_deploy(int argc, char ** argv) {
|
||||
if(argc != 3) return usage();
|
||||
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char * home = getHome();
|
||||
char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL);
|
||||
GList * ignoredMods = NULL;
|
||||
|
||||
if(access(gameFolder, F_OK) != 0) {
|
||||
free(home);
|
||||
g_free(gameFolder);
|
||||
printf("Please run '%s --setup %d' first", APP_NAME, appid);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//unmount everything beforhand
|
||||
//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);
|
||||
deploymentErrors_t errors = deploy(appIdStr, &ignoredMods);
|
||||
switch(errors) {
|
||||
//we prevalidate the appid so it is a bug
|
||||
default:
|
||||
case INVALID_APPID:
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if(argc != 3 ) return usage();
|
||||
const char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char * home = getHome();
|
||||
|
||||
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 * 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);
|
||||
g_mkdir_with_parents(gameUpperDir, 0755);
|
||||
g_mkdir_with_parents(gameWorkDir, 0755);
|
||||
free(gameUpperDir);
|
||||
free(gameWorkDir);
|
||||
|
||||
char * dataFolder = NULL;
|
||||
error_t error = getDataPath(appid, &dataFolder);
|
||||
error_t error = gameData_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, MODLIB_WORKING_DIR, GAME_FOLDER_NAME, appIdStr, NULL);
|
||||
if(access(gameFolder, F_OK) == 0) {
|
||||
//if the game folder alredy exists just delete it
|
||||
//this will allow the removal of dlcs and language change
|
||||
@@ -410,13 +303,13 @@ static int setup(int argc, char ** argv) {
|
||||
static int unbind(int argc, char ** argv) {
|
||||
if(argc != 3 ) return usage();
|
||||
const char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char * dataFolder = NULL;
|
||||
error_t error = getDataPath(appid, &dataFolder);
|
||||
error_t error = gameData_getDataPath(appid, &dataFolder);
|
||||
if(error != ERR_SUCCESS) {
|
||||
return ERR_FAILURE;
|
||||
}
|
||||
@@ -430,7 +323,7 @@ static int unbind(int argc, char ** argv) {
|
||||
static int removeMod(int argc, char ** argv) {
|
||||
if(argc != 4) return usage();
|
||||
const char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
fprintf(stderr, "Invalid appid");
|
||||
return EXIT_FAILURE;
|
||||
@@ -445,9 +338,9 @@ static int removeMod(int argc, char ** argv) {
|
||||
|
||||
//might crash if no mods were installed
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
free(home);
|
||||
GList * mods = listFilesInFolder(modFolder);
|
||||
GList * mods = order_listMods(appid);
|
||||
GList * modsFirstPointer = mods;
|
||||
|
||||
for(unsigned long i = 0; i < modId; i++) {
|
||||
@@ -472,7 +365,7 @@ static int removeMod(int argc, char ** argv) {
|
||||
static int fomod(int argc, char ** argv) {
|
||||
if(argc != 4) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
fprintf(stderr, "Invalid appid");
|
||||
return EXIT_FAILURE;
|
||||
@@ -487,9 +380,9 @@ static int fomod(int argc, char ** argv) {
|
||||
|
||||
//might crash if no mods were installed
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||
free(home);
|
||||
GList * mods = listFilesInFolder(modFolder);
|
||||
GList * mods = order_listMods(appid);
|
||||
GList * modsFirstPointer = mods;
|
||||
|
||||
for(unsigned long i = 0; i < modId; i++) {
|
||||
@@ -523,7 +416,7 @@ static int fomod(int argc, char ** argv) {
|
||||
static int swapMod(int argc, char ** argv) {
|
||||
if(argc != 5) return usage();
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
fprintf(stderr, "Invalid appid");
|
||||
return EXIT_FAILURE;
|
||||
@@ -551,7 +444,7 @@ static int printLoadOrder(int argc, char ** argv) {
|
||||
if(argc != 3) return usage();
|
||||
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
fprintf(stderr, "Invalid appid");
|
||||
return EXIT_FAILURE;
|
||||
@@ -580,7 +473,7 @@ static int printPlugins(int argc, char ** argv) {
|
||||
if(argc != 3) return usage();
|
||||
|
||||
char * appIdStr = argv[2];
|
||||
int appid = validateAppId(appIdStr);
|
||||
int appid = steam_parseAppId(appIdStr);
|
||||
if(appid < 0) {
|
||||
fprintf(stderr, "Invalid appid");
|
||||
return EXIT_FAILURE;
|
||||
@@ -613,7 +506,7 @@ int main(int argc, char ** argv) {
|
||||
|
||||
int returnValue = EXIT_SUCCESS;
|
||||
char * home = getHome();
|
||||
char * configFolder = g_build_filename(home, MANAGER_FILES, NULL);
|
||||
char * configFolder = g_build_filename(home, MODLIB_WORKING_DIR, NULL);
|
||||
free(home);
|
||||
if(access(configFolder, F_OK) != 0) {
|
||||
|
||||
@@ -651,7 +544,7 @@ int main(int argc, char ** argv) {
|
||||
returnValue = installAndUninstallMod(argc, argv, false);
|
||||
|
||||
else if(strcmp(argv[1], "--bind") == 0 || strcmp(argv[1], "-d") == 0)
|
||||
returnValue = deploy(argc, argv);
|
||||
returnValue = cli_deploy(argc, argv);
|
||||
|
||||
else if(strcmp(argv[1], "--unbind") == 0)
|
||||
returnValue = unbind(argc, argv);
|
||||
@@ -0,0 +1,2 @@
|
||||
#define APP_NAME "mod-manager CLI"
|
||||
#define VERSION "0.1"
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#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);
|
||||
-405
@@ -1,405 +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 "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,28 +0,0 @@
|
||||
#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
|
||||
@@ -1,35 +0,0 @@
|
||||
#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,38 +0,0 @@
|
||||
#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
|
||||
@@ -1,14 +1,12 @@
|
||||
#ifndef __MAIN_H__
|
||||
#define __MAIN_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#define MODLIB_NAME "mod-manager"
|
||||
//no function should ever be put here, only keep important macros and globals here
|
||||
|
||||
#define APP_NAME "mod-manager"
|
||||
//relative to home the url is not preprocessed so ../ might create some issues
|
||||
// in c "A" "B" is the same as "AB"
|
||||
#define MANAGER_FILES ".local/share/" APP_NAME
|
||||
#define MODLIB_WORKING_DIR ".local/share/" MODLIB_NAME
|
||||
#define MOD_FOLDER_NAME "MOD_FOLDER"
|
||||
//the folder in which the games file will be linked or copied
|
||||
#define GAME_FOLDER_NAME "GAME_FOLDER"
|
||||
@@ -17,8 +15,5 @@
|
||||
//overlayfs temporary dir.
|
||||
#define GAME_WORK_DIR_NAME "WORK_DIRS"
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
typedef enum { ERR_SUCCESS, ERR_FAILURE } error_t;
|
||||
|
||||
#define MODLIB_VERSION "0.1"
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#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
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef __ERROR_TYPE_H__
|
||||
#define __ERROR_TYPE_H__
|
||||
|
||||
typedef enum { ERR_SUCCESS, ERR_FAILURE } error_t;
|
||||
|
||||
#endif
|
||||
@@ -2,8 +2,10 @@
|
||||
#define __FILE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <glib.h>
|
||||
#include <sys/types.h>
|
||||
#include "main.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
|
||||
//valid copy flags
|
||||
@@ -62,4 +64,6 @@ const char * file_extractExtension(const char * filePath);
|
||||
*/
|
||||
const char * file_extractFileName(const char * filePath);
|
||||
|
||||
|
||||
GList * file_listFolderContent(const char * path);
|
||||
#endif
|
||||
@@ -1,22 +1,17 @@
|
||||
#ifndef __FOMOD_H__
|
||||
#define __FOMOD_H__
|
||||
#ifndef __PUBLIC_FOMOD_H__
|
||||
#define __PUBLIC_FOMOD_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "errorType.h"
|
||||
#include <glib.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "fomod/parser.h"
|
||||
#include "fomod/group.h"
|
||||
#include "fomod/xmlUtil.h"
|
||||
#include "fomodTypes.h"
|
||||
|
||||
/**
|
||||
* @brief Start a terminal based install process for fomod.
|
||||
* @brief parse a fomod xml file (found inside the mod folder /fomod/moduleconfig.xml (everything should be lowercase))
|
||||
*
|
||||
* @param modFolder folder of the fomod file.
|
||||
* @param destination folder of the new mod that contains the result of the fomod process.
|
||||
* @return int
|
||||
* @param fomodFile path to the moduleconfig.xml
|
||||
* @param fomod pointer to a new FOMod_t
|
||||
*/
|
||||
error_t fomod_installFOMod(const char * modFolder, const char * destination);
|
||||
error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod);
|
||||
|
||||
/**
|
||||
* @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,71 @@
|
||||
#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
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef __DATA_PATH_H__
|
||||
#define __DATA_PATH_H__
|
||||
#ifndef __GAME_DATA_H__
|
||||
#define __GAME_DATA_H__
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include "errorType.h"
|
||||
|
||||
/**
|
||||
* @brief Get the path to the data folder
|
||||
@@ -9,7 +10,6 @@
|
||||
* @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);
|
||||
|
||||
error_t gameData_getDataPath(int appid, char ** destination);
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
#ifndef __GET_HOME_H__
|
||||
#define __GET_HOME_H__
|
||||
|
||||
/**
|
||||
* @brief Get the user home dir regardless if he used sudo.
|
||||
@@ -6,3 +7,5 @@
|
||||
* @return char* path to the home dir
|
||||
*/
|
||||
char * getHome(void);
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,9 @@
|
||||
#ifndef __INSTALL_H__
|
||||
#define __INSTALL_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "main.h"
|
||||
#include <errorType.h>
|
||||
|
||||
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
|
||||
|
||||
/**
|
||||
@@ -2,7 +2,7 @@
|
||||
#define __LOAD_ORDER_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "main.h"
|
||||
#include <errorType.h>
|
||||
|
||||
/**
|
||||
* @brief find all plugins in the data folder of the game
|
||||
@@ -2,7 +2,7 @@
|
||||
#define __ORDER_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "main.h"
|
||||
#include <errorType.h>
|
||||
|
||||
#define ORDER_FILE "__ORDER__"
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#ifndef __STEAM_H__
|
||||
#define __STEAM_H__
|
||||
|
||||
#include "macro.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
#include <errorType.h>
|
||||
|
||||
typedef struct steam_App {
|
||||
unsigned int appid;
|
||||
@@ -43,7 +41,8 @@ static const char * GAMES_NAMES[] = {
|
||||
"Morrowind"
|
||||
};
|
||||
|
||||
_Static_assert(LEN(GAMES_APPIDS) == LEN(GAMES_NAMES), "Game APPIDS and Game Names doesn't match");
|
||||
_Static_assert(sizeof(GAMES_NAMES) / sizeof(GAMES_NAMES[0]) == sizeof(GAMES_APPIDS) / sizeof(GAMES_APPIDS[0]),
|
||||
"Game APPIDS and Game Names doesn't match");
|
||||
|
||||
/**
|
||||
* @brief list all installed games and the paths to the game's files
|
||||
@@ -64,4 +63,12 @@ void steam_freeGameTable(void);
|
||||
*/
|
||||
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
|
||||
@@ -0,0 +1,109 @@
|
||||
#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;
|
||||
}
|
||||
@@ -150,3 +150,21 @@ const char * file_extractExtension(const char * filePath) {
|
||||
const char * file_extractFileName(const char * 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;
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
#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));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#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,5 +1,4 @@
|
||||
#include "parser.h"
|
||||
#include "fomodTypes.h"
|
||||
#include <fomod.h>
|
||||
#include "group.h"
|
||||
#include "libxml/tree.h"
|
||||
#include "xmlUtil.h"
|
||||
@@ -193,6 +192,58 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
||||
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) {
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr cur;
|
||||
@@ -281,6 +332,14 @@ error_t parser_parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
||||
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);
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
@@ -3,9 +3,7 @@
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef enum FOModOrder { ASC, DESC, ORD } fomod_Order_t;
|
||||
#include <fomod.h>
|
||||
|
||||
/**
|
||||
* @brief
|
||||
@@ -1,14 +1,14 @@
|
||||
|
||||
#include "getDataPath.h"
|
||||
#include "main.h"
|
||||
#include "steam.h"
|
||||
#include <constants.h>
|
||||
#include <gameData.h>
|
||||
#include <steam.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
error_t getDataPath(int appid, char ** destination) {
|
||||
error_t gameData_getDataPath(int appid, char ** destination) {
|
||||
GHashTable * gamePaths;
|
||||
error_t status = steam_searchGames(&gamePaths);
|
||||
if(status == ERR_FAILURE) {
|
||||
@@ -4,8 +4,9 @@
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "install.h"
|
||||
#include "main.h"
|
||||
#include <install.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include "archives.h"
|
||||
#include "file.h"
|
||||
|
||||
@@ -17,7 +18,7 @@ error_t install_addMod(char * filePath, int appId) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char * configFolder = g_build_filename(getenv("HOME"), MANAGER_FILES, NULL);
|
||||
char * configFolder = g_build_filename(getenv("HOME"), MODLIB_WORKING_DIR, NULL);
|
||||
if(g_mkdir_with_parents(configFolder, 0755) < 0) {
|
||||
fprintf(stderr, "Could not create mod folder");
|
||||
resultError = ERR_FAILURE;
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <constants.h>
|
||||
|
||||
#include "loadOrder.h"
|
||||
#include "main.h"
|
||||
#include "steam.h"
|
||||
#include "file.h"
|
||||
|
||||
@@ -8,7 +9,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "getDataPath.h"
|
||||
#include "gameData.h"
|
||||
|
||||
//TODO: detect if the game is running
|
||||
//TODO: deploy the game
|
||||
@@ -21,7 +22,7 @@ error_t order_listPlugins(int appid, GList ** plugins) {
|
||||
sprintf(appidStr, "%d", appid);
|
||||
|
||||
char * dataFolder = NULL;
|
||||
error_t error = getDataPath(appid, &dataFolder);
|
||||
error_t error = gameData_getDataPath(appid, &dataFolder);
|
||||
if(error != ERR_SUCCESS) {
|
||||
return ERR_FAILURE;
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
#include "order.h"
|
||||
|
||||
//no function are declared in main it's just macros
|
||||
#include "main.h"
|
||||
#include <constants.h>
|
||||
|
||||
#include "getHome.h"
|
||||
|
||||
typedef struct Mod {
|
||||
@@ -27,7 +28,7 @@ GList * order_listMods(int appid) {
|
||||
sprintf(appidStr, "%d", appid);
|
||||
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appidStr, NULL);
|
||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appidStr, NULL);
|
||||
free(home);
|
||||
|
||||
GList * list = NULL;
|
||||
@@ -99,7 +100,7 @@ error_t order_swapPlace(int appid, int modIdA, int modIdB) {
|
||||
sprintf(appidStr, "%d", appid);
|
||||
|
||||
char * home = getHome();
|
||||
char * modFolder = g_build_filename(home, MANAGER_FILES, MOD_FOLDER_NAME, appidStr, NULL);
|
||||
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appidStr, NULL);
|
||||
free(home);
|
||||
|
||||
GList * list = order_listMods(appid);
|
||||
@@ -16,7 +16,7 @@ overlay_errors_t overlay_mount(char ** sources, const char * dest, const char *
|
||||
int pid = fork();
|
||||
if(pid == 0) {
|
||||
//exit is used to get the return value when using waitpid
|
||||
exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-r", "-o", mountData, dest, NULL));
|
||||
exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-o", mountData, dest, NULL));
|
||||
} else {
|
||||
|
||||
int returnValue = 0;
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "steam.h"
|
||||
#include "macro.h"
|
||||
#include "getHome.h"
|
||||
#include "main.h"
|
||||
|
||||
#include <constants.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -255,3 +257,33 @@ int steam_gameIdFromAppId(u_int32_t appid) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user