Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fce1c2912c | |||
| 0c2b3afa9b | |||
| 327486d897 | |||
| 1017d47312 | |||
| b8affcadbc | |||
| 9ee12a6c7e | |||
| f3cd47b2bb | |||
| 1abef49712 | |||
| 4ce99996e8 | |||
| 091ab3894c | |||
| b6d11aa9ee | |||
| 67a68011ed |
+60
-21
@@ -9,44 +9,83 @@ 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 -fsanitize=address -Werror -O0")
|
set(CMAKE_C_FLAGS_DEBUG "-g -Werror -O0")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "-O2")
|
set(CMAKE_C_FLAGS_RELEASE "")
|
||||||
|
|
||||||
# generate the compile_commands for vscode / clang
|
# generate the compile_commands for vscode / clang
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
||||||
|
|
||||||
# set the project name
|
# set the project name
|
||||||
project(ModManager)
|
project(mod-manager)
|
||||||
|
|
||||||
|
|
||||||
set(SOURCES
|
set(LIB_SOURCES
|
||||||
src/steam.c
|
src/lib/steam.c
|
||||||
src/install.c
|
src/lib/loadOrder.c
|
||||||
src/overlayfs.c
|
src/lib/install.c
|
||||||
src/getHome.c
|
src/lib/overlayfs.c
|
||||||
src/file.c
|
src/lib/gameData.c
|
||||||
src/order.c
|
src/lib/getHome.c
|
||||||
src/fomod.c
|
src/lib/file.c
|
||||||
src/fomod/group.c
|
src/lib/order.c
|
||||||
src/fomod/xmlUtil.c
|
src/lib/archives.c
|
||||||
src/fomod/parser.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 the executable
|
||||||
add_executable(ModManager 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)
|
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)
|
||||||
|
|
||||||
target_include_directories(ModManager 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_include_directories(mod-manager PRIVATE ${GLIB_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(ModManager ${GLIB_LDFLAGS})
|
install(TARGETS mod-manager OPTIONAL DESTINATION bin)
|
||||||
target_link_libraries(ModManager ${LIBXML_LIBRARIES})
|
|
||||||
target_link_libraries(ModManager ${AUDIT_LIBRARIES})
|
|
||||||
|
|
||||||
install(TARGETS ModManager DESTINATION bin)
|
install(TARGETS libmod-manager
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
PUBLIC_HEADER DESTINATION include/mod-manager
|
||||||
|
)
|
||||||
|
|
||||||
set_property(TARGET ModManager 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 "")
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
* Add tests
|
||||||
|
* Add safe interrupt support
|
||||||
|
* Add a gui in an external binary
|
||||||
|
* make the CLI into a separate binary
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Maintainer: Marc barbier
|
||||||
|
pkgname=modmanager
|
||||||
|
pkgver=0.1
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="a Mod manager for bethesda games"
|
||||||
|
arch=('x86_64') # might work on other archs but i can not try
|
||||||
|
url='https://gitlab.marcbarbier.fr/Marc/modmanager'
|
||||||
|
license=('GPL2')
|
||||||
|
source=( 'git+https://github.com/Marc-Pierre-Barbier/ModManager.git' )
|
||||||
|
md5sums=( 'SKIP' )
|
||||||
|
|
||||||
|
optdepends=('fuse-overlayfs: special filesystem support' )
|
||||||
|
|
||||||
|
depends=( 'glib2' 'unrar' 'p7zip' 'unzip' )
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cmake -B build -S "${pkgname}" \
|
||||||
|
-DCMAKE_BUILD_TYPE='None' \
|
||||||
|
-DCMAKE_INSTALL_PREFIX='/usr' \
|
||||||
|
-Wno-dev
|
||||||
|
cmake --build build
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
DESTDIR="$pkgdir" cmake --install build
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Current naming conventions (subject to debate)
|
||||||
|
|
||||||
|
## Function names
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
all static functions should be in camelCase and they should not mention their module.
|
||||||
|
|
||||||
|
## Struct names
|
||||||
|
|
||||||
|
function should start by the name of their module in camelCase followed by an underscore and the name of the struct in PascalCase
|
||||||
+185
-257
@@ -1,6 +1,5 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <glib.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -9,93 +8,63 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <libaudit.h>
|
#include <libaudit.h>
|
||||||
|
|
||||||
#include "overlayfs.h"
|
#include <gameData.h>
|
||||||
#include "install.h"
|
#include <loadOrder.h>
|
||||||
#include "getHome.h"
|
#include <deploy.h>
|
||||||
#include "steam.h"
|
#include <steam.h>
|
||||||
#include "main.h"
|
#include <install.h>
|
||||||
#include "file.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 "fomod.h"
|
||||||
#include "order.h"
|
|
||||||
|
|
||||||
#define ENABLED_COLOR "\033[0;32m"
|
#define ENABLED_COLOR "\033[0;32m"
|
||||||
#define DISABLED_COLOR "\033[0;31m"
|
#define DISABLED_COLOR "\033[0;31m"
|
||||||
|
|
||||||
GHashTable * gamePaths;
|
|
||||||
|
|
||||||
static bool isRoot() {
|
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 noRoot() {
|
|
||||||
fprintf(stderr, "Don't run this argument as root\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int needRoot() {
|
|
||||||
fprintf(stderr, "Root is needed to bind with the game files\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usage() {
|
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");
|
||||||
|
|
||||||
|
printf("Use --bind or -d <APPID> to deploy the mods for the game\n");
|
||||||
|
printf("Use --unbind <APPID> to rollback a deployment\n");
|
||||||
|
|
||||||
printf("Use --add or -a <APPID> <FILENAME> to add a mod to a game\n");
|
printf("Use --add or -a <APPID> <FILENAME> to add a mod to a game\n");
|
||||||
|
printf("Use --remove or -r <APPID> <MODID> to remove a mod\n");
|
||||||
|
printf("Use --fomod <APPID> <MODID> to create a new mod using the result from the FOMod\n");
|
||||||
|
|
||||||
printf("Use --list-mods or -m <APPID> to list all mods for a game\n");
|
printf("Use --list-mods or -m <APPID> to list all mods for a game\n");
|
||||||
printf("Use --install or -i <APPID> <MODID> to add a mod to a game\n");
|
printf("Use --install or -i <APPID> <MODID> to add a mod to a game\n");
|
||||||
printf("Use --uninstall or -u <APPID> <MODID> to uninstall a mod from a game\n");
|
printf("Use --uninstall or -u <APPID> <MODID> to uninstall a mod from a game\n");
|
||||||
printf("Use --remove or -r <APPID> <MODID> to remove a mod\n");
|
|
||||||
printf("Use --deploy or -d <APPID> to deploy the mods for the game\n");
|
|
||||||
printf("Use --unbind <APPID> to rollback a deployment\n");
|
|
||||||
printf("Use --fomod <APPID> <MODID> to create a new mod using the result from the FOMod\n");
|
|
||||||
printf("Use --swap <APPID> <MODID A> MODID B> to swap two mod in the loading order\n");
|
printf("Use --swap <APPID> <MODID A> MODID B> to swap two mod in the loading order\n");
|
||||||
|
|
||||||
printf("Use --version or -v to get the version number\n");
|
printf("Use --version or -v to get the version number\n");
|
||||||
|
|
||||||
|
printf("Use --show-load-order <APPID>\n");
|
||||||
|
printf("Use --list-plugins <APPID>\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int validateAppId(const char * appIdStr) {
|
|
||||||
char * strtoulSentinel;
|
|
||||||
//strtoul set EINVAL(after C99) if the string is invalid
|
|
||||||
unsigned long appid = strtoul(appIdStr, &strtoulSentinel, 10);
|
|
||||||
if(errno == EINVAL || strtoulSentinel == appIdStr) {
|
|
||||||
fprintf(stderr, "Appid has to be a valid number\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gameId = getGameIdFromAppId((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();
|
||||||
|
|
||||||
|
GHashTable * gamePaths;
|
||||||
|
error_t status = steam_searchGames(&gamePaths);
|
||||||
|
if(status == ERR_FAILURE) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
GList * gamesIds = g_hash_table_get_keys(gamePaths);
|
GList * gamesIds = g_hash_table_get_keys(gamePaths);
|
||||||
GList * gamesIdsFirstPointer = gamesIds;
|
GList * gamesIdsFirstPointer = gamesIds;
|
||||||
if(g_list_length(gamesIds) == 0) {
|
if(g_list_length(gamesIds) == 0) {
|
||||||
@@ -108,36 +77,36 @@ static int listGames(int argc, char **) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_list_free(gamesIdsFirstPointer);
|
g_list_free(gamesIdsFirstPointer);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add(int argc, char ** argv) {
|
static int add(int argc, char ** argv) {
|
||||||
if(argc != 4) return usage();
|
if(argc != 4) return usage();
|
||||||
const char * appIdStr = argv[2];
|
const char * appIdStr = argv[2];
|
||||||
|
|
||||||
int appid = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
addMod(argv[3], appid);
|
install_addMod(argv[3], appid);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(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, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
|
|
||||||
GList * mods = listMods(appid);
|
GList * mods = order_listMods(appid);
|
||||||
GList * p_mods = mods;
|
GList * p_mods = mods;
|
||||||
unsigned short index = 0;
|
unsigned short index = 0;
|
||||||
|
|
||||||
@@ -173,7 +142,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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -189,9 +158,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, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
GList * mods = listFilesInFolder(modFolder);
|
GList * mods = order_listMods(appid);
|
||||||
GList * modsFirstPointer = mods;
|
GList * modsFirstPointer = mods;
|
||||||
|
|
||||||
for(unsigned long i = 0; i < modId; i++) {
|
for(unsigned long i = 0; i < modId; i++) {
|
||||||
@@ -245,146 +214,86 @@ exit:
|
|||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int deploy(int argc, char ** argv) {
|
static error_t cli_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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * home = getHome();
|
GList * ignoredMods = NULL;
|
||||||
char * gameFolder = g_build_filename(home, MANAGER_FILES, GAME_FOLDER_NAME, appIdStr, NULL);
|
|
||||||
|
|
||||||
if(access(gameFolder, F_OK) != 0) {
|
deploymentErrors_t errors = deploy(appIdStr, &ignoredMods);
|
||||||
free(home);
|
switch(errors) {
|
||||||
g_free(gameFolder);
|
//we prevalidate the appid so it is a bug
|
||||||
printf("Please run '%s --setup %d' first", APP_NAME, appid);
|
default:
|
||||||
return EXIT_FAILURE;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//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");
|
|
||||||
int gameId = getGameIdFromAppId(appid);
|
|
||||||
const char * path = g_hash_table_lookup(gamePaths, &gameId);
|
|
||||||
char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL);
|
|
||||||
|
|
||||||
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(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0);
|
|
||||||
int status = overlayMount(modsToInstall, steamGameFolder, gameUpperDir, gameWorkDir);
|
|
||||||
if(status == 0) {
|
|
||||||
printf("Everything is ready, just launch the game\n");
|
|
||||||
} else if(status == -1) {
|
|
||||||
fprintf(stderr, "Could not mount the mods overlay, try to install fuse-overlay\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else if(status == -2) {
|
|
||||||
fprintf(stderr, "Could not mount the mods overlay\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "%d bug detected, please report this.\n", __LINE__);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(steamGameFolder);
|
|
||||||
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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
int gameId = getGameIdFromAppId(appid);
|
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
|
|
||||||
char * gameUpperDir = g_build_filename(home, MANAGER_FILES, GAME_UPPER_DIR_NAME, appIdStr, NULL);
|
char * gameUpperDir = g_build_filename(home, MODLIB_WORKING_DIR, GAME_UPPER_DIR_NAME, appIdStr, NULL);
|
||||||
char * gameWorkDir = g_build_filename(home, MANAGER_FILES, GAME_WORK_DIR_NAME, appIdStr, NULL);
|
char * gameWorkDir = g_build_filename(home, MODLIB_WORKING_DIR, 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);
|
||||||
|
|
||||||
const char * path = g_hash_table_lookup(gamePaths, &gameId);
|
char * dataFolder = NULL;
|
||||||
char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL);
|
error_t error = 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(access(gameFolder, F_OK) == 0) {
|
||||||
//if the game folder alredy exists just delete it
|
//if the game folder alredy exists just delete it
|
||||||
//this will allow the removal of dlcs and language change
|
//this will allow the removal of dlcs and language change
|
||||||
delete(gameFolder, true);
|
file_delete(gameFolder, true);
|
||||||
}
|
}
|
||||||
g_mkdir_with_parents(gameFolder, 0755);
|
g_mkdir_with_parents(gameFolder, 0755);
|
||||||
|
|
||||||
//links don't conflict with overlayfs and avoid coping 17Gb of files.
|
//links don't conflict with overlayfs and avoid coping 17Gb of files.
|
||||||
//but links require the files to be on the same filesystem
|
//but links require the files to be on the same filesystem
|
||||||
int returnValue = copy(steamGameFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR | CP_LINK);
|
int returnValue = file_copy(dataFolder, gameFolder, FILE_CP_RECURSIVE | FILE_CP_NO_TARGET_DIR | FILE_CP_LINK);
|
||||||
if(returnValue < 0) {
|
if(returnValue < 0) {
|
||||||
printf("Coping game files. HINT: having the game on the same partition as you home director will make this operation use zero extra space");
|
printf("Coping game files. HINT: having the game on the same partition as you home director will make this operation use zero extra space");
|
||||||
returnValue = copy(steamGameFolder, gameFolder, CP_RECURSIVE | CP_NO_TARGET_DIR);
|
returnValue = file_copy(dataFolder, gameFolder, FILE_CP_RECURSIVE | FILE_CP_NO_TARGET_DIR);
|
||||||
if(returnValue < 0) {
|
if(returnValue < 0) {
|
||||||
fprintf(stderr, "Copy failed make sure you have enough space on your device.");
|
fprintf(stderr, "Copy failed make sure you have enough space on your device.");
|
||||||
free(steamGameFolder);
|
free(dataFolder);
|
||||||
free(gameFolder);
|
free(gameFolder);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
casefold(gameFolder);
|
file_casefold(gameFolder);
|
||||||
|
|
||||||
free(steamGameFolder);
|
free(dataFolder);
|
||||||
free(gameFolder);
|
free(gameFolder);
|
||||||
printf("Done\n");
|
printf("Done\n");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@@ -394,24 +303,27 @@ 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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
int gameId = getGameIdFromAppId(appid);
|
|
||||||
const char * path = g_hash_table_lookup(gamePaths, &gameId);
|
|
||||||
char * steamGameFolder = g_build_path("/", path, "steamapps/common", GAMES_NAMES[gameId], "Data", NULL);
|
|
||||||
|
|
||||||
while(umount2(steamGameFolder, MNT_FORCE | MNT_DETACH) == 0);
|
char * dataFolder = NULL;
|
||||||
|
error_t error = gameData_getDataPath(appid, &dataFolder);
|
||||||
|
if(error != ERR_SUCCESS) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
free(steamGameFolder);
|
while(umount2(dataFolder, MNT_FORCE | MNT_DETACH) == 0);
|
||||||
|
|
||||||
|
free(dataFolder);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -426,9 +338,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, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
GList * mods = listFilesInFolder(modFolder);
|
GList * mods = order_listMods(appid);
|
||||||
GList * modsFirstPointer = mods;
|
GList * modsFirstPointer = mods;
|
||||||
|
|
||||||
for(unsigned long i = 0; i < modId; i++) {
|
for(unsigned long i = 0; i < modId; i++) {
|
||||||
@@ -442,7 +354,7 @@ static int removeMod(int argc, char ** argv) {
|
|||||||
|
|
||||||
gchar * filename = g_build_filename(modFolder, mods->data, NULL);
|
gchar * filename = g_build_filename(modFolder, mods->data, NULL);
|
||||||
|
|
||||||
delete(filename, true);
|
file_delete(filename, true);
|
||||||
|
|
||||||
g_free(filename);
|
g_free(filename);
|
||||||
g_free(modFolder);
|
g_free(modFolder);
|
||||||
@@ -453,7 +365,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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -468,9 +380,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, MANAGER_FILES, MOD_FOLDER_NAME, appIdStr, NULL);
|
char * modFolder = g_build_filename(home, MODLIB_WORKING_DIR, MOD_FOLDER_NAME, appIdStr, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
GList * mods = listFilesInFolder(modFolder);
|
GList * mods = order_listMods(appid);
|
||||||
GList * modsFirstPointer = mods;
|
GList * modsFirstPointer = mods;
|
||||||
|
|
||||||
for(unsigned long i = 0; i < modId; i++) {
|
for(unsigned long i = 0; i < modId; i++) {
|
||||||
@@ -485,13 +397,13 @@ static int fomod(int argc, char ** argv) {
|
|||||||
char * destination = g_strconcat(mods->data, "__FOMOD", NULL);
|
char * destination = g_strconcat(mods->data, "__FOMOD", NULL);
|
||||||
|
|
||||||
if(access(destination, F_OK) == 0) {
|
if(access(destination, F_OK) == 0) {
|
||||||
delete(destination, true);
|
file_delete(destination, true);
|
||||||
}
|
}
|
||||||
char * modDestination = g_build_filename(modFolder, destination, NULL);
|
char * modDestination = g_build_filename(modFolder, destination, NULL);
|
||||||
char * modPath = g_build_filename(modFolder, mods->data, NULL);
|
char * modPath = g_build_filename(modFolder, mods->data, NULL);
|
||||||
|
|
||||||
//TODO: add error handling
|
//TODO: add error handling
|
||||||
int returnValue = installFOMod(modPath, modDestination);
|
int returnValue = fomod_installFOMod(modPath, modDestination);
|
||||||
|
|
||||||
free(destination);
|
free(destination);
|
||||||
g_list_free_full(modsFirstPointer, free);
|
g_list_free_full(modsFirstPointer, free);
|
||||||
@@ -504,7 +416,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 = validateAppId(appIdStr);
|
int appid = steam_parseAppId(appIdStr);
|
||||||
if(appid < 0) {
|
if(appid < 0) {
|
||||||
fprintf(stderr, "Invalid appid");
|
fprintf(stderr, "Invalid appid");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -525,30 +437,76 @@ static int swapMod(int argc, char ** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printf("%d, %d\n", modIdA, modIdB);
|
printf("%d, %d\n", modIdA, modIdB);
|
||||||
return swapPlace(appid, modIdA, modIdB);
|
return order_swapPlace(appid, modIdA, modIdB);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int printLoadOrder(int argc, char ** argv) {
|
||||||
|
if(argc != 3) return usage();
|
||||||
|
|
||||||
|
char * appIdStr = argv[2];
|
||||||
|
int appid = steam_parseAppId(appIdStr);
|
||||||
|
if(appid < 0) {
|
||||||
|
fprintf(stderr, "Invalid appid");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * order = NULL;
|
||||||
|
error_t status = order_getLoadOrder(appid, &order);
|
||||||
|
if(status != ERR_SUCCESS) {
|
||||||
|
fprintf(stderr, "Could not fetch the load order\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("If the load order is empty that mean it wasn't set\n");
|
||||||
|
|
||||||
|
GList * cur_order = order;
|
||||||
|
while (cur_order != NULL) {
|
||||||
|
printf("%s\n", (char *)cur_order->data);
|
||||||
|
cur_order = g_list_next(cur_order);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free_full(order, free);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int printPlugins(int argc, char ** argv) {
|
||||||
|
if(argc != 3) return usage();
|
||||||
|
|
||||||
|
char * appIdStr = argv[2];
|
||||||
|
int appid = steam_parseAppId(appIdStr);
|
||||||
|
if(appid < 0) {
|
||||||
|
fprintf(stderr, "Invalid appid");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * mods = NULL;
|
||||||
|
error_t status = order_listPlugins(appid, &mods);
|
||||||
|
if(status != ERR_SUCCESS) {
|
||||||
|
fprintf(stderr, "Could not list plugins\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * cur_mods = mods;
|
||||||
|
while (cur_mods != NULL) {
|
||||||
|
printf("%s\n", (char *)cur_mods->data);
|
||||||
|
cur_mods = g_list_next(cur_mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free_full(mods, free);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char ** argv) {
|
int main(int argc, char ** argv) {
|
||||||
if(argc < 2 ) return usage();
|
if(argc < 2 ) return usage();
|
||||||
|
|
||||||
if(audit_getloginuid() == 0) {
|
if(audit_getloginuid() == 0 || isRoot()) {
|
||||||
fprintf(stderr, "The root user should not be using this\n");
|
fprintf(stderr, "Please don't run this software as root\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int searchStatus;
|
|
||||||
gamePaths = search_games(&searchStatus);
|
|
||||||
|
|
||||||
|
|
||||||
int returnValue = EXIT_SUCCESS;
|
int returnValue = EXIT_SUCCESS;
|
||||||
if(searchStatus == EXIT_FAILURE) {
|
|
||||||
returnValue = EXIT_FAILURE;
|
|
||||||
fprintf(stderr, "Error while looking up libraries, do you have steam installed ?");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
char * configFolder = g_build_filename(home, MANAGER_FILES, NULL);
|
char * configFolder = g_build_filename(home, MODLIB_WORKING_DIR, NULL);
|
||||||
free(home);
|
free(home);
|
||||||
if(access(configFolder, F_OK) != 0) {
|
if(access(configFolder, F_OK) != 0) {
|
||||||
|
|
||||||
@@ -559,85 +517,57 @@ int main(int argc, char ** argv) {
|
|||||||
if(getuid() == 0) {
|
if(getuid() == 0) {
|
||||||
fprintf(stderr, "For the first run please avoid sudo\n");
|
fprintf(stderr, "For the first run please avoid sudo\n");
|
||||||
returnValue = EXIT_FAILURE;
|
returnValue = EXIT_FAILURE;
|
||||||
goto exit2;
|
goto exit;
|
||||||
} else {
|
} else {
|
||||||
//leading 0 == octal
|
//leading 0 == octal
|
||||||
int i = g_mkdir_with_parents(configFolder, 0755);
|
int i = g_mkdir_with_parents(configFolder, 0755);
|
||||||
if(i < 0) {
|
if(i < 0) {
|
||||||
fprintf(stderr, "Could not create configs, check access rights for this path: %s", configFolder);
|
fprintf(stderr, "Could not create configs, check access rights for this path: %s", configFolder);
|
||||||
goto exit2;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0)
|
||||||
if(strcmp(argv[1], "--list-games") == 0 || strcmp(argv[1], "-l") == 0) {
|
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = listGames(argc, argv);
|
returnValue = listGames(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--add") == 0 || strcmp(argv[1], "-a") == 0) {
|
else if(strcmp(argv[1], "--add") == 0 || strcmp(argv[1], "-a") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = add(argc, argv);
|
returnValue = add(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--list-mods") == 0 || strcmp(argv[1], "-m") == 0) {
|
else if(strcmp(argv[1], "--list-mods") == 0 || strcmp(argv[1], "-m") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = listAllMods(argc, argv);
|
returnValue = listAllMods(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--install") == 0 || strcmp(argv[1], "-i") == 0) {
|
else if(strcmp(argv[1], "--install") == 0 || strcmp(argv[1], "-i") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = installAndUninstallMod(argc, argv, true);
|
returnValue = installAndUninstallMod(argc, argv, true);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0) {
|
else if(strcmp(argv[1], "--uninstall") == 0 || strcmp(argv[1], "-u") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = installAndUninstallMod(argc, argv, false);
|
returnValue = installAndUninstallMod(argc, argv, false);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--deploy") == 0 || strcmp(argv[1], "-d") == 0) {
|
else if(strcmp(argv[1], "--bind") == 0 || strcmp(argv[1], "-d") == 0)
|
||||||
if(!isRoot())
|
returnValue = cli_deploy(argc, argv);
|
||||||
returnValue = needRoot();
|
|
||||||
else
|
|
||||||
returnValue = deploy(argc, argv);
|
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--unbind") == 0){
|
else if(strcmp(argv[1], "--unbind") == 0)
|
||||||
if(!isRoot())
|
|
||||||
returnValue = needRoot();
|
|
||||||
else
|
|
||||||
returnValue = unbind(argc, argv);
|
returnValue = unbind(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--setup") == 0) {
|
else if(strcmp(argv[1], "--setup") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = setup(argc, argv);
|
returnValue = setup(argc, argv);
|
||||||
} else if(strcmp(argv[1], "--fomod") == 0){
|
|
||||||
if(isRoot())
|
else if(strcmp(argv[1], "--fomod") == 0)
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = fomod(argc, argv);
|
returnValue = fomod(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--remove") == 0 || strcmp(argv[1], "-r") == 0) {
|
else if(strcmp(argv[1], "--remove") == 0 || strcmp(argv[1], "-r") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = removeMod(argc, argv);
|
returnValue = removeMod(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0) {
|
else if(strcmp(argv[1], "--swap") == 0 || strcmp(argv[1], "-s") == 0)
|
||||||
if(isRoot())
|
|
||||||
returnValue = noRoot();
|
|
||||||
else
|
|
||||||
returnValue = swapMod(argc, argv);
|
returnValue = swapMod(argc, argv);
|
||||||
|
|
||||||
} else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) {
|
else if(strcmp(argv[1], "--list-plugins") == 0)
|
||||||
returnValue = EXIT_SUCCESS;
|
returnValue = printPlugins(argc, argv);
|
||||||
|
|
||||||
|
else if(strcmp(argv[1], "--show-load-order") == 0)
|
||||||
|
returnValue = printLoadOrder(argc, argv);
|
||||||
|
|
||||||
|
else if(strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0) {
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
printf("%s: Clang: %d.%d.%d\n", VERSION, __clang_major__, __clang_minor__, __clang_patchlevel__);
|
printf("%s: Clang: %d.%d.%d\n", VERSION, __clang_major__, __clang_minor__, __clang_patchlevel__);
|
||||||
#elifdef __GNUC__
|
#elifdef __GNUC__
|
||||||
@@ -645,15 +575,13 @@ int main(int argc, char ** argv) {
|
|||||||
#else
|
#else
|
||||||
printf("%s: unknown compiler\n", VERSION);
|
printf("%s: unknown compiler\n", VERSION);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
usage();
|
usage();
|
||||||
returnValue = EXIT_FAILURE;
|
returnValue = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
exit2:
|
|
||||||
g_free(configFolder);
|
|
||||||
g_hash_table_destroy(gamePaths);
|
|
||||||
exit:
|
exit:
|
||||||
|
g_free(configFolder);
|
||||||
|
steam_freeGameTable();
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifndef __FILE_H__
|
|
||||||
#define __FILE_H__
|
|
||||||
|
|
||||||
|
|
||||||
//valid copy flags
|
|
||||||
#define cp_DEFAULT 0
|
|
||||||
#define CP_LINK 1
|
|
||||||
#define CP_RECURSIVE 2
|
|
||||||
#define CP_NO_TARGET_DIR 4
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief execute the cp command from the source to the dest
|
|
||||||
* @param source path to the source files
|
|
||||||
* @param dest path to the destination folder
|
|
||||||
* @param flags refer to the "valid copy flags" use them like this CP_LINK | CP_RECURSIVE
|
|
||||||
* @return status code
|
|
||||||
*/
|
|
||||||
int copy(const char * source, const char * dest, u_int32_t flags);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief execute the cp command from the source to the dest
|
|
||||||
* @param source path to the source files
|
|
||||||
* @param dest path to the destination folder
|
|
||||||
* @param bool enable recursive rm -r
|
|
||||||
* @return status code
|
|
||||||
*/
|
|
||||||
int delete(const char * path, bool recursive);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run the mv command
|
|
||||||
* @param source
|
|
||||||
* @param destination
|
|
||||||
* @return mv's exit value
|
|
||||||
*/
|
|
||||||
int move(const char * source, const char * destination);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Recursively rename all file and folder to lowercase.
|
|
||||||
*
|
|
||||||
* @param folder
|
|
||||||
*/
|
|
||||||
void casefold(const char * folder);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
-355
@@ -1,355 +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 "libxml/globals.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 FOModFile_t * fileA = (const FOModFile_t *)a;
|
|
||||||
const FOModFile_t * fileB = (const FOModFile_t *)b;
|
|
||||||
|
|
||||||
return fileB->priority - fileA->priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printfOptionsInOrder(FOModGroup_t group) {
|
|
||||||
for(int i = 0; i < group.pluginCount; i++) {
|
|
||||||
printf("%d, %s\n", i, group.plugins[i].name);
|
|
||||||
printf("%s\n", group.plugins[i].description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static gint flagEqual(const FOModFlag_t * a, const FOModFlag_t * b) {
|
|
||||||
int nameCmp = strcmp(a->name, b->name);
|
|
||||||
if(nameCmp == 0) {
|
|
||||||
if(strcmp(a->value, b->value) == 0)
|
|
||||||
//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 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 groupCmpAsc(const void * stepA, const void * stepB) {
|
|
||||||
const FOModGroup_t * step1 = (const FOModGroup_t *)stepA;
|
|
||||||
const FOModGroup_t * step2 = (const FOModGroup_t *)stepB;
|
|
||||||
return strcmp(step1->name, step2->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int groupCmpDesc(const void * stepA, const void * stepB) {
|
|
||||||
const FOModGroup_t * step1 = (const FOModGroup_t *)stepA;
|
|
||||||
const FOModGroup_t * step2 = (const FOModGroup_t *)stepB;
|
|
||||||
return 1 - strcmp(step1->name, step2->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sortGroup(FOModGroup_t * group) {
|
|
||||||
switch(group->order) {
|
|
||||||
case ASC:
|
|
||||||
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpAsc);
|
|
||||||
break;
|
|
||||||
case DESC:
|
|
||||||
qsort(group->plugins, group->pluginCount, sizeof(*group->plugins), groupCmpDesc);
|
|
||||||
break;
|
|
||||||
case ORD:
|
|
||||||
//ord mean that we keep the curent order, so no need to sort anything.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: handle error
|
|
||||||
int processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination) {
|
|
||||||
//priority higher a less important and should be processed first.
|
|
||||||
*pendingFileOperations = g_list_sort(*pendingFileOperations, priorityCmp);
|
|
||||||
GList * currentFileOperation = *pendingFileOperations;
|
|
||||||
|
|
||||||
while(currentFileOperation != NULL) {
|
|
||||||
//TODO: support destination
|
|
||||||
//no using link since priority is made to override files and link is annoying to deal with when overriding files.
|
|
||||||
const FOModFile_t * file = (const FOModFile_t *)currentFileOperation->data;
|
|
||||||
char * source = g_build_path("/", modFolder, file->source, NULL);
|
|
||||||
|
|
||||||
//fix the / and \ windows - unix paths
|
|
||||||
fixPath(source);
|
|
||||||
|
|
||||||
int copyResult;
|
|
||||||
if(file->isFolder) {
|
|
||||||
copyResult = copy(source, destination, CP_NO_TARGET_DIR | CP_RECURSIVE);
|
|
||||||
} else {
|
|
||||||
copyResult = copy(source, destination, 0);
|
|
||||||
}
|
|
||||||
if(copyResult != EXIT_SUCCESS) {
|
|
||||||
fprintf(stderr, "Copy failed, some file might be corrupted\n");
|
|
||||||
}
|
|
||||||
g_free(source);
|
|
||||||
|
|
||||||
currentFileOperation = g_list_next(currentFileOperation);
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) {
|
|
||||||
for(int condId = 0; condId < fomod->condFilesCount; condId++) {
|
|
||||||
const FOModCondFile_t *condFile = &fomod->condFiles[condId];
|
|
||||||
|
|
||||||
bool areAllFlagsValid = true;
|
|
||||||
|
|
||||||
//checking if all flags are valid
|
|
||||||
for(long flagId = 0; flagId < condFile->flagCount; flagId++) {
|
|
||||||
const GList * link = g_list_find_custom(flagList, &(condFile->requiredFlags[flagId]), (GCompareFunc)flagEqual);
|
|
||||||
if(link == NULL) {
|
|
||||||
areAllFlagsValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(areAllFlagsValid) {
|
|
||||||
for(long fileId = 0; fileId < condFile->flagCount; fileId++) {
|
|
||||||
const FOModFile_t * file = &(condFile->files[fileId]);
|
|
||||||
|
|
||||||
FOModFile_t * fileCopy = malloc(sizeof(*file));
|
|
||||||
*fileCopy = *file;
|
|
||||||
|
|
||||||
//changing pathes to lowercase since we used casefold and the pathes in the xml might not like it
|
|
||||||
char * destination = g_ascii_strdown(file->destination, -1);
|
|
||||||
fileCopy->destination = destination;
|
|
||||||
|
|
||||||
char * source = g_ascii_strdown(file->source, -1);
|
|
||||||
fileCopy->source = source;
|
|
||||||
|
|
||||||
pendingFileOperations = g_list_append(pendingFileOperations, fileCopy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pendingFileOperations;
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeFileOperations(GList * fileOperations) {
|
|
||||||
GList * fileOperationsStart = fileOperations;
|
|
||||||
while(fileOperations != NULL) {
|
|
||||||
FOModFile_t * file = (FOModFile_t *)fileOperations->data;
|
|
||||||
if(file->destination != NULL)free(file->destination);
|
|
||||||
if(file->source != NULL)free(file->source);
|
|
||||||
fileOperations = g_list_next(fileOperations);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_list_free_full(fileOperationsStart, free);
|
|
||||||
}
|
|
||||||
|
|
||||||
int installFOMod(const char * modFolder, const char * destination) {
|
|
||||||
//everything should be lowercase since we use casefold() before calling any install function
|
|
||||||
char * fomodFolder = g_build_path("/", modFolder, "fomod", NULL);
|
|
||||||
char * fomodFile = g_build_filename(fomodFolder, "moduleconfig.xml", NULL);
|
|
||||||
|
|
||||||
if(access(fomodFile, F_OK) != 0) {
|
|
||||||
fprintf(stderr, "FOMod file not found, are you sure this is a fomod mod ?\n");
|
|
||||||
g_free(fomodFolder);
|
|
||||||
g_free(fomodFile);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
FOMod_t fomod;
|
|
||||||
int returnValue = parseFOMod(fomodFile, &fomod);
|
|
||||||
if(returnValue != EXIT_SUCCESS) {
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
g_free(fomodFile);
|
|
||||||
|
|
||||||
GList * flagList = NULL;
|
|
||||||
GList * pendingFileOperations = NULL;
|
|
||||||
|
|
||||||
sortSteps(&fomod);
|
|
||||||
|
|
||||||
for(int i = 0; i < fomod.stepCount; i++) {
|
|
||||||
const FOModStep_t * step = &fomod.steps[i];
|
|
||||||
|
|
||||||
bool validFlags = true;
|
|
||||||
for(int flagId = 0; flagId < step->flagCount; flagId++) {
|
|
||||||
const GList * flagLink = g_list_find_custom(flagList, &step->requiredFlags[flagId], (GCompareFunc)flagEqual);
|
|
||||||
if(flagLink == NULL) {
|
|
||||||
validFlags = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!validFlags) continue;
|
|
||||||
|
|
||||||
for(int groupId = 0; groupId < step->groupCount; groupId++ ) {
|
|
||||||
FOModGroup_t group = step->groups[groupId];
|
|
||||||
|
|
||||||
sortGroup(&group);
|
|
||||||
|
|
||||||
u_int8_t min;
|
|
||||||
u_int8_t max;
|
|
||||||
int inputCount = 0;
|
|
||||||
char *inputBuffer = NULL;
|
|
||||||
size_t bufferSize = 0;
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
printfOptionsInOrder(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 EXIT_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]);
|
|
||||||
FOModPlugin_t plugin = group.plugins[choice];
|
|
||||||
for(int flagId = 0; flagId < plugin.flagCount; flagId++) {
|
|
||||||
flagList = g_list_append(flagList, &plugin.flags[flagId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//do the install
|
|
||||||
for(int pluginId = 0; pluginId < plugin.fileCount; pluginId++) {
|
|
||||||
const FOModFile_t * file = &plugin.files[pluginId];
|
|
||||||
|
|
||||||
FOModFile_t * fileCopy = malloc(sizeof(FOModFile_t));
|
|
||||||
*fileCopy = *file;
|
|
||||||
|
|
||||||
//changing pathes to lowercase since we used casefold and the pathes in the xml might not like it
|
|
||||||
char * destination = g_ascii_strdown(file->destination, -1);
|
|
||||||
fileCopy->destination = strdup(destination);
|
|
||||||
g_free(destination);
|
|
||||||
|
|
||||||
char * source = g_ascii_strdown(file->source, -1);
|
|
||||||
fileCopy->source = strdup(source);
|
|
||||||
g_free(source);
|
|
||||||
|
|
||||||
pendingFileOperations = g_list_append(pendingFileOperations, fileCopy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_strfreev(choices);
|
|
||||||
if(inputBuffer != NULL)free(inputBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: manage multiple files with the same name
|
|
||||||
|
|
||||||
pendingFileOperations = processCondFiles(&fomod, flagList, pendingFileOperations);
|
|
||||||
processFileOperations(&pendingFileOperations, modFolder, destination);
|
|
||||||
|
|
||||||
printf("FOMod successfully installed!\n");
|
|
||||||
g_list_free(flagList);
|
|
||||||
freeFileOperations(pendingFileOperations);
|
|
||||||
freeFOMod(&fomod);
|
|
||||||
g_free(fomodFolder);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef __FOMOD_TYPES_H__
|
|
||||||
#define __FOMOD_TYPES_H__
|
|
||||||
|
|
||||||
#include "stdbool.h"
|
|
||||||
#include "xmlUtil.h"
|
|
||||||
|
|
||||||
typedef struct FOModFlag {
|
|
||||||
char * name;
|
|
||||||
char * value;
|
|
||||||
} FOModFlag_t;
|
|
||||||
|
|
||||||
typedef struct FOModFile {
|
|
||||||
char * source;
|
|
||||||
char * destination;
|
|
||||||
int priority;
|
|
||||||
bool isFolder;
|
|
||||||
} FOModFile_t;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct FOModCondFile {
|
|
||||||
FOModFlag_t * requiredFlags;
|
|
||||||
unsigned int flagCount;
|
|
||||||
FOModFile_t * files;
|
|
||||||
unsigned int fileCount;
|
|
||||||
} FOModCondFile_t;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#ifndef __GROUP_H__
|
|
||||||
#define __GROUP_H__
|
|
||||||
|
|
||||||
#include <libxml/parser.h>
|
|
||||||
#include "fomodTypes.h"
|
|
||||||
|
|
||||||
typedef enum GroupType_t { ONE_ONLY, ANY, AT_LEAST_ONE, AT_MOST_ONE, ALL } GroupType_t;
|
|
||||||
typedef enum TypeDescriptor { OPTIONAL, MAYBE_USABLE, NOT_USABLE, REQUIRED, RECOMMENDED } TypeDescriptor_t;
|
|
||||||
|
|
||||||
typedef struct FOModPlugin {
|
|
||||||
char * description;
|
|
||||||
char * image;
|
|
||||||
FOModFlag_t * flags;
|
|
||||||
int flagCount;
|
|
||||||
FOModFile_t * files;
|
|
||||||
int fileCount;
|
|
||||||
TypeDescriptor_t type;
|
|
||||||
char * name;
|
|
||||||
} FOModPlugin_t;
|
|
||||||
|
|
||||||
//combine group and "plugins"
|
|
||||||
typedef struct FOModGroup {
|
|
||||||
FOModPlugin_t * plugins;
|
|
||||||
int pluginCount;
|
|
||||||
GroupType_t type;
|
|
||||||
char * name;
|
|
||||||
FOModOrder_t order;
|
|
||||||
} FOModGroup_t;
|
|
||||||
|
|
||||||
int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group);
|
|
||||||
void freeGroup(FOModGroup_t * group);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#ifndef __FOMOD_PARSER_H__
|
|
||||||
#define __FOMOD_PARSER_H__
|
|
||||||
|
|
||||||
|
|
||||||
#include "group.h"
|
|
||||||
|
|
||||||
//combine installStep and optionalFileGroups
|
|
||||||
typedef struct FOModStep {
|
|
||||||
FOModOrder_t optionOrder;
|
|
||||||
FOModGroup_t * groups;
|
|
||||||
int groupCount;
|
|
||||||
char * name;
|
|
||||||
FOModFlag_t * requiredFlags;
|
|
||||||
int flagCount;
|
|
||||||
} FOModStep_t;
|
|
||||||
|
|
||||||
typedef struct FOMod {
|
|
||||||
char * moduleName;
|
|
||||||
char * moduleImage;
|
|
||||||
char ** requiredInstallFiles;
|
|
||||||
FOModOrder_t stepOrder;
|
|
||||||
FOModStep_t * steps;
|
|
||||||
int stepCount;
|
|
||||||
FOModCondFile_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
|
|
||||||
* @return error code.
|
|
||||||
*/
|
|
||||||
int parseFOMod(const char * fomodFile, FOMod_t* fomod);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Free content of a fomod file.
|
|
||||||
* @param fomod
|
|
||||||
*/
|
|
||||||
void freeFOMod(FOMod_t * fomod);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#ifndef __XML_UTIL_H__
|
|
||||||
#define __XML_UTIL_H__
|
|
||||||
|
|
||||||
#include <libxml/parser.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum FOModOrder { ASC, DESC, ORD } FOModOrder_t;
|
|
||||||
|
|
||||||
bool validateNode(xmlNodePtr * node, bool skipText, const char * names, ...);
|
|
||||||
/**
|
|
||||||
* @brief Free memory of and xmlChar and return a strdup version. just to make sure there is nothing remaining in libxml
|
|
||||||
*/
|
|
||||||
char * freeAndDup(xmlChar * line);
|
|
||||||
FOModOrder_t getFOModOrder(const char * order);
|
|
||||||
/**
|
|
||||||
* @brief replace / by \
|
|
||||||
* @param path
|
|
||||||
*/
|
|
||||||
void fixPath(char * path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Count the number of step before null
|
|
||||||
*
|
|
||||||
* @param pointers pointer to the list
|
|
||||||
* @param typeSize size of each element of the list
|
|
||||||
* @return size
|
|
||||||
*/
|
|
||||||
int countUntilNull(void * pointers, size_t typeSize);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
//no function should ever be put here, only keep important macros here
|
#ifndef __MAIN_H__
|
||||||
|
#define __MAIN_H__
|
||||||
|
|
||||||
|
#define MODLIB_NAME "mod-manager"
|
||||||
|
//no function should ever be put here, only keep important macros and globals here
|
||||||
|
|
||||||
#define APP_NAME "modmanager"
|
|
||||||
//relative to home the url is not preprocessed so ../ might create some issues
|
//relative to home the url is not preprocessed so ../ might create some issues
|
||||||
// in c "A" "B" is the same as "AB"
|
// in c "A" "B" is the same as "AB"
|
||||||
#define MANAGER_FILES ".local/share/" APP_NAME
|
#define MODLIB_WORKING_DIR ".local/share/" MODLIB_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"
|
||||||
@@ -12,4 +15,5 @@
|
|||||||
//overlayfs temporary dir.
|
//overlayfs temporary dir.
|
||||||
#define GAME_WORK_DIR_NAME "WORK_DIRS"
|
#define GAME_WORK_DIR_NAME "WORK_DIRS"
|
||||||
|
|
||||||
#define VERSION "0.1"
|
#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
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#ifndef __FILE_H__
|
||||||
|
#define __FILE_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
|
||||||
|
//valid copy flags
|
||||||
|
#define FILE_CP_DEFAULT 0
|
||||||
|
#define FILE_CP_LINK 1
|
||||||
|
#define FILE_CP_RECURSIVE 2
|
||||||
|
#define FILE_CP_NO_TARGET_DIR 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief execute the cp command from the source to the dest
|
||||||
|
* @param source path to the source files
|
||||||
|
* @param dest path to the destination folder
|
||||||
|
* @param flags refer to the "valid copy flags" use them like this CP_LINK | CP_RECURSIVE
|
||||||
|
* @return cp return value
|
||||||
|
*/
|
||||||
|
int file_copy(const char * source, const char * dest, u_int32_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief execute the cp command from the source to the dest
|
||||||
|
* @param source path to the source files
|
||||||
|
* @param dest path to the destination folder
|
||||||
|
* @param bool enable recursive rm -r
|
||||||
|
* @return rm return value
|
||||||
|
*/
|
||||||
|
int file_delete(const char * path, bool recursive);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the mv command
|
||||||
|
* @param source
|
||||||
|
* @param destination
|
||||||
|
* @return mv's exit value
|
||||||
|
*/
|
||||||
|
int file_move(const char * source, const char * destination);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Recursively rename all file and folder to lowercase.
|
||||||
|
*
|
||||||
|
* @param folder
|
||||||
|
*/
|
||||||
|
void file_casefold(const char * folder);
|
||||||
|
|
||||||
|
|
||||||
|
const char * file_extractLastPart(const char * filePath, const char delimeter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the extension of a file by looking for the character '.'
|
||||||
|
* @param filePath
|
||||||
|
* @return return a pointer to the address after the '.' or null if it was not found;
|
||||||
|
*/
|
||||||
|
const char * file_extractExtension(const char * filePath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the file name by looking for the last character '/'
|
||||||
|
* @param filePath
|
||||||
|
* @return return a pointer to the address after the last '/' or null if it was not found (the path might not be a path in this case)
|
||||||
|
*/
|
||||||
|
const char * file_extractFileName(const char * filePath);
|
||||||
|
|
||||||
|
|
||||||
|
GList * file_listFolderContent(const char * path);
|
||||||
|
#endif
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
#ifndef __FOMOD_H__
|
#ifndef __PUBLIC_FOMOD_H__
|
||||||
#define __FOMOD_H__
|
#define __PUBLIC_FOMOD_H__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include "errorType.h"
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "fomod/parser.h"
|
#include "fomodTypes.h"
|
||||||
|
|
||||||
#include "fomod/group.h"
|
|
||||||
#include "fomod/xmlUtil.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 fomodFile path to the moduleconfig.xml
|
||||||
* @param destination folder of the new mod that contains the result of the fomod process.
|
* @param fomod pointer to a new FOMod_t
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
int 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.
|
* @brief In fomod there is file operations which depends on multiple flags this function find the ones that mach our current flags and append them to a list.
|
||||||
@@ -25,7 +21,7 @@ int installFOMod(const char * modFolder, const char * destination);
|
|||||||
* @param pendingFileOperations a list of pending FOModFile_t operation to which add the new ones. (can be null)
|
* @param pendingFileOperations a list of pending FOModFile_t operation to which add the new ones. (can be null)
|
||||||
* @return a list of pendingFileOperations(FOModFile_t)
|
* @return a list of pendingFileOperations(FOModFile_t)
|
||||||
*/
|
*/
|
||||||
GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) __attribute__((warn_unused_result));
|
GList * fomod_processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendingFileOperations) __attribute__((warn_unused_result));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief FOModFile_t have a priority option and this function execute the file operation while taking this into account.
|
* @brief FOModFile_t have a priority option and this function execute the file operation while taking this into account.
|
||||||
@@ -35,13 +31,20 @@ GList * processCondFiles(const FOMod_t * fomod, GList * flagList, GList * pendin
|
|||||||
* @param destination folder of the new mod that contains the result of the process.
|
* @param destination folder of the new mod that contains the result of the process.
|
||||||
* @return error code
|
* @return error code
|
||||||
*/
|
*/
|
||||||
int processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination);
|
error_t fomod_processFileOperations(GList ** pendingFileOperations, const char * modFolder, const char * destination);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
*
|
*
|
||||||
* @param fileOperations
|
* @param fileOperations
|
||||||
*/
|
*/
|
||||||
void freeFileOperations(GList * fileOperations);
|
void fomod_freeFileOperations(GList * fileOperations);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free content of a fomod file.
|
||||||
|
* @param fomod
|
||||||
|
*/
|
||||||
|
void fomod_freeFOMod(FOMod_t * fomod);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef __GAME_DATA_H__
|
||||||
|
#define __GAME_DATA_H__
|
||||||
|
|
||||||
|
|
||||||
|
#include "errorType.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the path to the data folder
|
||||||
|
* @param appid appid of the game
|
||||||
|
* @param destination pointer to a null char * variable. it's value will be allocated with malloc
|
||||||
|
* @return error_t
|
||||||
|
*/
|
||||||
|
error_t 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.
|
* @brief Get the user home dir regardless if he used sudo.
|
||||||
@@ -6,3 +7,5 @@
|
|||||||
* @return char* path to the home dir
|
* @return char* path to the home dir
|
||||||
*/
|
*/
|
||||||
char * getHome(void);
|
char * getHome(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
#ifndef __INSTALL_H__
|
#ifndef __INSTALL_H__
|
||||||
#define __INSTALL_H__
|
#define __INSTALL_H__
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <errorType.h>
|
||||||
|
|
||||||
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
|
#define INSTALLED_FLAG_FILE "__DO_NOT_REMOVE__"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,8 +11,7 @@
|
|||||||
*
|
*
|
||||||
* @param filePath path to the mod file
|
* @param filePath path to the mod file
|
||||||
* @param appId game for which the mod is destined to be used with.
|
* @param appId game for which the mod is destined to be used with.
|
||||||
* @return EXIT_SUCCESS or EXIT_FAILURE
|
|
||||||
*/
|
*/
|
||||||
int addMod(char * filePath, int appId);
|
error_t install_addMod(char * filePath, int appId);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef __LOAD_ORDER_H__
|
||||||
|
#define __LOAD_ORDER_H__
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <errorType.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief find all plugins in the data folder of the game
|
||||||
|
* @param appid the appid of the game
|
||||||
|
* @param order a pointer to a null Glist *. in which there will be the list of esm files.
|
||||||
|
*/
|
||||||
|
error_t order_listPlugins(int appid, GList ** list) __attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief fetch the load order of the game it might be null if setLoadOrder was never called.
|
||||||
|
* @param appid the appid of the game
|
||||||
|
* @param order a pointer to a null Glist *. in which there will be the list of esm files.
|
||||||
|
*/
|
||||||
|
error_t order_getLoadOrder(int appid, GList ** order) __attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief change the plugin load order of the game
|
||||||
|
* @param appid the appid of the game
|
||||||
|
* @param loadOrder the load order
|
||||||
|
*/
|
||||||
|
error_t order_setLoadOrder(int appid, GList * loadOrder) __attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief List all dependencies for a esm mod.
|
||||||
|
* @param esmPath path to the esm file
|
||||||
|
* @param dependencies a pointer to a null Glist *. in which there will be the list of esm files.
|
||||||
|
* free it using g_list_free_full(dependencies, free);
|
||||||
|
*/
|
||||||
|
error_t order_getModDependencies(const char * esmPath, GList ** dependencies) __attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#define __ORDER_H__
|
#define __ORDER_H__
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <errorType.h>
|
||||||
|
|
||||||
#define ORDER_FILE "__ORDER__"
|
#define ORDER_FILE "__ORDER__"
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
* @return GList of char containing the name of the mod folder in order
|
* @return GList of char containing the name of the mod folder in order
|
||||||
*/
|
*/
|
||||||
GList * listMods(int appid);
|
GList * order_listMods(int appid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Change the mod order by swaping two mod
|
* @brief Change the mod order by swaping two mod
|
||||||
@@ -24,6 +25,6 @@ GList * listMods(int appid);
|
|||||||
* @param modId
|
* @param modId
|
||||||
* @param modId2
|
* @param modId2
|
||||||
*/
|
*/
|
||||||
int swapPlace(int appid, int modId, int modId2);
|
error_t order_swapPlace(int appid, int modId, int modId2);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,55 +1,59 @@
|
|||||||
#ifndef __STEAM_H__
|
#ifndef __STEAM_H__
|
||||||
#define __STEAM_H__
|
#define __STEAM_H__
|
||||||
|
|
||||||
#include "macro.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 ValveApp {
|
typedef struct steam_App {
|
||||||
unsigned int appid;
|
unsigned int appid;
|
||||||
unsigned int update;
|
unsigned int update;
|
||||||
} ValveApp_t;
|
} steam_App_t;
|
||||||
|
|
||||||
typedef struct ValveLibraries {
|
typedef struct steam_Libraries {
|
||||||
char * path;
|
char * path;
|
||||||
char * label;
|
char * label;
|
||||||
char * contentId;
|
char * contentId;
|
||||||
unsigned long totalSize;
|
unsigned long totalSize;
|
||||||
char * update_clean_bytes_tally;
|
char * update_clean_bytes_tally;
|
||||||
char * time_last_update_corruption;
|
char * time_last_update_corruption;
|
||||||
ValveApp_t * apps;
|
steam_App_t * apps;
|
||||||
size_t appsCount;
|
size_t appsCount;
|
||||||
} ValveLibraries_t;
|
} steam_Libraries_t;
|
||||||
|
|
||||||
//todo add the older games
|
//todo add the older games
|
||||||
// order has to be the same as in GAMES_NAMES
|
// order has to be the same as in GAMES_NAMES
|
||||||
static const u_int32_t GAMES_APPIDS[] = {
|
static const u_int32_t GAMES_APPIDS[] = {
|
||||||
489830,
|
489830,
|
||||||
22330,
|
22330,
|
||||||
377160
|
377160,
|
||||||
|
22320
|
||||||
};
|
};
|
||||||
|
|
||||||
//the name of the game in the steamapps/common folder
|
//the name of the game in the steamapps/common folder
|
||||||
static const char * GAMES_NAMES[] = {
|
static const char * GAMES_NAMES[] = {
|
||||||
"Skyrim Special Edition",
|
"Skyrim Special Edition",
|
||||||
"Oblivion",
|
"Oblivion",
|
||||||
"Fallout 4"
|
"Fallout 4",
|
||||||
|
"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
|
* @brief list all installed games and the paths to the game's files
|
||||||
|
* This method has a singleton so after the first execution it will no rescan for new games.
|
||||||
*
|
*
|
||||||
* @param status pointer to a status variable that will be modified to EXIT_SUCCESS or EXIT_FAILURE
|
* @param status pointer to a status variable that will be modified to EXIT_SUCCESS or EXIT_FAILURE
|
||||||
* @return GHashTable* a map appid(int) => path(char *)
|
* @return GHashTable* a map gameId(int) => path(char *) to the corresponding steam library
|
||||||
*/
|
*/
|
||||||
//TODO: flip the return value and parameter
|
error_t steam_searchGames(GHashTable** tablePointer);
|
||||||
GHashTable* search_games(int * status);
|
|
||||||
|
void steam_freeGameTable(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS
|
* @brief search the index of the game inside GAMES_NAMES or GAMES_APPIDS
|
||||||
@@ -57,6 +61,14 @@ GHashTable* search_games(int * status);
|
|||||||
* @param appid
|
* @param appid
|
||||||
* @return -1 in case of failure or the index of the game.
|
* @return -1 in case of failure or the index of the game.
|
||||||
*/
|
*/
|
||||||
int getGameIdFromAppId(u_int32_t appid);
|
int steam_gameIdFromAppId(u_int32_t appid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
-166
@@ -1,166 +0,0 @@
|
|||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include "install.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "file.h"
|
|
||||||
|
|
||||||
static int unzip(char * path, char * outdir) {
|
|
||||||
char * const args[] = {
|
|
||||||
"unzip",
|
|
||||||
"-LL", // to lowercase
|
|
||||||
"-q",
|
|
||||||
path,
|
|
||||||
"-d",
|
|
||||||
outdir,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
|
|
||||||
if(pid == 0) {
|
|
||||||
execv("/usr/bin/unzip", args);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
int returnValue;
|
|
||||||
waitpid(pid, &returnValue, 0);
|
|
||||||
|
|
||||||
if(returnValue != 0) {
|
|
||||||
fprintf(stderr, "\nFailed to decompress archive\n");
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int unrar(char * path, char * outdir) {
|
|
||||||
char * const args[] = {
|
|
||||||
"unrar",
|
|
||||||
"x",
|
|
||||||
"-y", //assume yes
|
|
||||||
"-cl", // to lowercase
|
|
||||||
path,
|
|
||||||
outdir,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
|
|
||||||
if(pid == 0) {
|
|
||||||
execv("/usr/bin/unrar", args);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
int returnValue;
|
|
||||||
waitpid(pid, &returnValue, 0);
|
|
||||||
|
|
||||||
if(returnValue != 0) {
|
|
||||||
fprintf(stderr, "\nFailed to decompress archive\n");
|
|
||||||
}
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int un7z(char * path, const char * outdir) {
|
|
||||||
gchar * outParameter = g_strjoin("", "-o", outdir, NULL);
|
|
||||||
|
|
||||||
char * const args[] = {
|
|
||||||
"7z",
|
|
||||||
"-y", //assume yes
|
|
||||||
"x",
|
|
||||||
path,
|
|
||||||
outParameter,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
|
|
||||||
if(pid == 0) {
|
|
||||||
execv("/usr/bin/7z", args);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
g_free(outParameter);
|
|
||||||
int returnValue;
|
|
||||||
waitpid(pid, &returnValue, 0);
|
|
||||||
|
|
||||||
if(returnValue != 0) {
|
|
||||||
fprintf(stderr, "\nFailed to decompress archive\n");
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
//make everything lowercase since 7z don't have an argument for that.
|
|
||||||
casefold(outdir);
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * extractLastPart(const char * filePath, const char delimeter) {
|
|
||||||
const int length = strlen(filePath);
|
|
||||||
long index = -1;
|
|
||||||
for(long i= length - 1; i >= 0; i--) {
|
|
||||||
if(filePath[i] == delimeter) {
|
|
||||||
index = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(index <= 0 || index == length) return NULL;
|
|
||||||
return &filePath[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * extractExtension(const char * filePath) {
|
|
||||||
return extractLastPart(filePath, '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * extractFileName(const char * filePath) {
|
|
||||||
return extractLastPart(filePath, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
int addMod(char * filePath, int appId) {
|
|
||||||
int returnValue = EXIT_SUCCESS;
|
|
||||||
|
|
||||||
if (access(filePath, F_OK) != 0) {
|
|
||||||
fprintf(stderr, "File not found\n");
|
|
||||||
returnValue = EXIT_FAILURE;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * configFolder = g_build_filename(getenv("HOME"), MANAGER_FILES, NULL);
|
|
||||||
if(g_mkdir_with_parents(configFolder, 0755) < 0) {
|
|
||||||
fprintf(stderr, "Could not create mod folder");
|
|
||||||
returnValue = EXIT_FAILURE;
|
|
||||||
goto exit2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * filename = extractFileName(filePath);
|
|
||||||
const char * extension = extractExtension(filename);
|
|
||||||
char * lowercaseExtension = g_ascii_strdown(extension, -1);
|
|
||||||
|
|
||||||
char appIdStr[20];
|
|
||||||
sprintf(appIdStr, "%d", appId);
|
|
||||||
char * outdir = g_build_filename(configFolder, MOD_FOLDER_NAME, appIdStr, filename, NULL);
|
|
||||||
|
|
||||||
g_mkdir_with_parents(outdir, 0755);
|
|
||||||
printf("Adding mod, this process can be slow depending on your hardware\n");
|
|
||||||
if(strcmp(lowercaseExtension, "rar") == 0) {
|
|
||||||
returnValue = unrar(filePath, outdir);
|
|
||||||
} else if (strcmp(lowercaseExtension, "zip") == 0) {
|
|
||||||
returnValue = unzip(filePath, outdir);
|
|
||||||
} else if (strcmp(lowercaseExtension, "7z") == 0) {
|
|
||||||
returnValue = un7z(filePath, outdir);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unsupported format only zip/7z/rar are supported\n");
|
|
||||||
returnValue = EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Done\n");
|
|
||||||
|
|
||||||
free(lowercaseExtension);
|
|
||||||
free(outdir);
|
|
||||||
exit2:
|
|
||||||
free(configFolder);
|
|
||||||
exit:
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#include "archives.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
int archive_unzip(char * path, char * outdir) {
|
||||||
|
char * const args[] = {
|
||||||
|
"unzip",
|
||||||
|
"-LL", // to lowercase
|
||||||
|
"-q",
|
||||||
|
path,
|
||||||
|
"-d",
|
||||||
|
outdir,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if(pid == 0) {
|
||||||
|
execv("/usr/bin/unzip", args);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
int returnValue;
|
||||||
|
waitpid(pid, &returnValue, 0);
|
||||||
|
|
||||||
|
if(returnValue != 0) {
|
||||||
|
fprintf(stderr, "\nFailed to decompress archive\n");
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int archive_unrar(char * path, char * outdir) {
|
||||||
|
char * const args[] = {
|
||||||
|
"unrar",
|
||||||
|
"x",
|
||||||
|
"-y", //assume yes
|
||||||
|
"-cl", // to lowercase
|
||||||
|
path,
|
||||||
|
outdir,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if(pid == 0) {
|
||||||
|
execv("/usr/bin/unrar", args);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
int returnValue;
|
||||||
|
waitpid(pid, &returnValue, 0);
|
||||||
|
|
||||||
|
if(returnValue != 0) {
|
||||||
|
fprintf(stderr, "\nFailed to decompress archive\n");
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int archive_un7z(char * path, const char * outdir) {
|
||||||
|
gchar * outParameter = g_strjoin("", "-o", outdir, NULL);
|
||||||
|
|
||||||
|
char * const args[] = {
|
||||||
|
"7z",
|
||||||
|
"-y", //assume yes
|
||||||
|
"x",
|
||||||
|
path,
|
||||||
|
outParameter,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if(pid == 0) {
|
||||||
|
execv("/usr/bin/7z", args);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
g_free(outParameter);
|
||||||
|
int returnValue;
|
||||||
|
waitpid(pid, &returnValue, 0);
|
||||||
|
|
||||||
|
if(returnValue != 0) {
|
||||||
|
fprintf(stderr, "\nFailed to decompress archive\n");
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
//make everything lowercase since 7z don't have an argument for that.
|
||||||
|
file_casefold(outdir);
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __ARCHIVES_H__
|
||||||
|
#define __ARCHIVES_H__
|
||||||
|
|
||||||
|
//all of these function will make every file into lowercase
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute the unzip command
|
||||||
|
* @param path path to archive
|
||||||
|
* @param outdir output director
|
||||||
|
* @return int return code
|
||||||
|
*/
|
||||||
|
int archive_unzip(char * path, char * outdir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute the unrar command
|
||||||
|
* @param path path to archive
|
||||||
|
* @param outdir output director
|
||||||
|
* @return int return code
|
||||||
|
*/
|
||||||
|
int archive_unrar(char * path, char * outdir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute the 7z command
|
||||||
|
* @param path path to archive
|
||||||
|
* @param outdir output director
|
||||||
|
* @return int return code
|
||||||
|
*/
|
||||||
|
int archive_un7z(char * path, const char * outdir);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ static u_int32_t countSetBits(u_int32_t n) {
|
|||||||
|
|
||||||
//TODO: add interruption support
|
//TODO: add interruption support
|
||||||
//simplest way to copy a file in c(linux)
|
//simplest way to copy a file in c(linux)
|
||||||
int copy(const char * path, const char * dest, u_int32_t flags) {
|
int file_copy(const char * path, const char * dest, u_int32_t flags) {
|
||||||
int flagCount = countSetBits(flags);
|
int flagCount = countSetBits(flags);
|
||||||
if(flagCount > 3) {
|
if(flagCount > 3) {
|
||||||
fprintf(stderr, "Invalid flags for cp command\n");
|
fprintf(stderr, "Invalid flags for cp command\n");
|
||||||
@@ -34,15 +34,15 @@ int copy(const char * path, const char * dest, u_int32_t flags) {
|
|||||||
strcpy(args[2], dest);
|
strcpy(args[2], dest);
|
||||||
int argIndex = 3;
|
int argIndex = 3;
|
||||||
|
|
||||||
if(flags & CP_LINK) {
|
if(flags & FILE_CP_LINK) {
|
||||||
args[argIndex] = "--link";
|
args[argIndex] = "--link";
|
||||||
argIndex += 1;
|
argIndex += 1;
|
||||||
}
|
}
|
||||||
if(flags & CP_RECURSIVE) {
|
if(flags & FILE_CP_RECURSIVE) {
|
||||||
args[argIndex] = "-r";
|
args[argIndex] = "-r";
|
||||||
argIndex += 1;
|
argIndex += 1;
|
||||||
}
|
}
|
||||||
if(flags & CP_NO_TARGET_DIR) {
|
if(flags & FILE_CP_NO_TARGET_DIR) {
|
||||||
args[argIndex] = "-T";
|
args[argIndex] = "-T";
|
||||||
argIndex += 1;
|
argIndex += 1;
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ int copy(const char * path, const char * dest, u_int32_t flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int delete(const char * path, bool recursive) {
|
int file_delete(const char * path, bool recursive) {
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if(pid == 0) {
|
if(pid == 0) {
|
||||||
if(recursive) {
|
if(recursive) {
|
||||||
@@ -77,7 +77,7 @@ int delete(const char * path, bool recursive) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int move(const char * source, const char * destination) {
|
int file_move(const char * source, const char * destination) {
|
||||||
int pid = fork();
|
int pid = fork();
|
||||||
if(pid == 0) {
|
if(pid == 0) {
|
||||||
execl("/bin/mv", "/bin/mv", source, destination, NULL);
|
execl("/bin/mv", "/bin/mv", source, destination, NULL);
|
||||||
@@ -92,7 +92,7 @@ int move(const char * source, const char * destination) {
|
|||||||
|
|
||||||
//rename a folder and all subfolder and files to lowercase
|
//rename a folder and all subfolder and files to lowercase
|
||||||
//TODO: error handling
|
//TODO: error handling
|
||||||
void casefold(const char * folder) {
|
void file_casefold(const char * folder) {
|
||||||
DIR * d = opendir(folder);
|
DIR * d = opendir(folder);
|
||||||
struct dirent *dir;
|
struct dirent *dir;
|
||||||
if (d) {
|
if (d) {
|
||||||
@@ -108,7 +108,7 @@ void casefold(const char * folder) {
|
|||||||
|
|
||||||
if(strcmp(destinationName, dir->d_name) != 0) {
|
if(strcmp(destinationName, dir->d_name) != 0) {
|
||||||
|
|
||||||
int result = move(file, destination);
|
int result = file_move(file, destination);
|
||||||
if(result != EXIT_SUCCESS) {
|
if(result != EXIT_SUCCESS) {
|
||||||
fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName);
|
fprintf(stderr, "Move failed: %s => %s \n", dir->d_name, destinationName);
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ void casefold(const char * folder) {
|
|||||||
g_free(destinationName);
|
g_free(destinationName);
|
||||||
|
|
||||||
if(dir->d_type == DT_DIR) {
|
if(dir->d_type == DT_DIR) {
|
||||||
casefold(destination);
|
file_casefold(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(destination);
|
g_free(destination);
|
||||||
@@ -128,3 +128,43 @@ void casefold(const char * folder) {
|
|||||||
closedir(d);
|
closedir(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char * file_extractLastPart(const char * filePath, const char delimeter) {
|
||||||
|
const int length = strlen(filePath);
|
||||||
|
long index = -1;
|
||||||
|
for(long i= length - 1; i >= 0; i--) {
|
||||||
|
if(filePath[i] == delimeter) {
|
||||||
|
index = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index <= 0 || index == length) return NULL;
|
||||||
|
return &filePath[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * file_extractExtension(const char * filePath) {
|
||||||
|
return file_extractLastPart(filePath, '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * file_extractFileName(const char * filePath) {
|
||||||
|
return file_extractLastPart(filePath, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "group.h"
|
#include "group.h"
|
||||||
|
#include "fomodTypes.h"
|
||||||
#include "xmlUtil.h"
|
#include "xmlUtil.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -35,11 +36,11 @@ static TypeDescriptor_t getDescriptor(const char * descriptor) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeGroup(FOModGroup_t * group) {
|
void grp_freeGroup(fomod_Group_t * group){
|
||||||
free(group->name);
|
free(group->name);
|
||||||
if(group->pluginCount == 0) return;
|
if(group->pluginCount == 0) return;
|
||||||
for(int pluginId = 0; pluginId < group->pluginCount; pluginId++) {
|
for(int pluginId = 0; pluginId < group->pluginCount; pluginId++) {
|
||||||
FOModPlugin_t * plugin = &group->plugins[pluginId];
|
fomod_Plugin_t * plugin = &group->plugins[pluginId];
|
||||||
if(plugin->fileCount > 0) {
|
if(plugin->fileCount > 0) {
|
||||||
for(int i = 0; i < plugin->fileCount; i++) {
|
for(int i = 0; i < plugin->fileCount; i++) {
|
||||||
free(plugin->files[i].destination);
|
free(plugin->files[i].destination);
|
||||||
@@ -65,10 +66,10 @@ void freeGroup(FOModGroup_t * group) {
|
|||||||
group->pluginCount = 0;
|
group->pluginCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
static int parseConditionFlags(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) {
|
||||||
xmlNodePtr flagNode = nodeElement->children;
|
xmlNodePtr flagNode = nodeElement->children;
|
||||||
while(flagNode != NULL) {
|
while(flagNode != NULL) {
|
||||||
if(!validateNode(&flagNode, true, "flag", NULL)) {
|
if(!xml_validateNode(&flagNode, true, "flag", NULL)) {
|
||||||
if(plugin->flagCount > 0) {
|
if(plugin->flagCount > 0) {
|
||||||
free(plugin->flags);
|
free(plugin->flags);
|
||||||
}
|
}
|
||||||
@@ -77,12 +78,12 @@ static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
|||||||
if(flagNode == NULL)continue;
|
if(flagNode == NULL)continue;
|
||||||
|
|
||||||
plugin->flagCount += 1;
|
plugin->flagCount += 1;
|
||||||
plugin->flags = realloc(plugin->flags, plugin->flagCount * sizeof(FOModFlag_t));
|
plugin->flags = realloc(plugin->flags, plugin->flagCount * sizeof(fomod_Flag_t));
|
||||||
|
|
||||||
FOModFlag_t * flag = &plugin->flags[plugin->flagCount - 1];
|
fomod_Flag_t * flag = &plugin->flags[plugin->flagCount - 1];
|
||||||
|
|
||||||
flag->name = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "name"));
|
flag->name = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "name"));
|
||||||
flag->value = freeAndDup(xmlNodeGetContent(flagNode));
|
flag->value = xml_freeAndDup(xmlNodeGetContent(flagNode));
|
||||||
|
|
||||||
flagNode = flagNode->next;
|
flagNode = flagNode->next;
|
||||||
}
|
}
|
||||||
@@ -90,10 +91,10 @@ static int parseConditionFlags(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
static int parseGroupFiles(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) {
|
||||||
xmlNodePtr fileNode = nodeElement->children;
|
xmlNodePtr fileNode = nodeElement->children;
|
||||||
while(fileNode != NULL) {
|
while(fileNode != NULL) {
|
||||||
if(!validateNode(&fileNode, true, "folder", "file", NULL)) {
|
if(!xml_validateNode(&fileNode, true, "folder", "file", NULL)) {
|
||||||
fprintf(stderr, "Unexpected node in files");
|
fprintf(stderr, "Unexpected node in files");
|
||||||
//TODO: free
|
//TODO: free
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -103,11 +104,11 @@ static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
|||||||
|
|
||||||
plugin->fileCount += 1;
|
plugin->fileCount += 1;
|
||||||
|
|
||||||
plugin->files = realloc(plugin->files, (plugin->fileCount + 1) * sizeof(FOModFile_t));
|
plugin->files = realloc(plugin->files, (plugin->fileCount + 1) * sizeof(fomod_File_t));
|
||||||
FOModFile_t * file = &plugin->files[plugin->fileCount - 1];
|
fomod_File_t * file = &plugin->files[plugin->fileCount - 1];
|
||||||
|
|
||||||
file->destination = freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "destination"));
|
file->destination = xml_freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "destination"));
|
||||||
file->source = freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "source"));
|
file->source = xml_freeAndDup(xmlGetProp(fileNode, (const xmlChar *) "source"));
|
||||||
|
|
||||||
//TODO: test if it's a number
|
//TODO: test if it's a number
|
||||||
xmlChar * priority = xmlGetProp(fileNode, (const xmlChar *) "priority");
|
xmlChar * priority = xmlGetProp(fileNode, (const xmlChar *) "priority");
|
||||||
@@ -123,18 +124,18 @@ static int parseGroupFiles(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
static int parseNodeElement(fomod_Plugin_t * plugin, xmlNodePtr nodeElement) {
|
||||||
if(xmlStrcmp(nodeElement->name, (const xmlChar *) "description") == 0) {
|
if(xmlStrcmp(nodeElement->name, (const xmlChar *) "description") == 0) {
|
||||||
plugin->description = freeAndDup(xmlNodeGetContent(nodeElement));
|
plugin->description = xml_freeAndDup(xmlNodeGetContent(nodeElement));
|
||||||
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "image") == 0) {
|
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "image") == 0) {
|
||||||
plugin->image = freeAndDup(xmlGetProp(nodeElement, (const xmlChar *) "path"));
|
plugin->image = xml_freeAndDup(xmlGetProp(nodeElement, (const xmlChar *) "path"));
|
||||||
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "conditionFlags") == 0) {
|
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "conditionFlags") == 0) {
|
||||||
return parseConditionFlags(plugin, nodeElement);
|
return parseConditionFlags(plugin, nodeElement);
|
||||||
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "files") == 0) {
|
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "files") == 0) {
|
||||||
return parseGroupFiles(plugin, nodeElement);
|
return parseGroupFiles(plugin, nodeElement);
|
||||||
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "typeDescriptor") == 0) {
|
} else if(xmlStrcmp(nodeElement->name, (const xmlChar *) "typeDescriptor") == 0) {
|
||||||
xmlNodePtr typeNode = nodeElement->children;
|
xmlNodePtr typeNode = nodeElement->children;
|
||||||
if(!validateNode(&typeNode, true, "type", NULL)) {
|
if(!xml_validateNode(&typeNode, true, "type", NULL)) {
|
||||||
fprintf(stderr, "Unexpected node in typeDescriptor");
|
fprintf(stderr, "Unexpected node in typeDescriptor");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -145,29 +146,29 @@ static int parseNodeElement(FOModPlugin_t * plugin, xmlNodePtr nodeElement) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group) {
|
int grp_parseGroup(xmlNodePtr groupNode, fomod_Group_t* group) {
|
||||||
xmlNodePtr pluginsNode = groupNode->children;
|
xmlNodePtr pluginsNode = groupNode->children;
|
||||||
if(!validateNode(&pluginsNode, true, "plugins", NULL)) {
|
if(!xml_validateNode(&pluginsNode, true, "plugins", NULL)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
group->name = freeAndDup(xmlGetProp( groupNode, (const xmlChar *) "name"));
|
group->name = xml_freeAndDup(xmlGetProp( groupNode, (const xmlChar *) "name"));
|
||||||
|
|
||||||
xmlChar * type = xmlGetProp(groupNode, (const xmlChar *) "type");
|
xmlChar * type = xmlGetProp(groupNode, (const xmlChar *) "type");
|
||||||
group->type = getGroupType((const char *)type);
|
group->type = getGroupType((const char *)type);
|
||||||
xmlFree(type);
|
xmlFree(type);
|
||||||
|
|
||||||
char * order = (char *) xmlGetProp(pluginsNode, (const xmlChar *) "order");
|
char * order = (char *) xmlGetProp(pluginsNode, (const xmlChar *) "order");
|
||||||
group->order = getFOModOrder(order);
|
group->order = fomod_getOrder(order);
|
||||||
xmlFree(order);
|
xmlFree(order);
|
||||||
|
|
||||||
FOModPlugin_t * plugins = NULL;
|
fomod_Plugin_t * plugins = NULL;
|
||||||
int pluginCount = 0;
|
int pluginCount = 0;
|
||||||
|
|
||||||
xmlNodePtr currentPlugin = pluginsNode->children;
|
xmlNodePtr currentPlugin = pluginsNode->children;
|
||||||
while(currentPlugin != NULL) {
|
while(currentPlugin != NULL) {
|
||||||
if(!validateNode(¤tPlugin, true, "plugin", NULL)) {
|
if(!xml_validateNode(¤tPlugin, true, "plugin", NULL)) {
|
||||||
//TODO handle error;
|
//TODO handle error;
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -177,12 +178,12 @@ int parseGroup(xmlNodePtr groupNode, FOModGroup_t* group) {
|
|||||||
|
|
||||||
|
|
||||||
pluginCount += 1;
|
pluginCount += 1;
|
||||||
plugins = realloc(plugins, pluginCount * sizeof(FOModPlugin_t));
|
plugins = realloc(plugins, pluginCount * sizeof(fomod_Plugin_t));
|
||||||
FOModPlugin_t * plugin = &plugins[pluginCount - 1];
|
fomod_Plugin_t * plugin = &plugins[pluginCount - 1];
|
||||||
//initialise everything to 0 and null pointers
|
//initialise everything to 0 and null pointers
|
||||||
memset(plugin, 0, sizeof(FOModPlugin_t));
|
memset(plugin, 0, sizeof(fomod_Plugin_t));
|
||||||
|
|
||||||
plugin->name = freeAndDup(xmlGetProp(currentPlugin, (const xmlChar *) "name"));
|
plugin->name = xml_freeAndDup(xmlGetProp(currentPlugin, (const xmlChar *) "name"));
|
||||||
|
|
||||||
xmlNodePtr nodeElement = currentPlugin->children;
|
xmlNodePtr nodeElement = currentPlugin->children;
|
||||||
while(nodeElement != NULL) {
|
while(nodeElement != NULL) {
|
||||||
@@ -203,6 +204,6 @@ failure:
|
|||||||
//we need to free all of our allocations since we can't expect ou parent function to know what we allocated and what we haven't.
|
//we need to free all of our allocations since we can't expect ou parent function to know what we allocated and what we haven't.
|
||||||
group->plugins = plugins;
|
group->plugins = plugins;
|
||||||
group->pluginCount = pluginCount;
|
group->pluginCount = pluginCount;
|
||||||
freeGroup(group);
|
grp_freeGroup(group);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -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,63 +1,16 @@
|
|||||||
#include "parser.h"
|
#include <fomod.h>
|
||||||
|
#include "group.h"
|
||||||
#include "libxml/tree.h"
|
#include "libxml/tree.h"
|
||||||
#include "xmlUtil.h"
|
#include "xmlUtil.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
//Maybe integrate this into the rest of the code instead of freeing after the fact
|
|
||||||
void freeFOMod(FOMod_t * fomod) {
|
|
||||||
for(int i = 0; i < fomod->condFilesCount; i++) {
|
|
||||||
FOModCondFile_t * condFile = &(fomod->condFiles[i]);
|
|
||||||
for(long fileId = 0; fileId < condFile->fileCount; fileId++) {
|
|
||||||
free(condFile->files[fileId].destination);
|
|
||||||
free(condFile->files[fileId].source);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(long flagId = 0; flagId < condFile->flagCount; flagId++) {
|
|
||||||
FOModFlag_t * flag = &(condFile->requiredFlags[flagId]);
|
|
||||||
free(flag->name);
|
|
||||||
free(flag->value);
|
|
||||||
}
|
|
||||||
free(condFile->files);
|
|
||||||
free(condFile->requiredFlags);
|
|
||||||
}
|
|
||||||
free(fomod->condFiles);
|
|
||||||
free(fomod->moduleImage);
|
|
||||||
free(fomod->moduleName);
|
|
||||||
|
|
||||||
int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **));
|
|
||||||
for(int i = 0; i < size; i++) {
|
|
||||||
free(fomod->requiredInstallFiles[i]);
|
|
||||||
}
|
|
||||||
free(fomod->requiredInstallFiles);
|
|
||||||
|
|
||||||
for(int i = 0; i < fomod->stepCount; i++) {
|
|
||||||
FOModStep_t * step = &fomod->steps[i];
|
|
||||||
for(int groupId = 0; groupId < step->groupCount; groupId++) {
|
|
||||||
FOModGroup_t * group = &step->groups[groupId];
|
|
||||||
freeGroup(group);
|
|
||||||
}
|
|
||||||
for(int flagId = 0; flagId < step->flagCount; flagId++) {
|
|
||||||
FOModFlag_t * flag = &(step->requiredFlags[flagId]);
|
|
||||||
free(flag->name);
|
|
||||||
free(flag->value);
|
|
||||||
}
|
|
||||||
free(step->groups);
|
|
||||||
free(step->requiredFlags);
|
|
||||||
free(step->name);
|
|
||||||
}
|
|
||||||
free(fomod->steps);
|
|
||||||
|
|
||||||
//set every counter to zero and every pointer to null
|
|
||||||
memset(fomod, 0, sizeof(FOMod_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
|
static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
|
||||||
xmlNodePtr requiredFlagsNode = node->children;
|
xmlNodePtr requiredFlagsNode = node->children;
|
||||||
|
|
||||||
while (requiredFlagsNode != NULL) {
|
while (requiredFlagsNode != NULL) {
|
||||||
|
|
||||||
if(!validateNode(&requiredFlagsNode, true, "flagDependency", NULL)) {
|
if(!xml_validateNode(&requiredFlagsNode, true, "flagDependency", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -66,10 +19,10 @@ static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
|
|||||||
if(requiredFlagsNode == NULL)break;
|
if(requiredFlagsNode == NULL)break;
|
||||||
|
|
||||||
step->flagCount += 1;
|
step->flagCount += 1;
|
||||||
step->requiredFlags = realloc(step->requiredFlags, step->flagCount * sizeof(FOModFlag_t));
|
step->requiredFlags = realloc(step->requiredFlags, step->flagCount * sizeof(fomod_Flag_t));
|
||||||
FOModFlag_t * flag = &(step->requiredFlags[step->flagCount - 1]);
|
fomod_Flag_t * flag = &(step->requiredFlags[step->flagCount - 1]);
|
||||||
flag->name = freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "flag"));
|
flag->name = xml_freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "flag"));
|
||||||
flag->value = freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "value"));
|
flag->value = xml_freeAndDup(xmlGetProp(requiredFlagsNode, (const xmlChar *) "value"));
|
||||||
|
|
||||||
requiredFlagsNode = requiredFlagsNode->next;
|
requiredFlagsNode = requiredFlagsNode->next;
|
||||||
}
|
}
|
||||||
@@ -79,11 +32,11 @@ static int parseVisibleNode(xmlNodePtr node, FOModStep_t * step) {
|
|||||||
|
|
||||||
static int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) {
|
static int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) {
|
||||||
xmlChar * optionOrder = xmlGetProp(node, (const xmlChar *)"order");
|
xmlChar * optionOrder = xmlGetProp(node, (const xmlChar *)"order");
|
||||||
step->optionOrder = getFOModOrder((char *)optionOrder);
|
step->optionOrder = fomod_getOrder((char *)optionOrder);
|
||||||
xmlFree(optionOrder);
|
xmlFree(optionOrder);
|
||||||
xmlNodePtr group = node->children;
|
xmlNodePtr group = node->children;
|
||||||
while(group != NULL) {
|
while(group != NULL) {
|
||||||
if(!validateNode(&group, true, "group", NULL)) {
|
if(!xml_validateNode(&group, true, "group", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -92,8 +45,8 @@ static int parseOptionalFileGroup(xmlNodePtr node, FOModStep_t * step) {
|
|||||||
if(group == NULL)break;
|
if(group == NULL)break;
|
||||||
|
|
||||||
step->groupCount += 1;
|
step->groupCount += 1;
|
||||||
step->groups = realloc(step->groups, step->groupCount * sizeof(FOModGroup_t));
|
step->groups = realloc(step->groups, step->groupCount * sizeof(fomod_Group_t));
|
||||||
int status = parseGroup(group, &step->groups[step->groupCount - 1]);
|
int status = grp_parseGroup(group, &step->groups[step->groupCount - 1]);
|
||||||
|
|
||||||
if(status != EXIT_SUCCESS) {
|
if(status != EXIT_SUCCESS) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
@@ -113,7 +66,7 @@ static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCo
|
|||||||
xmlNodePtr stepNode = installStepsNode->children;
|
xmlNodePtr stepNode = installStepsNode->children;
|
||||||
while(stepNode != NULL) {
|
while(stepNode != NULL) {
|
||||||
//skipping the text node
|
//skipping the text node
|
||||||
if(!validateNode(&stepNode, true, "installStep", NULL)) {
|
if(!xml_validateNode(&stepNode, true, "installStep", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -125,7 +78,7 @@ static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCo
|
|||||||
steps = realloc(steps, *stepCount * sizeof(FOModStep_t));
|
steps = realloc(steps, *stepCount * sizeof(FOModStep_t));
|
||||||
|
|
||||||
FOModStep_t * step = &steps[*stepCount - 1];
|
FOModStep_t * step = &steps[*stepCount - 1];
|
||||||
step->name = freeAndDup(xmlGetProp(stepNode, (const xmlChar *)"name"));
|
step->name = xml_freeAndDup(xmlGetProp(stepNode, (const xmlChar *)"name"));
|
||||||
step->requiredFlags = NULL;
|
step->requiredFlags = NULL;
|
||||||
step->flagCount = 0;
|
step->flagCount = 0;
|
||||||
step->groupCount = 0;
|
step->groupCount = 0;
|
||||||
@@ -147,10 +100,10 @@ static FOModStep_t * parseInstallSteps(xmlNodePtr installStepsNode, int * stepCo
|
|||||||
return steps;
|
return steps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) {
|
static int parseDependencies(xmlNodePtr node, fomod_CondFile_t * condFile) {
|
||||||
xmlNodePtr flagNode = node->children;
|
xmlNodePtr flagNode = node->children;
|
||||||
|
|
||||||
if(!validateNode(&flagNode, true, "flagDependency", NULL)) {
|
if(!xml_validateNode(&flagNode, true, "flagDependency", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -158,32 +111,32 @@ static int parseDependencies(xmlNodePtr node, FOModCondFile_t * condFile) {
|
|||||||
|
|
||||||
while(flagNode != NULL) {
|
while(flagNode != NULL) {
|
||||||
condFile->flagCount += 1;
|
condFile->flagCount += 1;
|
||||||
condFile->requiredFlags = realloc(condFile->requiredFlags, condFile->flagCount * sizeof(FOModFlag_t));
|
condFile->requiredFlags = realloc(condFile->requiredFlags, condFile->flagCount * sizeof(fomod_Flag_t));
|
||||||
FOModFlag_t * flag = &(condFile->requiredFlags[condFile->flagCount - 1]);
|
fomod_Flag_t * flag = &(condFile->requiredFlags[condFile->flagCount - 1]);
|
||||||
flag->name = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "flag"));
|
flag->name = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "flag"));
|
||||||
flag->value = freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "value"));
|
flag->value = xml_freeAndDup(xmlGetProp(flagNode, (const xmlChar *) "value"));
|
||||||
|
|
||||||
flagNode = flagNode->next;
|
flagNode = flagNode->next;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) {
|
static int parseFiles(xmlNodePtr node, fomod_CondFile_t * condFile) {
|
||||||
xmlNodePtr filesNode = node->children;
|
xmlNodePtr filesNode = node->children;
|
||||||
|
|
||||||
|
|
||||||
while(filesNode != NULL) {
|
while(filesNode != NULL) {
|
||||||
if(!validateNode(&filesNode, true, "folder", "file", NULL)) {
|
if(!xml_validateNode(&filesNode, true, "folder", "file", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
condFile->fileCount += 1;
|
condFile->fileCount += 1;
|
||||||
condFile->files = realloc(condFile->files, condFile->fileCount * sizeof(FOModFile_t));
|
condFile->files = realloc(condFile->files, condFile->fileCount * sizeof(fomod_File_t));
|
||||||
FOModFile_t * flag = &(condFile->files[condFile->fileCount - 1]);
|
fomod_File_t * flag = &(condFile->files[condFile->fileCount - 1]);
|
||||||
flag->source = freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "source"));
|
flag->source = xml_freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "source"));
|
||||||
flag->destination = freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "destination"));
|
flag->destination = xml_freeAndDup(xmlGetProp(filesNode, (const xmlChar *) "destination"));
|
||||||
flag->priority = 0;
|
flag->priority = 0;
|
||||||
flag->isFolder = xmlStrcmp(filesNode->name, (xmlChar *) "folder") == 0;
|
flag->isFolder = xmlStrcmp(filesNode->name, (xmlChar *) "folder") == 0;
|
||||||
|
|
||||||
@@ -196,7 +149,7 @@ static int parseFiles(xmlNodePtr node, FOModCondFile_t * condFile) {
|
|||||||
static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
||||||
xmlNodePtr patterns = node->children;
|
xmlNodePtr patterns = node->children;
|
||||||
if(patterns != NULL) {
|
if(patterns != NULL) {
|
||||||
if(!validateNode(&patterns, true, "patterns", NULL)) {
|
if(!xml_validateNode(&patterns, true, "patterns", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -205,7 +158,7 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
|||||||
while(currentPattern != NULL) {
|
while(currentPattern != NULL) {
|
||||||
xmlNodePtr patternChild = currentPattern->children;
|
xmlNodePtr patternChild = currentPattern->children;
|
||||||
|
|
||||||
if(!validateNode(&patternChild, true, "pattern", NULL)) {
|
if(!xml_validateNode(&patternChild, true, "pattern", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -213,8 +166,8 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
|||||||
|
|
||||||
while(patternChild != NULL) {
|
while(patternChild != NULL) {
|
||||||
fomod->condFilesCount += 1;
|
fomod->condFilesCount += 1;
|
||||||
fomod->condFiles = realloc(fomod->condFiles, fomod->condFilesCount * sizeof(FOModCondFile_t));
|
fomod->condFiles = realloc(fomod->condFiles, fomod->condFilesCount * sizeof(fomod_CondFile_t));
|
||||||
FOModCondFile_t * condFile = &(fomod->condFiles[fomod->condFilesCount - 1]);
|
fomod_CondFile_t * condFile = &(fomod->condFiles[fomod->condFilesCount - 1]);
|
||||||
|
|
||||||
condFile->fileCount = 0;
|
condFile->fileCount = 0;
|
||||||
condFile->files = NULL;
|
condFile->files = NULL;
|
||||||
@@ -239,7 +192,59 @@ static int parseConditionalInstalls(xmlNodePtr node, FOMod_t * fomod) {
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
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;
|
xmlDocPtr doc;
|
||||||
xmlNodePtr cur;
|
xmlNodePtr cur;
|
||||||
|
|
||||||
@@ -256,7 +261,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
|||||||
|
|
||||||
if(doc == NULL) {
|
if(doc == NULL) {
|
||||||
fprintf(stderr, "Document is not a valid xml file\n");
|
fprintf(stderr, "Document is not a valid xml file\n");
|
||||||
return EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -264,12 +269,13 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
|||||||
if(cur == NULL) {
|
if(cur == NULL) {
|
||||||
fprintf(stderr, "emptyDocument");
|
fprintf(stderr, "emptyDocument");
|
||||||
xmlFreeDoc(doc);
|
xmlFreeDoc(doc);
|
||||||
return EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(xmlStrcmp(cur->name, (const xmlChar *) "config") != 0) {
|
if(xmlStrcmp(cur->name, (const xmlChar *) "config") != 0) {
|
||||||
fprintf(stderr, "document of the wrong type, root node is '%s' instead of config\n", cur->name);
|
fprintf(stderr, "document of the wrong type, root node is '%s' instead of config\n", cur->name);
|
||||||
return EXIT_FAILURE;
|
xmlFreeDoc(doc);
|
||||||
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur = cur->children;
|
cur = cur->children;
|
||||||
@@ -278,22 +284,22 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
|||||||
//might cause some issues. when will c finally support utf-8
|
//might cause some issues. when will c finally support utf-8
|
||||||
fomod->moduleName = (char *)cur->content;
|
fomod->moduleName = (char *)cur->content;
|
||||||
} else if(xmlStrcmp(cur->name, (const xmlChar *) "moduleImage") == 0) {
|
} else if(xmlStrcmp(cur->name, (const xmlChar *) "moduleImage") == 0) {
|
||||||
fomod->moduleImage = freeAndDup(xmlGetProp(cur, (const xmlChar *)"path"));
|
fomod->moduleImage = xml_freeAndDup(xmlGetProp(cur, (const xmlChar *)"path"));
|
||||||
} else if(xmlStrcmp(cur->name, (const xmlChar *)"requiredInstallFiles") == 0) {
|
} else if(xmlStrcmp(cur->name, (const xmlChar *)"requiredInstallFiles") == 0) {
|
||||||
//TODO: support non empty destination.
|
//TODO: support non empty destination.
|
||||||
xmlNodePtr requiredInstallFile = cur->children;
|
xmlNodePtr requiredInstallFile = cur->children;
|
||||||
while(requiredInstallFile != NULL) {
|
while(requiredInstallFile != NULL) {
|
||||||
if(validateNode(&requiredInstallFile, true, "folder", "file", NULL)) {
|
if(xml_validateNode(&requiredInstallFile, true, "folder", "file", NULL)) {
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
printf("%d\n", __LINE__);
|
printf("%d\n", __LINE__);
|
||||||
exit(EXIT_FAILURE);
|
exit(ERR_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = countUntilNull(fomod->requiredInstallFiles, sizeof(char **)) + 2;
|
int size = fomod_countUntilNull(fomod->requiredInstallFiles, sizeof(char **)) + 2;
|
||||||
fomod->requiredInstallFiles = realloc(fomod->requiredInstallFiles, sizeof(char *) * size);
|
fomod->requiredInstallFiles = realloc(fomod->requiredInstallFiles, sizeof(char *) * size);
|
||||||
//ensure it is null terminated
|
//ensure it is null terminated
|
||||||
fomod->requiredInstallFiles[size - 1] = NULL;
|
fomod->requiredInstallFiles[size - 1] = NULL;
|
||||||
fomod->requiredInstallFiles[size - 2] = freeAndDup(xmlGetProp(requiredInstallFile, (const xmlChar *)"source"));
|
fomod->requiredInstallFiles[size - 2] = xml_freeAndDup(xmlGetProp(requiredInstallFile, (const xmlChar *)"source"));
|
||||||
|
|
||||||
requiredInstallFile = cur->children;
|
requiredInstallFile = cur->children;
|
||||||
}
|
}
|
||||||
@@ -301,11 +307,11 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
|||||||
if(fomod->steps != NULL) {
|
if(fomod->steps != NULL) {
|
||||||
fprintf(stderr, "Multiple 'installSteps' tags");
|
fprintf(stderr, "Multiple 'installSteps' tags");
|
||||||
//TODO: handle error
|
//TODO: handle error
|
||||||
return EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlChar * stepOrder = xmlGetProp(cur, (xmlChar *)"order");
|
xmlChar * stepOrder = xmlGetProp(cur, (xmlChar *)"order");
|
||||||
fomod->stepOrder = getFOModOrder((char *)stepOrder);
|
fomod->stepOrder = fomod_getOrder((char *)stepOrder);
|
||||||
xmlFree(stepOrder);
|
xmlFree(stepOrder);
|
||||||
|
|
||||||
int stepCount = 0;
|
int stepCount = 0;
|
||||||
@@ -315,7 +321,7 @@ int parseFOMod(const char * fomodFile, FOMod_t* fomod) {
|
|||||||
fprintf(stderr, "Failed to parse the install steps\n");
|
fprintf(stderr, "Failed to parse the install steps\n");
|
||||||
|
|
||||||
//TODO: manage the error properly
|
//TODO: manage the error properly
|
||||||
return EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
fomod->steps = steps;
|
fomod->steps = steps;
|
||||||
@@ -326,6 +332,14 @@ int 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 EXIT_SUCCESS;
|
return ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,13 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
char * freeAndDup(xmlChar * line) {
|
char * xml_freeAndDup(xmlChar * line) {
|
||||||
char * free = strdup((const char *) line);
|
char * free = strdup((const char *) line);
|
||||||
xmlFree(line);
|
xmlFree(line);
|
||||||
return free;
|
return free;
|
||||||
}
|
}
|
||||||
|
|
||||||
FOModOrder_t getFOModOrder(const char * order) {
|
fomod_Order_t fomod_getOrder(const char * order) {
|
||||||
if(order == NULL || strcmp(order, "Ascending") == 0) {
|
if(order == NULL || strcmp(order, "Ascending") == 0) {
|
||||||
return ASC;
|
return ASC;
|
||||||
} else if(strcmp(order, "Explicit") == 0) {
|
} else if(strcmp(order, "Explicit") == 0) {
|
||||||
@@ -21,14 +21,14 @@ FOModOrder_t getFOModOrder(const char * order) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//replace \ in the path by /
|
//replace \ in the path by /
|
||||||
void fixPath(char * path) {
|
void xml_fixPath(char * path) {
|
||||||
while(*path != '\0') {
|
while(*path != '\0') {
|
||||||
if(*path == '\\')*path = '/';
|
if(*path == '\\')*path = '/';
|
||||||
path++;
|
path++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int countUntilNull(void * pointers, size_t typeSize) {
|
int fomod_countUntilNull(void * pointers, size_t typeSize) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
char * arithmetic = (char *)pointers;
|
char * arithmetic = (char *)pointers;
|
||||||
while(arithmetic != NULL) {
|
while(arithmetic != NULL) {
|
||||||
@@ -40,7 +40,7 @@ int countUntilNull(void * pointers, size_t typeSize) {
|
|||||||
|
|
||||||
//names cannot contain false
|
//names cannot contain false
|
||||||
//need to be null terminated
|
//need to be null terminated
|
||||||
bool validateNode(xmlNodePtr * node, bool skipText, const char * names, ...) {
|
bool xml_validateNode(xmlNodePtr * node, bool skipText, const char * names, ...) {
|
||||||
va_list namesPtr;
|
va_list namesPtr;
|
||||||
|
|
||||||
while(*node != NULL && xmlStrcmp((*node)->name, (const xmlChar *)"text") == 0) {
|
while(*node != NULL && xmlStrcmp((*node)->name, (const xmlChar *)"text") == 0) {
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef __XML_UTIL_H__
|
||||||
|
#define __XML_UTIL_H__
|
||||||
|
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <fomod.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param node a pointer to the current node pointer (xmlNode **)
|
||||||
|
* @param skipText if a text element is found skip it.
|
||||||
|
* @param names variadic of the valid names.
|
||||||
|
* @return return true if it found a valid node
|
||||||
|
*/
|
||||||
|
bool xml_validateNode(xmlNodePtr * node, bool skipText, const char * names, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free memory of and xmlChar and return a strdup version. just to make sure there is nothing remaining in libxml
|
||||||
|
*/
|
||||||
|
char * xml_freeAndDup(xmlChar * line);
|
||||||
|
|
||||||
|
|
||||||
|
fomod_Order_t fomod_getOrder(const char * order);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief replace / by \
|
||||||
|
* @param path
|
||||||
|
*/
|
||||||
|
void xml_fixPath(char * path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count the number of step before null
|
||||||
|
*
|
||||||
|
* @param pointers pointer to the list
|
||||||
|
* @param typeSize size of each element of the list
|
||||||
|
* @return size
|
||||||
|
*/
|
||||||
|
int fomod_countUntilNull(void * pointers, size_t typeSize);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
#include <constants.h>
|
||||||
|
#include <gameData.h>
|
||||||
|
#include <steam.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
error_t gameData_getDataPath(int appid, char ** destination) {
|
||||||
|
GHashTable * gamePaths;
|
||||||
|
error_t status = steam_searchGames(&gamePaths);
|
||||||
|
if(status == ERR_FAILURE) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gameId = steam_gameIdFromAppId(appid);
|
||||||
|
if(gameId < 0 ) {
|
||||||
|
fprintf(stderr, "invalid appid");
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * path = g_hash_table_lookup(gamePaths, &gameId);
|
||||||
|
|
||||||
|
if(path == NULL) {
|
||||||
|
fprintf(stderr, "game not found\n");
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * gameFolder = g_build_filename(path, "steamapps/common", GAMES_NAMES[gameId], NULL);
|
||||||
|
//the folder names for older an newer titles
|
||||||
|
char * dataFolderOld = g_build_filename(gameFolder, "Data Files", NULL);
|
||||||
|
char * dataFolderNew = g_build_filename(gameFolder, "Data", NULL);
|
||||||
|
|
||||||
|
g_free(gameFolder);
|
||||||
|
|
||||||
|
*destination = NULL;
|
||||||
|
//struct stat sb;
|
||||||
|
if(access(dataFolderOld, F_OK) == 0) {
|
||||||
|
*destination = strdup(dataFolderOld);
|
||||||
|
} else if(access(dataFolderNew, F_OK) == 0) {
|
||||||
|
*destination = strdup(dataFolderNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(dataFolderNew);
|
||||||
|
g_free(dataFolderOld);
|
||||||
|
|
||||||
|
if(*destination == NULL) return ERR_FAILURE;
|
||||||
|
else return ERR_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <install.h>
|
||||||
|
|
||||||
|
#include <constants.h>
|
||||||
|
#include "archives.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
error_t install_addMod(char * filePath, int appId) {
|
||||||
|
error_t resultError = ERR_SUCCESS;
|
||||||
|
if (access(filePath, F_OK) != 0) {
|
||||||
|
fprintf(stderr, "File not found\n");
|
||||||
|
resultError = ERR_FAILURE;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
goto exit2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * filename = file_extractFileName(filePath);
|
||||||
|
const char * extension = file_extractExtension(filename);
|
||||||
|
char * lowercaseExtension = g_ascii_strdown(extension, -1);
|
||||||
|
|
||||||
|
char appIdStr[20];
|
||||||
|
sprintf(appIdStr, "%d", appId);
|
||||||
|
char * outdir = g_build_filename(configFolder, MOD_FOLDER_NAME, appIdStr, filename, NULL);
|
||||||
|
|
||||||
|
g_mkdir_with_parents(outdir, 0755);
|
||||||
|
|
||||||
|
int returnValue = EXIT_SUCCESS;
|
||||||
|
printf("Adding mod, this process can be slow depending on your hardware\n");
|
||||||
|
if(strcmp(lowercaseExtension, "rar") == 0) {
|
||||||
|
returnValue = archive_unrar(filePath, outdir);
|
||||||
|
} else if (strcmp(lowercaseExtension, "zip") == 0) {
|
||||||
|
returnValue = archive_unzip(filePath, outdir);
|
||||||
|
} else if (strcmp(lowercaseExtension, "7z") == 0) {
|
||||||
|
returnValue = archive_un7z(filePath, outdir);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unsupported format only zip/7z/rar are supported\n");
|
||||||
|
returnValue = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(returnValue == EXIT_FAILURE)
|
||||||
|
resultError = ERR_FAILURE;
|
||||||
|
|
||||||
|
printf("Done\n");
|
||||||
|
|
||||||
|
free(lowercaseExtension);
|
||||||
|
free(outdir);
|
||||||
|
exit2:
|
||||||
|
free(configFolder);
|
||||||
|
exit:
|
||||||
|
return resultError;
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
#include <constants.h>
|
||||||
|
|
||||||
|
#include "loadOrder.h"
|
||||||
|
#include "steam.h"
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "gameData.h"
|
||||||
|
|
||||||
|
//TODO: detect if the game is running
|
||||||
|
//TODO: deploy the game
|
||||||
|
|
||||||
|
error_t order_listPlugins(int appid, GList ** plugins) {
|
||||||
|
//save appid parsing
|
||||||
|
//TODO: apply a similar mechanism everywhere
|
||||||
|
size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1;
|
||||||
|
char appidStr[appidStrLen];
|
||||||
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
|
char * dataFolder = NULL;
|
||||||
|
error_t error = gameData_getDataPath(appid, &dataFolder);
|
||||||
|
if(error != ERR_SUCCESS) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//esp && esm files are loadable
|
||||||
|
DIR * d = opendir(dataFolder);
|
||||||
|
struct dirent *dir;
|
||||||
|
if (d != NULL) {
|
||||||
|
while ((dir = readdir(d)) != NULL) {
|
||||||
|
const char * extension = file_extractExtension(dir->d_name);
|
||||||
|
//folder don't have file extensions
|
||||||
|
if(extension == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(strcmp(extension, "esp") == 0 || strcmp(extension, "esm") == 0) {
|
||||||
|
*plugins = g_list_append(*plugins, strdup(dir->d_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dataFolder);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeCRLF_CR_LF(char * string) {
|
||||||
|
int size = strlen(string);
|
||||||
|
if(string[size - 1] == '\r') size--;
|
||||||
|
if(string[size - 1] == '\n') size--;
|
||||||
|
if(string[size - 1] == '\r') size--;
|
||||||
|
string[size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t order_getLoadOrder(int appid, GList ** order) {
|
||||||
|
|
||||||
|
GHashTable * gamePaths;
|
||||||
|
error_t status = steam_searchGames(&gamePaths);
|
||||||
|
if(status == ERR_FAILURE) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * l_plugins = NULL;
|
||||||
|
error_t error = order_listPlugins(appid, &l_plugins);
|
||||||
|
if(error == ERR_FAILURE)
|
||||||
|
return ERR_FAILURE;
|
||||||
|
|
||||||
|
|
||||||
|
int gameId = steam_gameIdFromAppId(appid);
|
||||||
|
if(gameId < 0 ) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1;
|
||||||
|
char appidStr[appidStrLen];
|
||||||
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
|
const char * path = g_hash_table_lookup(gamePaths, &gameId);
|
||||||
|
|
||||||
|
|
||||||
|
//this is the path i would use in windows but it seems it is not avaliable in all wine versions.
|
||||||
|
//char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/AppData/Local/", GAMES_NAMES[gameId], "loadorder.txt", NULL);
|
||||||
|
char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/Local Settings/Application Data/", GAMES_NAMES[gameId], "loadorder.txt", NULL);
|
||||||
|
|
||||||
|
GList * l_currentLoadOrder = NULL;
|
||||||
|
if(access(loadOrderPath, R_OK) == 0) {
|
||||||
|
FILE * f_loadOrder = fopen(loadOrderPath, "r");
|
||||||
|
|
||||||
|
size_t length = 0;
|
||||||
|
char * line = NULL;
|
||||||
|
|
||||||
|
while(getline(&line, &length, f_loadOrder) > 0) {
|
||||||
|
if(line[0] == '#' || line[0] == '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
removeCRLF_CR_LF(line);
|
||||||
|
|
||||||
|
l_currentLoadOrder = g_list_append(l_currentLoadOrder, strdup(line));
|
||||||
|
}
|
||||||
|
if(line != NULL)free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GList * l_currentLoadOrderCursor = l_currentLoadOrder;
|
||||||
|
GList * l_completeLoadOrder = NULL;
|
||||||
|
|
||||||
|
while(l_currentLoadOrderCursor != NULL) {
|
||||||
|
char * modName = l_currentLoadOrderCursor->data;
|
||||||
|
//TODO: finir sa
|
||||||
|
GList * mod = g_list_find_custom(l_plugins, modName, (GCompareFunc)strcmp);
|
||||||
|
if(mod == NULL) {
|
||||||
|
//The plugin is no longer installed
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
l_completeLoadOrder = g_list_append(l_completeLoadOrder, strdup(modName));
|
||||||
|
}
|
||||||
|
l_currentLoadOrderCursor = g_list_next(l_currentLoadOrderCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
*order = l_completeLoadOrder;
|
||||||
|
|
||||||
|
g_list_free_full(l_plugins, free);
|
||||||
|
g_list_free_full(l_currentLoadOrder, free);
|
||||||
|
g_free(loadOrderPath);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t order_setLoadOrder(int appid, GList * loadOrder) {
|
||||||
|
GHashTable * gamePaths;
|
||||||
|
error_t status = steam_searchGames(&gamePaths);
|
||||||
|
if(status == ERR_FAILURE) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gameId = steam_gameIdFromAppId(appid);
|
||||||
|
if(gameId < 0 ) {
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t appidStrLen = snprintf(NULL, 0, "%d", appid) + 1;
|
||||||
|
char appidStr[appidStrLen];
|
||||||
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
|
const char * path = g_hash_table_lookup(gamePaths, &gameId);
|
||||||
|
char * loadOrderPath = g_build_filename(path, "steamapps/compatdata", appidStr, "pfx/drive_c/users/steamuser/AppData/Local/", GAMES_NAMES[gameId], "loadorder.txt", NULL);
|
||||||
|
|
||||||
|
FILE * f_loadOrder = fopen(loadOrderPath, "w");
|
||||||
|
while(loadOrder != NULL) {
|
||||||
|
fwrite(loadOrder->data, sizeof(char), strlen(loadOrder->data), f_loadOrder);
|
||||||
|
loadOrder = g_list_next(loadOrder);
|
||||||
|
}
|
||||||
|
fclose(f_loadOrder);
|
||||||
|
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: support compression since it can change how we read the file
|
||||||
|
//https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format#Records
|
||||||
|
//https://www.mwmythicmods.com/argent/tech/es_format.html
|
||||||
|
error_t order_getModDependencies(const char * esmPath, GList ** dependencies) {
|
||||||
|
FILE * file = fopen(esmPath, "r");
|
||||||
|
|
||||||
|
char sectionName[5];
|
||||||
|
sectionName[4] = '\0';
|
||||||
|
fread(sectionName, sizeof(char), 4, file);
|
||||||
|
|
||||||
|
size_t sizeFieldSize = 0;
|
||||||
|
u_int8_t recordHeaderToIgnore = 0;
|
||||||
|
|
||||||
|
//the field "length" in the sub-record change between games.
|
||||||
|
//TES4 => Fallout4 Oblivion Skyrim(+SE)
|
||||||
|
//TES3 => Morrowind
|
||||||
|
if(strcmp(sectionName, "TES3") == 0) {
|
||||||
|
printf("Using tes3 file format\n");
|
||||||
|
recordHeaderToIgnore = 8;
|
||||||
|
sizeFieldSize = 4;
|
||||||
|
} else if(strcmp(sectionName, "TES4") == 0) {
|
||||||
|
printf("Using tes4 file format\n");
|
||||||
|
recordHeaderToIgnore = 16;
|
||||||
|
sizeFieldSize = 2;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unrecognized file format %s\n", sectionName);
|
||||||
|
fclose(file);
|
||||||
|
return ERR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u_int32_t lengthVal = 0;
|
||||||
|
fread(&lengthVal, 4, 1, file);
|
||||||
|
//ignore the rest of the data
|
||||||
|
fseek(file, recordHeaderToIgnore, SEEK_CUR);
|
||||||
|
|
||||||
|
int64_t length = lengthVal;
|
||||||
|
while(length > 0) {
|
||||||
|
char sectionName[5];
|
||||||
|
sectionName[4] = '\0';
|
||||||
|
fread(sectionName, sizeof(char), 4, file);
|
||||||
|
unsigned long subsectionLength = 0;
|
||||||
|
fread(&subsectionLength, sizeFieldSize, 1, file);
|
||||||
|
|
||||||
|
length -= 8 + subsectionLength;
|
||||||
|
if(strcmp(sectionName, "MAST") == 0) {
|
||||||
|
char * dependency = malloc(subsectionLength + 1);
|
||||||
|
dependency[subsectionLength] = '\0';
|
||||||
|
fread(dependency, sizeof(char), subsectionLength, file);
|
||||||
|
*dependencies = g_list_append(*dependencies, dependency);
|
||||||
|
} else {
|
||||||
|
fseek(file, subsectionLength, SEEK_CUR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
#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 "main.h"
|
#include <constants.h>
|
||||||
|
|
||||||
#include "getHome.h"
|
#include "getHome.h"
|
||||||
|
|
||||||
typedef struct Mod {
|
typedef struct Mod {
|
||||||
@@ -22,12 +23,12 @@ static gint compareOrder(const void * a, const void * b) {
|
|||||||
return ModA->modId - ModB->modId;
|
return ModA->modId - ModB->modId;
|
||||||
}
|
}
|
||||||
|
|
||||||
GList * listMods(int appid) {
|
GList * order_listMods(int appid) {
|
||||||
char appidStr[10];
|
char appidStr[10];
|
||||||
sprintf(appidStr, "%d", appid);
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
char * home = getHome();
|
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);
|
free(home);
|
||||||
|
|
||||||
GList * list = NULL;
|
GList * list = NULL;
|
||||||
@@ -94,15 +95,15 @@ GList * listMods(int appid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int swapPlace(int appid, int modIdA, int modIdB) {
|
error_t order_swapPlace(int appid, int modIdA, int modIdB) {
|
||||||
char appidStr[10];
|
char appidStr[10];
|
||||||
sprintf(appidStr, "%d", appid);
|
sprintf(appidStr, "%d", appid);
|
||||||
|
|
||||||
char * home = getHome();
|
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);
|
free(home);
|
||||||
|
|
||||||
GList * list = listMods(appid);
|
GList * list = order_listMods(appid);
|
||||||
GList * listA = list;
|
GList * listA = list;
|
||||||
GList * listB = list;
|
GList * listB = list;
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ int swapPlace(int appid, int modIdA, int modIdB) {
|
|||||||
|
|
||||||
if(listA == NULL || listB == NULL) {
|
if(listA == NULL || listB == NULL) {
|
||||||
fprintf(stderr, "Invalid modId\n");
|
fprintf(stderr, "Invalid modId\n");
|
||||||
return EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * modAFolder = g_build_filename(modFolder, listA->data, ORDER_FILE, NULL);
|
char * modAFolder = g_build_filename(modFolder, listA->data, ORDER_FILE, NULL);
|
||||||
@@ -131,7 +132,7 @@ int swapPlace(int appid, int modIdA, int modIdB) {
|
|||||||
|
|
||||||
if(fileA == NULL || fileB == NULL) {
|
if(fileA == NULL || fileB == NULL) {
|
||||||
fprintf(stderr, "Error could not open order file\n");
|
fprintf(stderr, "Error could not open order file\n");
|
||||||
return EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(fileA, "%d", modIdB);
|
fprintf(fileA, "%d", modIdB);
|
||||||
@@ -141,5 +142,5 @@ int swapPlace(int appid, int modIdA, int modIdB) {
|
|||||||
fclose(fileB);
|
fclose(fileB);
|
||||||
|
|
||||||
g_list_free_full(list, free);
|
g_list_free_full(list, free);
|
||||||
return EXIT_SUCCESS;
|
return ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <sys/mount.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "overlayfs.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
overlay_errors_t overlay_mount(char ** sources, const char * dest, const char * upperdir, const char * workdir) {
|
||||||
|
char * lowerdir = g_strjoinv(":", sources);
|
||||||
|
char * mountData = g_strjoin("", "lowerdir=", lowerdir, ",workdir=", workdir, ",upperdir=", upperdir, NULL);
|
||||||
|
|
||||||
|
overlay_errors_t result = SUCESS;
|
||||||
|
|
||||||
|
if(access("/usr/bin/fuse-overlayfs", F_OK) == 0) {
|
||||||
|
int pid = fork();
|
||||||
|
if(pid == 0) {
|
||||||
|
//exit is used to get the return value when using waitpid
|
||||||
|
exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-o", mountData, dest, NULL));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
int returnValue = 0;
|
||||||
|
waitpid(pid, &returnValue, 0);
|
||||||
|
if(returnValue != 0) {
|
||||||
|
result = FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = NOT_INSTALLED;
|
||||||
|
}
|
||||||
|
free(lowerdir);
|
||||||
|
free(mountData);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
#ifndef __OVERLAY_H__
|
#ifndef __OVERLAY_H__
|
||||||
#define __OVERLAY_H__
|
#define __OVERLAY_H__
|
||||||
|
|
||||||
|
typedef enum overlay_errors { SUCESS, NOT_INSTALLED, FAILURE } overlay_errors_t;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Overlayfs is what make it possible to deploy to the game files without altering anything.
|
* @brief Overlayfs is what make it possible to deploy to the game files without altering anything.
|
||||||
* it allows us to overlay multiple folder over the game files. like appling filters to an image.
|
* it allows us to overlay multiple folder over the game files. like appling filters to an image.
|
||||||
@@ -10,6 +13,6 @@
|
|||||||
* @param workdir a directory that will contains only temporary files.
|
* @param workdir a directory that will contains only temporary files.
|
||||||
* @return int error code
|
* @return int error code
|
||||||
*/
|
*/
|
||||||
int overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir);
|
overlay_errors_t overlay_mount(char ** sources, const char * dest, const char * upperdir, const char * workdir);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "steam.h"
|
#include "steam.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "getHome.h"
|
#include "getHome.h"
|
||||||
|
|
||||||
|
#include <constants.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -43,7 +46,7 @@ static int getFiledId(const char * field) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * status) {
|
static steam_Libraries_t * parseVDF(const char * path, size_t * size, int * status) {
|
||||||
FILE * fd = fopen(path, "r");
|
FILE * fd = fopen(path, "r");
|
||||||
char * line = NULL;
|
char * line = NULL;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
@@ -52,7 +55,7 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu
|
|||||||
|
|
||||||
bool inQuotes = false;
|
bool inQuotes = false;
|
||||||
|
|
||||||
ValveLibraries_t * libraries = NULL;
|
steam_Libraries_t * libraries = NULL;
|
||||||
*size = 0;
|
*size = 0;
|
||||||
|
|
||||||
//skip the "libraryfolders" label & the first opening brace
|
//skip the "libraryfolders" label & the first opening brace
|
||||||
@@ -89,7 +92,7 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
char * value = strndup(buffer, bufferIndex);
|
char * value = strndup(buffer, bufferIndex);
|
||||||
ValveLibraries_t * library = &libraries[*size - 1];
|
steam_Libraries_t * library = &libraries[*size - 1];
|
||||||
switch (nextFieldToFill) {
|
switch (nextFieldToFill) {
|
||||||
case FIELD_PATH:
|
case FIELD_PATH:
|
||||||
library->path = value;
|
library->path = value;
|
||||||
@@ -119,7 +122,7 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu
|
|||||||
case FIELD_APPS:
|
case FIELD_APPS:
|
||||||
if(isAppId) {
|
if(isAppId) {
|
||||||
library->appsCount++;
|
library->appsCount++;
|
||||||
library->apps = realloc(library->apps, library->appsCount * sizeof(ValveApp_t));
|
library->apps = realloc(library->apps, library->appsCount * sizeof(steam_App_t));
|
||||||
unsigned int appid = strtoul(value, NULL, 10);
|
unsigned int appid = strtoul(value, NULL, 10);
|
||||||
library->apps[library->appsCount - 1].appid = appid;
|
library->apps[library->appsCount - 1].appid = appid;
|
||||||
} else {
|
} else {
|
||||||
@@ -145,8 +148,8 @@ static ValveLibraries_t * parseVDF(const char * path, size_t * size, int * statu
|
|||||||
braceDepth++;
|
braceDepth++;
|
||||||
if(braceDepth == 1) {
|
if(braceDepth == 1) {
|
||||||
*size += 1;
|
*size += 1;
|
||||||
libraries = realloc(libraries, sizeof(ValveLibraries_t) * (*size));
|
libraries = realloc(libraries, sizeof(steam_Libraries_t) * (*size));
|
||||||
memset(&libraries[*size - 1], 0, sizeof(ValveLibraries_t));
|
memset(&libraries[*size - 1], 0, sizeof(steam_Libraries_t));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '}':
|
case '}':
|
||||||
@@ -178,7 +181,7 @@ exit:
|
|||||||
return libraries;
|
return libraries;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeLibraries(ValveLibraries_t * libraries, int size) {
|
static void freeLibraries(steam_Libraries_t * libraries, int size) {
|
||||||
for(int i = 0; i < size; i++) {
|
for(int i = 0; i < size; i++) {
|
||||||
free(libraries[i].path);
|
free(libraries[i].path);
|
||||||
free(libraries[i].label);
|
free(libraries[i].label);
|
||||||
@@ -190,11 +193,21 @@ static void freeLibraries(ValveLibraries_t * libraries, int size) {
|
|||||||
free(libraries);
|
free(libraries);
|
||||||
}
|
}
|
||||||
|
|
||||||
GHashTable* search_games(int * status) {
|
static GHashTable* gameTableSingleton = NULL;
|
||||||
ValveLibraries_t * libraries = NULL;
|
|
||||||
|
void steam_freeGameTable() {
|
||||||
|
if(gameTableSingleton != NULL)g_hash_table_destroy(gameTableSingleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t steam_searchGames(GHashTable ** p_hashTable) {
|
||||||
|
if(gameTableSingleton != NULL) {
|
||||||
|
*p_hashTable = gameTableSingleton;
|
||||||
|
return ERR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
steam_Libraries_t * libraries = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
char * home = getHome();
|
char * home = getHome();
|
||||||
*status = EXIT_SUCCESS;
|
|
||||||
|
|
||||||
for(unsigned long i = 0; i < LEN(steamLibraries); i++) {
|
for(unsigned long i = 0; i < LEN(steamLibraries); i++) {
|
||||||
char * path = g_build_filename(home, steamLibraries[i], "steamapps/libraryfolders.vdf", NULL);
|
char * path = g_build_filename(home, steamLibraries[i], "steamapps/libraryfolders.vdf", NULL);
|
||||||
@@ -212,8 +225,7 @@ GHashTable* search_games(int * status) {
|
|||||||
|
|
||||||
free(home);
|
free(home);
|
||||||
if(libraries == NULL) {
|
if(libraries == NULL) {
|
||||||
*status = EXIT_FAILURE;
|
return ERR_FAILURE;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GHashTable* table = g_hash_table_new_full(g_int_hash, g_int_equal, free, free);
|
GHashTable* table = g_hash_table_new_full(g_int_hash, g_int_equal, free, free);
|
||||||
@@ -221,7 +233,7 @@ GHashTable* search_games(int * status) {
|
|||||||
//fill the table
|
//fill the table
|
||||||
for(unsigned long i = 0; i < size; i++) {
|
for(unsigned long i = 0; i < size; i++) {
|
||||||
for(unsigned long j = 0; j < libraries[i].appsCount; j++) {
|
for(unsigned long j = 0; j < libraries[i].appsCount; j++) {
|
||||||
int gameId = getGameIdFromAppId(libraries[i].apps[j].appid);
|
int gameId = steam_gameIdFromAppId(libraries[i].apps[j].appid);
|
||||||
if(gameId >= 0) {
|
if(gameId >= 0) {
|
||||||
int * key = malloc(sizeof(int));
|
int * key = malloc(sizeof(int));
|
||||||
*key = gameId;
|
*key = gameId;
|
||||||
@@ -231,11 +243,13 @@ GHashTable* search_games(int * status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
freeLibraries(libraries, size);
|
freeLibraries(libraries, size);
|
||||||
return table;
|
*p_hashTable = table;
|
||||||
|
gameTableSingleton = table;
|
||||||
|
return ERR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int getGameIdFromAppId(u_int32_t appid) {
|
int steam_gameIdFromAppId(u_int32_t appid) {
|
||||||
for(unsigned long k = 0; k < LEN(GAMES_APPIDS); k++) {
|
for(unsigned long k = 0; k < LEN(GAMES_APPIDS); k++) {
|
||||||
if(appid == GAMES_APPIDS[k]) {
|
if(appid == GAMES_APPIDS[k]) {
|
||||||
return k;
|
return k;
|
||||||
@@ -243,3 +257,33 @@ int getGameIdFromAppId(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,44 +0,0 @@
|
|||||||
#include <sys/mount.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include "overlayfs.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int overlayMount(char ** sources, const char * dest, const char * upperdir, const char * workdir) {
|
|
||||||
char * lowerdir = g_strjoinv(":", sources);
|
|
||||||
char * mountData = g_strjoin("", "lowerdir=", lowerdir, ",workdir=", workdir, ",upperdir=", upperdir, NULL);
|
|
||||||
int result = mount("overlay", "none", "overlay", MS_RDONLY, mountData);
|
|
||||||
if(result < 0) {
|
|
||||||
//this is very important for a lot of filesystems
|
|
||||||
//even ext4 can be incompatible with the kernel's implementation
|
|
||||||
//with some flags such as casefold
|
|
||||||
if(access("/usr/bin/fuse-overlayfs", F_OK) == 0) {
|
|
||||||
|
|
||||||
int pid = fork();
|
|
||||||
if(pid == 0) {
|
|
||||||
//exit is used to get the return value when using waitpid
|
|
||||||
exit(execl("/usr/bin/fuse-overlayfs", "/usr/bin/fuse-overlayfs", "-r", "-o", mountData, dest, NULL));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
int returnValue = 0;
|
|
||||||
waitpid(pid, &returnValue, 0);
|
|
||||||
free(lowerdir);
|
|
||||||
free(mountData);
|
|
||||||
if(returnValue != 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
free(lowerdir);
|
|
||||||
free(mountData);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(lowerdir);
|
|
||||||
free(mountData);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user