aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.c21
-rw-r--r--src/common.h28
-rw-r--r--src/dxvk.c98
-rw-r--r--src/dxvk.h9
-rw-r--r--src/main.c47
-rw-r--r--src/main.h7
-rw-r--r--src/net.c100
-rw-r--r--src/net.h10
-rw-r--r--src/wine.c97
-rw-r--r--src/wine.h9
10 files changed, 426 insertions, 0 deletions
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