Downloading works. Still to come: cancelling downloads and extraction.
This commit is contained in:
committed by
Chris Forbes
parent
5991ecdcf5
commit
2e309b46e8
@@ -14,6 +14,7 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define JS_STR(str) JSStringCreateWithUTF8CString(str)
|
||||
#define JS_FUNC(ctx, callback) JSObjectMakeFunctionWithCallback(ctx, NULL, \
|
||||
@@ -101,6 +102,8 @@ JSValueRef js_exists_in_mod(JSContextRef ctx, JSObjectRef func,
|
||||
return_value = JSValueMakeNumber(ctx, 1);
|
||||
}
|
||||
|
||||
g_message("JS ExistsInMod: Not found");
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
@@ -175,6 +178,287 @@ JSValueRef js_launch_mod(JSContextRef ctx, JSObjectRef func,
|
||||
return return_value;
|
||||
}
|
||||
|
||||
typedef struct download_t
|
||||
{
|
||||
char key[32];
|
||||
char url[128];
|
||||
char dest[128];
|
||||
int current_bytes;
|
||||
int total_bytes;
|
||||
JSContextGroupRef ctx_group;
|
||||
JSObjectRef download_progressed_func;
|
||||
JSValueRef status;
|
||||
JSValueRef error;
|
||||
GIOChannel * output_channel;
|
||||
GPid pid;
|
||||
} download_t;
|
||||
|
||||
#define MAX_DOWNLOADS 16
|
||||
|
||||
static download_t downloads[MAX_DOWNLOADS];
|
||||
static int num_downloads = 0;
|
||||
|
||||
download_t * find_download(char const * key)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_downloads; i++)
|
||||
{
|
||||
if (0 == strcmp(downloads[i].key, key))
|
||||
return downloads + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
char * key, * url, * filename;
|
||||
size_t key_size, url_size, filename_size;
|
||||
download_t * download;
|
||||
JSValueRef o;
|
||||
if (!js_check_num_args(ctx, "registerDownload", argc, 3, exception))
|
||||
return JSValueMakeNull(ctx);
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
|
||||
|
||||
g_message("JS RegisterDownload: Registering %s", key);
|
||||
|
||||
if (NULL == (download = find_download(key)))
|
||||
{
|
||||
download = downloads + num_downloads++;
|
||||
if (num_downloads >= MAX_DOWNLOADS)
|
||||
{
|
||||
num_downloads = MAX_DOWNLOADS - 1;
|
||||
return JSValueMakeNull(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
memset(download, 0, sizeof(download_t));
|
||||
|
||||
download->ctx_group = JSContextGetGroup(ctx);
|
||||
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("downloadProgressed"), NULL);
|
||||
download->download_progressed_func = JSValueToObject(ctx, o, NULL);
|
||||
|
||||
strncpy(download->key, key, 31);
|
||||
download->key[31] = '\0';
|
||||
|
||||
free(key);
|
||||
|
||||
url = js_get_cstr_from_val(ctx, argv[1], &url_size);
|
||||
strncpy(download->url, url, 127);
|
||||
download->url[127] = '\0';
|
||||
free(url);
|
||||
|
||||
filename = js_get_cstr_from_val(ctx, argv[2], &filename_size);
|
||||
strncpy(download->dest, filename, 127);
|
||||
download->dest[127] = '\0';
|
||||
free(filename);
|
||||
|
||||
download->status = JSValueMakeString(ctx, JS_STR("AVAILABLE"));
|
||||
|
||||
return JSValueMakeNull(ctx);
|
||||
}
|
||||
|
||||
gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpointer data)
|
||||
{
|
||||
download_t * download = (download_t *)data;
|
||||
gchar * line;
|
||||
gsize line_length;
|
||||
GIOStatus io_status;
|
||||
JSValueRef args[1];
|
||||
JSContextRef ctx;
|
||||
|
||||
ctx = JSGlobalContextCreateInGroup(download->ctx_group, NULL);
|
||||
|
||||
switch(condition)
|
||||
{
|
||||
case G_IO_IN:
|
||||
io_status = g_io_channel_read_line(source, &line, &line_length, NULL, NULL);
|
||||
if (G_IO_STATUS_NORMAL == io_status)
|
||||
{
|
||||
if (0 == memcmp(line, "Error:", 6))
|
||||
{
|
||||
download->status = JSValueMakeString(ctx, JS_STR("ERROR"));
|
||||
download->error = JSValueMakeString(ctx, JS_STR(line + 7));
|
||||
}
|
||||
else
|
||||
{
|
||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADING"));
|
||||
GRegex * pattern = g_regex_new("(\\d{1,3})% (\\d+)/(\\d+) bytes", 0, 0, NULL);
|
||||
GMatchInfo * match;
|
||||
if (g_regex_match(pattern, line, 0, &match))
|
||||
{
|
||||
gchar * current = g_match_info_fetch(match, 2), * total = g_match_info_fetch(match, 3);
|
||||
download->current_bytes = atoi(current);
|
||||
download->total_bytes = atoi(total);
|
||||
g_free(current);
|
||||
g_free(total);
|
||||
}
|
||||
g_free(match);
|
||||
}
|
||||
}
|
||||
g_free(line);
|
||||
break;
|
||||
case G_IO_HUP:
|
||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADED"));
|
||||
g_io_channel_shutdown(source, FALSE, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
|
||||
JSObjectCallAsFunction(ctx, download->download_progressed_func, NULL, 1, args, NULL);
|
||||
|
||||
JSGlobalContextRelease((JSGlobalContextRef)ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
JSValueRef js_start_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
char * key;
|
||||
size_t key_size;
|
||||
download_t * download;
|
||||
int fd;
|
||||
GPid pid;
|
||||
|
||||
if (!js_check_num_args(ctx, "startDownload", argc, 1, exception))
|
||||
return JSValueMakeNull(ctx);
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
|
||||
|
||||
if (NULL == (download = find_download(key)))
|
||||
{
|
||||
free(key);
|
||||
return JSValueMakeBoolean(ctx, 0);
|
||||
}
|
||||
|
||||
free(key);
|
||||
|
||||
g_message("Starting download %s", download->key);
|
||||
|
||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADING"));
|
||||
|
||||
fd = util_do_download(download->url, download->dest, &pid);
|
||||
|
||||
if (!fd)
|
||||
return JSValueMakeBoolean(ctx, 0);
|
||||
|
||||
download->pid = pid;
|
||||
download->output_channel = g_io_channel_unix_new(fd);
|
||||
|
||||
g_io_add_watch(download->output_channel, G_IO_IN | G_IO_HUP, update_download_stats, download);
|
||||
|
||||
return JSValueMakeBoolean(ctx, 1);
|
||||
}
|
||||
|
||||
JSValueRef js_cancel_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
return JSValueMakeNull(ctx);
|
||||
}
|
||||
|
||||
JSValueRef js_download_status(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
char * key;
|
||||
size_t key_size;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "downloadStatus", argc, 1, exception))
|
||||
return JSValueMakeNull(ctx);
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
|
||||
|
||||
if (NULL == (download = find_download(key)))
|
||||
{
|
||||
free(key);
|
||||
return JSValueMakeString(ctx, JS_STR("NOT_REGISTERED"));
|
||||
}
|
||||
|
||||
free(key);
|
||||
|
||||
return download->status;
|
||||
}
|
||||
|
||||
JSValueRef js_download_error(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
char * key;
|
||||
size_t key_size;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "downloadError", argc, 1, exception))
|
||||
return JSValueMakeNull(ctx);
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
|
||||
|
||||
g_message("JS DownloadError: Retrieving error message for %s", key);
|
||||
|
||||
if (NULL == (download = find_download(key)))
|
||||
{
|
||||
free(key);
|
||||
return JSValueMakeString(ctx, JS_STR(""));
|
||||
}
|
||||
|
||||
free(key);
|
||||
|
||||
return download->error;
|
||||
}
|
||||
|
||||
JSValueRef js_bytes_completed(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
char * key;
|
||||
size_t key_size;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "bytesCompleted", argc, 1, exception))
|
||||
return JSValueMakeNull(ctx);
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
|
||||
|
||||
if (NULL == (download = find_download(key)))
|
||||
{
|
||||
free(key);
|
||||
return JSValueMakeNumber(ctx, 0);
|
||||
}
|
||||
|
||||
free(key);
|
||||
|
||||
return JSValueMakeNumber(ctx, download->current_bytes);
|
||||
}
|
||||
|
||||
JSValueRef js_bytes_total(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
char * key;
|
||||
size_t key_size;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "bytesTotal", argc, 1, exception))
|
||||
return JSValueMakeNull(ctx);
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
|
||||
|
||||
if (NULL == (download = find_download(key)))
|
||||
{
|
||||
free(key);
|
||||
return JSValueMakeNumber(ctx, 0);
|
||||
}
|
||||
|
||||
free(key);
|
||||
|
||||
return JSValueMakeNumber(ctx, download->total_bytes);
|
||||
}
|
||||
|
||||
JSValueRef js_extract_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
return JSValueMakeNull(ctx);
|
||||
}
|
||||
|
||||
void js_add_functions(JSGlobalContextRef ctx, JSObjectRef target, char ** names,
|
||||
JSObjectCallAsFunctionCallback * callbacks, size_t count)
|
||||
{
|
||||
@@ -193,9 +477,16 @@ void bind_js_bridge(WebKitWebView * view, WebKitWebFrame * frame,
|
||||
JSGlobalContextRef js_ctx;
|
||||
JSObjectRef window_obj, external_obj;
|
||||
|
||||
int func_count = 3;
|
||||
char * names[] = { "log", "existsInMod", "launchMod" };
|
||||
JSObjectCallAsFunctionCallback callbacks[] = { js_log, js_exists_in_mod, js_launch_mod };
|
||||
int func_count = 11;
|
||||
char * names[] = { "log", "existsInMod", "launchMod", "registerDownload",
|
||||
"startDownload", "cancelDownload", "downloadStatus",
|
||||
"downloadError", "bytesCompleted", "bytesTotal",
|
||||
"extractDownload"};
|
||||
JSObjectCallAsFunctionCallback callbacks[] = { js_log, js_exists_in_mod, js_launch_mod,
|
||||
js_register_download, js_start_download,
|
||||
js_cancel_download, js_download_status,
|
||||
js_download_error, js_bytes_completed,
|
||||
js_bytes_total, js_extract_download };
|
||||
|
||||
js_ctx = (JSGlobalContextRef)context;
|
||||
|
||||
|
||||
@@ -12,14 +12,16 @@
|
||||
#include <glib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
int util_get_mod_list (GChildWatchFunc callback)
|
||||
int util_do_command_async(char * command, GChildWatchFunc callback)
|
||||
{
|
||||
GPid child_pid;
|
||||
gint * out_fd = (gint *)malloc(sizeof(gint));
|
||||
char * spawn_args[] = { "mono", "OpenRA.Utility.exe", "-l", NULL };
|
||||
gboolean result = g_spawn_async_with_pipes(NULL, spawn_args, NULL,
|
||||
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL, NULL, &child_pid, NULL, out_fd, NULL, NULL);
|
||||
char * spawn_args[] = { "mono", "OpenRA.Utility.exe", command, NULL };
|
||||
gboolean result;
|
||||
|
||||
result = g_spawn_async_with_pipes(NULL, spawn_args, NULL,
|
||||
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL, NULL, &child_pid, NULL, out_fd, NULL, NULL);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -31,6 +33,11 @@ int util_get_mod_list (GChildWatchFunc callback)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int util_get_mod_list (GChildWatchFunc callback)
|
||||
{
|
||||
return util_do_command_async("-l", callback);
|
||||
}
|
||||
|
||||
int util_do_command_blocking(char * command, GChildWatchFunc callback)
|
||||
{
|
||||
GPid child_pid;
|
||||
@@ -58,16 +65,50 @@ int util_do_command_blocking(char * command, GChildWatchFunc callback)
|
||||
|
||||
int util_get_mod_metadata(char const * mod, GChildWatchFunc callback)
|
||||
{
|
||||
char util_args[32];
|
||||
char * util_args;
|
||||
int return_val;
|
||||
|
||||
util_args = (char *)malloc(strlen(mod) + strlen("-i=") + 1);
|
||||
sprintf(util_args, "-i=%s", mod);
|
||||
return util_do_command_blocking(util_args, callback);
|
||||
return_val = util_do_command_blocking(util_args, callback);
|
||||
free(util_args);
|
||||
return return_val;
|
||||
}
|
||||
|
||||
int util_get_setting(const char * setting, GChildWatchFunc callback)
|
||||
{
|
||||
char command[64];
|
||||
char * command;
|
||||
int return_val;
|
||||
|
||||
command = (char *)malloc(strlen(setting) + strlen("--settings-value=~/.openra,") + 1);
|
||||
sprintf(command, "--settings-value=~/.openra,%s", setting);
|
||||
return util_do_command_blocking(command, callback);
|
||||
return_val = util_do_command_blocking(command, callback);
|
||||
free(command);
|
||||
return return_val;
|
||||
}
|
||||
|
||||
int util_do_download(const char * url, const char * dest, GPid * pid)
|
||||
{
|
||||
char * command;
|
||||
int out_fd;
|
||||
gboolean result;
|
||||
|
||||
char * launch_args[] = { "mono", "OpenRA.Utility.exe", NULL, NULL };
|
||||
|
||||
command = (char *)malloc(strlen(url) + strlen(dest) + strlen("--download-url=") + 2);
|
||||
sprintf(command, "--download-url=%s,%s", url, dest);
|
||||
|
||||
launch_args[2] = command;
|
||||
|
||||
result = g_spawn_async_with_pipes(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, pid, NULL, &out_fd, NULL, NULL);
|
||||
free(command);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return out_fd;
|
||||
}
|
||||
|
||||
char * util_get_output(int fd, int * output_len)
|
||||
|
||||
@@ -9,4 +9,5 @@
|
||||
int util_get_mod_list (GChildWatchFunc);
|
||||
int util_get_mod_metadata(char const *, GChildWatchFunc);
|
||||
int util_get_setting(const char *, GChildWatchFunc);
|
||||
int util_do_download(const char *, const char *, GPid *);
|
||||
char * util_get_output(int, int *);
|
||||
|
||||
Reference in New Issue
Block a user