diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | LICENSE | 22 | ||||
-rw-r--r-- | Makefile | 127 | ||||
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | src/common.c | 21 | ||||
-rw-r--r-- | src/common.h | 28 | ||||
-rw-r--r-- | src/dxvk.c | 98 | ||||
-rw-r--r-- | src/dxvk.h | 9 | ||||
-rw-r--r-- | src/main.c | 47 | ||||
-rw-r--r-- | src/main.h | 7 | ||||
-rw-r--r-- | src/net.c | 100 | ||||
-rw-r--r-- | src/net.h | 10 | ||||
-rw-r--r-- | src/wine.c | 97 | ||||
-rw-r--r-- | src/wine.h | 9 |
14 files changed, 588 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1037633 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin +obj +*.tar* @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2020 Jan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bae52c5 --- /dev/null +++ b/Makefile @@ -0,0 +1,127 @@ +# GENERAL VARIABLES +NAME := polecat +VERSION := 0.1.0 +TARGET ?= debug +DEBUG := 0 +ifeq ($(TARGET),debug) + DEBUG := 1 +endif + +PRETTY_OUTPUT ?= 1 +STATIC ?= 0 + +# CROSS COMPILATION SETUP +CROSS ?= + +# COMMAND VARIABLES +RM := rm -rf +MKDIR := mkdir -p +DOXYGEN := doxygen +PKGCONFIG := $(CROSS)pkg-config +CURLCONFIG := $(CROSS)curl-config + + +ifndef PLATFORM + PLATFORM := $(HOSTPLATFORM) +endif + +# CROSS COMPILATION ADAPTION + +# DIRECTORIES +BIN_DIR := bin +OBJ_DIR := obj + +SRC_DIR := src + +FILES := $(filter-out $(BIN_DIR) $(OBJ_DIR), $(wildcard *)) + +# FLAGS +COMMONFLAGS := +ifneq ($(STATIC), 0) + COMMONFLAGS += -static +endif +ifeq ($(DEBUG),0) + COMMONFLAGS += -O2 +else + COMMONFLAGS += -g +endif +CFLAGS := $(COMMONFLAGS) -Wall `$(PKGCONFIG) json-c --cflags` `$(CURLCONFIG) --cflags` +LDFLAGS := `$(PKGCONFIG) json-c --libs` `$(CURLCONFIG) --libs` +DEFINES := -DNAME=\"$(NAME)\" -DVERSION=\"$(VERSION)\" +ifeq ($(DEBUG),1) + DEFINES += -DDEBUG +endif + +# SOURCE CODE AND OBJECT FILES +CC_SRC_FILES := $(wildcard $(SRC_DIR)/*.c) + +OBJ_FILES := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/$(TARGET)/%.o, $(CC_SRC_FILES)) + + +# TARGETS +default: $(BIN_DIR)/$(TARGET)/$(NAME)$(OUT_EXT) + +all: $(BIN_DIR)/$(TARGET)/$(NAME)$(OUT_EXT) docs test + +$(BIN_DIR)/$(TARGET): + ${MKDIR} $@ + +$(OBJ_DIR)/$(TARGET): + ${MKDIR} $@ + +$(BIN_DIR)/$(TARGET)/$(NAME)$(OUT_EXT): $(OBJ_FILES) | $(BIN_DIR)/$(TARGET) + ${LINK_STATUS} + ${RECIPE_IF} ${CROSS}${CC} -o$@ $^ ${CFLAGS} ${DEFINES} ${LDFLAGS} ${RECIPE_RESULT_LINK} + +$(OBJ_DIR)/$(TARGET)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)/$(TARGET) + ${COMPILE_STATUS} + ${RECIPE_IF} ${CROSS}${CC} -c -o$@ $< ${CFLAGS} ${DEFINES} ${RECIPE_RESULT_COMPILE} + +clean: + ${RM} ${BIN_DIR} ${OBJ_DIR} ${NAME}.tar 2> /dev/null + +loc: + -find ${SRC_DIR} ${INC_DIR} -name '*.cpp' -o -name '*.c' -o -name '*.h' -o -name '*.hpp' -type f | xargs wc -l + +tar: ${NAME}.tar + +dist: tar + +${NAME}.tar: + ${ARCHIVE_STATUS} + $(RECIPE_IF) tar -cf $@ ${FILES} $(RECIPE_RESULT_ARCHIVE) + +.PHONY: default all clean docs loc tar dist + + +ifeq ($(PRETTY_OUTPUT),1) +.SILENT: +RECIPE_IF = if +COMPILE_STATUS = printf "\033[K\033[0mBuilding object \033[1m$@\033[0m...\033[0m\r" +COMPILE_OK = printf "\033[K\033[0;32mBuilt object \033[1;32m$@\033[0;32m \033[0m\n" +COMPILE_FAILED = printf "\033[K\033[0;31mFailed building \033[1;31m$@\033[0;31m from\033[0m \033[1;31m$<\033[0;31m!\033[0m\n"; exit 1 +RECIPE_RESULT_COMPILE = ; then $(COMPILE_OK); else $(COMPILE_FAILED); fi +ARCHIVE_STATUS = printf "\033[K\033[0mCreating library archive \033[1m$@\033[0m...\033[0m\r" +ARCHIVE_OK = printf "\033[K\033[0;32mCreated library archive \033[1;32m$@\033[0;32m \033[0m\n" +ARCHIVE_FAILED = printf "\033[K\033[0;31mFailed creating library archive \033[1;31m$@\033[0;31m from\033[0m \033[1;31m$<\033[0;31m!\033[0m\n"; exit 1 +RECIPE_RESULT_ARCHIVE = ; then $(ARCHIVE_OK); else $(ARCHIVE_FAILED); fi +LINK_STATUS = printf "\033[K\033[0;0mLinking \033[1m$@\033[0;0m...\033[0m\r" +LINK_OK = printf "\033[K\033[0;32mLinked \033[1;32m$@\033[0;32m \033[0m\n" +LINK_FAILED = printf "\033[K\033[0;31mFailed linking \033[1;31m$@\033[0;31m!\nIf the build options, environment, or system packages have changed, run \'\033[1;31mmake clean\033[0;31m\' and try again.\033[0m\n"; exit 1 +RECIPE_RESULT_LINK = ; then $(LINK_OK); else $(LINK_FAILED); fi +else +RECIPE_IF = +BUILD_STARTED = +COMPILE_STATUS = +COMPILE_OK = true +COMPILE_FAILED = false; exit 1 +RECIPE_RESULT_COMPILE = +ARCHIVE_STATUS = +ARCHIVE_OK = true +ARCHIVE_FAILED = false; exit 1 +RECIPE_RESULT_ARCHIVE = +LINK_STATUS = +LINK_OK = true +LINK_FAILED = false; exit 1 +RECIPE_RESULT_LINK = +endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..a62e8bd --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# polecat + +*What is polecat?* +polecat is like lutris except it does none of the work for you and only downloads wine and dxvk + +*why?* +I got sick of Lutris breaking every 2 weeks and know how to manage my own wine prefixes + +### License +MIT
\ No newline at end of file diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..f3131e0 --- /dev/null +++ b/src/common.c @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <string.h> + +#include "common.h" + +void print_help(const struct Command* commands, const size_t size) +{ + int longestCommand = 0; + + for (size_t i = 0; i < size; ++i) + { + int commandLength = strlen(commands[i].name); + + if (commandLength > longestCommand) longestCommand = commandLength; + } + + for (size_t i = 0; i < size; ++i) + { + printf("\t%-*s\t%s\n", longestCommand, commands[i].name, commands[i].description); + } +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..8b9d95b --- /dev/null +++ b/src/common.h @@ -0,0 +1,28 @@ +#ifndef COMMON_H +#define COMMON_H + +#include <stddef.h> +#include <stdint.h> + +#define ARRAY_LEN(arr) sizeof(arr) / sizeof(arr[0]) + +#define WINE_API "https://lutris.net/api/runners/wine" +#define DXVK_API "https://api.github.com/repos/lutris/dxvk/releases" + +#define USER_AGENT NAME "/" VERSION + + +struct MemoryStruct { + uint8_t* memory; + size_t size; +}; + +struct Command { + char* name; + int (*func)(int, char**); + char* description; +}; + +void print_help(const struct Command*, size_t); + +#endif
\ No newline at end of file diff --git a/src/dxvk.c b/src/dxvk.c new file mode 100644 index 0000000..32d7d69 --- /dev/null +++ b/src/dxvk.c @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <string.h> +#include <json.h> +#include <libgen.h> + +#include "dxvk.h" +#include "net.h" +#include "common.h" + +const static struct Command dxvk_commands[] = { + { .name = "install", .func = dxvk_install, .description = "download and install a dxvk version" }, + { .name = "list", .func = dxvk_list, .description = "list available dxvk versions" }, + { .name = "help", .func = dxvk_help, .description = "shows this message" }, +}; + +int dxvk(int argc, char** argv) +{ + if (argc > 2) + { + for (int i = 0; i < ARRAY_LEN(dxvk_commands); ++i) + { + if (!strcmp(dxvk_commands[i].name, argv[2])) return dxvk_commands[i].func(argc, argv); + } + } + + return dxvk_help(argc, argv); +} + + +int dxvk_install(int argc, char** argv) +{ + if (argc == 4) + { + struct json_object* runner = fetchJSON(DXVK_API); + + if (runner) + { + + int choice = atoi(argv[3]); + + if (choice > json_object_array_length(runner) - 1 || choice < 0) + { + printf("`%i' is not a valid ID\n\nrun `polecat dxvk list' to get a valid ID", choice); + } + else + { + struct json_object* version = json_object_array_get_idx(runner, choice); + struct json_object* assets; + + json_object_object_get_ex(version, "assets", &assets); + version =json_object_array_get_idx(assets, 0); + + json_object_object_get_ex(version, "browser_download_url", &assets); + + char* name = basename((char*)json_object_get_string(assets)); + + printf("Downloading %s", json_object_get_string(assets)); + downloadFile(json_object_get_string(assets), name); + printf("\nDone\n"); + } + } + } + else + { + puts("Usage: polecat dxvk download <ID>\n\nIDs are obtained via `polecat dxvk list' "); + } + return 0; +} + +int dxvk_list(int argc, char** argv) +{ + struct json_object* runner = fetchJSON(DXVK_API); + + if (runner) + { + puts("Installable DXVK versions:"); + + for (size_t i = 0; i < json_object_array_length(runner); ++i) + { + struct json_object* version = json_object_array_get_idx(runner, i); + struct json_object* name; + + json_object_object_get_ex(version, "name", &name); + printf(" [%zu]\t%s\n", i, json_object_get_string(name)); + } + } + + return 0; +} + +int dxvk_help(int argc, char** argv) +{ + puts("usage: polecat dxvk <command>\n\nList of commands:"); + + print_help(dxvk_commands, ARRAY_LEN(dxvk_commands)); + + return 0; +}
\ No newline at end of file diff --git a/src/dxvk.h b/src/dxvk.h new file mode 100644 index 0000000..ef423e2 --- /dev/null +++ b/src/dxvk.h @@ -0,0 +1,9 @@ +#ifndef DXVK_H +#define DXVK_H + +int dxvk(int, char**); +int dxvk_install(int, char**); +int dxvk_list(int, char**); +int dxvk_help(int, char**); + +#endif
\ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2e7655d --- /dev/null +++ b/src/main.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <string.h> + +#include "main.h" +#include "wine.h" +#include "dxvk.h" +#include "common.h" + +const static struct Command main_commands[] = { + { .name = "wine", .func = wine, .description = "manage wine versions" }, + { .name = "dxvk", .func = dxvk, .description = "manage dxvk versions" }, + { .name = "info", .func = main_info, .description = "show some information about polecat" }, + { .name = "help", .func = main_help, .description = "displays this message" }, +}; + + +int main(int argc, char** argv) +{ + if (argc > 1) + { + for (int i = 0; i < ARRAY_LEN(main_commands); ++i) + { + if (!strcmp(main_commands[i].name, argv[1])) return main_commands[i].func(argc, argv); + } + } + + return main_help(argc, argv); +} + +int main_info(int argc, char** argv) +{ + printf("Version:\t\t%s\n" + "User-Agent:\t\t%s/%s\n", + VERSION, + NAME, VERSION); + + return 0; +} + +int main_help(int argc, char** argv) +{ + puts("usage: polecat <command>\n\nList of commands:"); + + print_help(main_commands, ARRAY_LEN(main_commands)); + + return 0; +}
\ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..594b29e --- /dev/null +++ b/src/main.h @@ -0,0 +1,7 @@ +#ifndef MAIN_H +#define MAIN_H + +extern int main_help(int, char**); +extern int main_info(int, char**); + +#endif
\ No newline at end of file diff --git a/src/net.c b/src/net.c new file mode 100644 index 0000000..f7dc481 --- /dev/null +++ b/src/net.c @@ -0,0 +1,100 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <curl/curl.h> +#include <json.h> + +#include "net.h" +#include "common.h" + +size_t memoryCallback(void* contents, size_t size, size_t nmemb, void* userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct* mem = (struct MemoryStruct*)userp; + + uint8_t* ptr = realloc(mem->memory, mem->size + realsize + 1); + if(ptr == NULL) { + /* out of memory! */ + puts("out of memory"); + return 0; + } + + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +struct MemoryStruct* downloadToRam(const char* URL) +{ + CURL* curl_handle; + CURLcode res; + + struct MemoryStruct* chunk = malloc(sizeof(struct MemoryStruct)); + + chunk->memory = malloc(1); + chunk->size = 0; + + curl_global_init(CURL_GLOBAL_ALL); + + curl_handle = curl_easy_init(); + + curl_easy_setopt(curl_handle, CURLOPT_URL, URL); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memoryCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)chunk); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, USER_AGENT); + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); + + res = curl_easy_perform(curl_handle); + + long http_code = 0; + curl_easy_getinfo (curl_handle, CURLINFO_RESPONSE_CODE, &http_code); + + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + return NULL; + } + else if (http_code != 200) + { + fprintf(stderr, "Server didn't respond as expected [HTTP Error %li]\n", http_code); + return NULL; + } + + curl_easy_cleanup(curl_handle); + curl_global_cleanup(); + + return chunk; +} + +int downloadFile(const char* URL, const char* path) +{ + struct MemoryStruct* chunk = downloadToRam(URL); + + if (chunk) + { + FILE* file = fopen(path, "wb"); + fwrite(chunk->memory, chunk->size, 1, file); + fclose(file); + + free(chunk->memory); + free(chunk); + } + + return 0; +} + +struct json_object* fetchJSON(const char* URL) +{ + struct MemoryStruct* chunk = downloadToRam(URL); + + struct json_object* json = json_tokener_parse((char*)chunk->memory); + + free(chunk->memory); + free(chunk); + + return json; +}
\ No newline at end of file diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..2b203fa --- /dev/null +++ b/src/net.h @@ -0,0 +1,10 @@ +#ifndef NET_H +#define NET_H + +#include <json.h> + +size_t WriteMemoryCallback(void*, size_t, size_t, void*); +int downloadFile(const char*, const char*); +struct json_object* fetchJSON(const char*); + +#endif
\ No newline at end of file diff --git a/src/wine.c b/src/wine.c new file mode 100644 index 0000000..2c304ed --- /dev/null +++ b/src/wine.c @@ -0,0 +1,97 @@ +#include <stdio.h> +#include <string.h> +#include <json.h> +#include <libgen.h> + +#include "wine.h" +#include "net.h" +#include "common.h" + +const static struct Command wine_commands[] = { + { .name = "install", .func = wine_install, .description = "download and install a wine version from lutris" }, + { .name = "list", .func = wine_list, .description = "list available wine versions" }, + { .name = "help", .func = wine_help, .description = "shows this message" }, +}; + +int wine(int argc, char** argv) +{ + if (argc > 2) + { + for (int i = 0; i < ARRAY_LEN(wine_commands); ++i) + { + if (!strcmp(wine_commands[i].name, argv[2])) return wine_commands[i].func(argc, argv); + } + } + + return wine_help(argc, argv); +} + + +int wine_install(int argc, char** argv) +{ + if (argc == 4) + { + struct json_object* runner = fetchJSON(WINE_API); + + if (runner) + { + struct json_object* versions; + json_object_object_get_ex(runner, "versions", &versions); + + int choice = atoi(argv[3]); + + if (choice > json_object_array_length(versions) - 1 || choice < 0) + { + printf("`%i' is not a valid ID\n\nrun `polecat wine list' to get a valid ID", choice); + } + else + { + struct json_object* url; + struct json_object* value = json_object_array_get_idx(versions, choice); + + json_object_object_get_ex(value, "url", &url); + char* name = basename((char*)json_object_get_string(url)); + + printf("Downloading %s", name); + downloadFile(json_object_get_string(url), name); + printf("\nDone\n"); + } + } + } + else + { + puts("Usage: polecat wine download <ID>\n\nIDs are obtained via `polecat wine list' "); + } + return 0; +} + +int wine_list(int argc, char** argv) +{ + struct json_object* runner = fetchJSON(WINE_API); + + if (runner) + { + struct json_object* versions, *value, *val; + json_object_object_get_ex(runner, "versions", &versions); + + puts("Installable wine versions:"); + + for (size_t i = 0; i < json_object_array_length(versions); ++i) + { + value = json_object_array_get_idx(versions, i); + json_object_object_get_ex(value, "version", &val); + printf(" [%zu]\t%s\n", i, json_object_get_string(val)); + } + } + + return 0; +} + +int wine_help(int argc, char** argv) +{ + puts("usage: polecat wine <command>\n\nList of commands:"); + + print_help(wine_commands, ARRAY_LEN(wine_commands)); + + return 0; +}
\ No newline at end of file diff --git a/src/wine.h b/src/wine.h new file mode 100644 index 0000000..872a17e --- /dev/null +++ b/src/wine.h @@ -0,0 +1,9 @@ +#ifndef WINE_H +#define WINE_H + +int wine(int, char**); +int wine_install(int, char**); +int wine_list(int, char**); +int wine_help(int, char**); + +#endif
\ No newline at end of file |