Reverted packaging changes from launchers.
This commit is contained in:
committed by
Paul Chote
parent
a81c865620
commit
4eaf97f90e
40
Makefile
40
Makefile
@@ -4,16 +4,12 @@ DEFINE = DEBUG;TRACE
|
||||
COMMON_LIBS = System.dll System.Core.dll System.Drawing.dll System.Xml.dll thirdparty/ICSharpCode.SharpZipLib.dll
|
||||
PHONY = core tools package all mods clean distclean
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall
|
||||
CFLAGS32 = $(CFLAGS) -m32
|
||||
|
||||
.SUFFIXES:
|
||||
core: game renderers mod_ra mod_cnc
|
||||
tools: editor ralint seqed filex tsbuild utility
|
||||
package: fixheader core editor utility winlaunch gtklaunch gtklaunch32
|
||||
package: fixheader core editor utility
|
||||
mods: mod_ra mod_cnc
|
||||
all: core tools winlaunch
|
||||
all: core tools
|
||||
clean:
|
||||
@-rm *.exe *.dll *.mdb mods/**/*.dll mods/**/*.mdb *.resources
|
||||
distclean: clean
|
||||
@@ -166,28 +162,6 @@ utility_LIBS = $(COMMON_LIBS) $(utility_DEPS) thirdparty/ICSharpCode.Shar
|
||||
PROGRAMS += utility
|
||||
utility: $(utility_TARGET)
|
||||
|
||||
# Windows launcher
|
||||
winlaunch_SRCS = $(shell find OpenRA.Launcher/ -iname '*.cs')
|
||||
winlaunch_TARGET = OpenRA.Launcher.exe
|
||||
winlaunch_KIND = winexe
|
||||
winlaunch_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll
|
||||
winlaunch_EXTRA = -resource:OpenRA.Launcher.Launcher.resources
|
||||
PROGRAMS += winlaunch
|
||||
OpenRA.Launcher.Launcher.resources:
|
||||
resgen2 OpenRA.Launcher/Launcher.resx OpenRA.Launcher.Launcher.resources 1> /dev/null
|
||||
winlaunch: OpenRA.Launcher.Launcher.resources $(winlaunch_TARGET)
|
||||
|
||||
gtklaunch_HEADERS = $(shell find OpenRA.Launcher.Gtk/ -iname '*.h')
|
||||
gtklaunch_SRCS = $(shell find OpenRA.Launcher.Gtk/ -iname '*.c')
|
||||
|
||||
gtklaunch: $(gtklaunch_HEADERS) $(gtklaunch_SRCS)
|
||||
@echo CC launcher
|
||||
@$(CC) $(CFLAGS) $(shell pkg-config --cflags --libs gtk+-2.0 webkit-1.0) -I/usr/include/ -lgcrypt -o gtklaunch $(gtklaunch_SRCS) /usr/lib/libmicrohttpd.a
|
||||
|
||||
gtklaunch32: $(gtklaunch_HEADERS) $(gtklaunch_SRCS)
|
||||
@echo CC launcher32
|
||||
@$(CC) $(CFLAGS32) $(shell pkg-config --cflags --libs gtk+-2.0 webkit-1.0) -I/usr/include/ -lgcrypt -o gtklaunch32 $(gtklaunch_SRCS) /usr/lib/libmicrohttpd.a
|
||||
|
||||
.PHONY: $(PHONY) $(PROGRAMS)
|
||||
|
||||
#
|
||||
@@ -220,9 +194,9 @@ BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
|
||||
INSTALL_DIR = $(DESTDIR)$(datadir)/openra
|
||||
INSTALL = install
|
||||
INSTALL_PROGRAM = $(INSTALL)
|
||||
CORE = fileformats rcg rgl rnull game editor utility winlaunch
|
||||
CORE = fileformats rcg rgl rnull game editor utility
|
||||
|
||||
install: all gtklaunch
|
||||
install: all
|
||||
@-echo "Installing OpenRA to $(INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -d $(INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) $(foreach prog,$(CORE),$($(prog)_TARGET)) $(INSTALL_DIR)
|
||||
@@ -254,8 +228,12 @@ install: all gtklaunch
|
||||
@cp --parents -r thirdparty/Tao $(INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) thirdparty/ICSharpCode.SharpZipLib.dll $(INSTALL_DIR)
|
||||
|
||||
@echo "#!/bin/sh" > openra
|
||||
@echo "cd "$(datadir)"/openra" >> openra
|
||||
@echo "mono "$(datadir)"/openra/OpenRA.Game.exe SupportDir=~/.openra \"$$""@\"" >> openra
|
||||
|
||||
@$(INSTALL_PROGRAM) -d $(BIN_INSTALL_DIR)
|
||||
@$(INSTALL_PROGRAM) -T gtklaunch $(BIN_INSTALL_DIR)/openra
|
||||
@$(INSTALL_PROGRAM) -m +rx openra $(BIN_INSTALL_DIR)
|
||||
|
||||
uninstall:
|
||||
@-rm -r $(INSTALL_DIR)
|
||||
|
||||
@@ -1,794 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <webkit/webkit.h>
|
||||
#include <JavaScriptCore/JavaScript.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define JS_STR(str) JSStringCreateWithUTF8CString(str)
|
||||
#define JS_FUNC(ctx, callback) JSObjectMakeFunctionWithCallback(ctx, NULL, \
|
||||
callback)
|
||||
|
||||
#define JS_TRUE JSValueMakeBoolean(ctx, TRUE)
|
||||
#define JS_FALSE JSValueMakeBoolean(ctx, FALSE)
|
||||
#define JS_NULL JSValueMakeNull(ctx)
|
||||
|
||||
GString * sanitize_path(gchar const * path)
|
||||
{
|
||||
gchar * basename = g_path_get_basename(path);
|
||||
gchar * dirname = g_path_get_dirname(path);
|
||||
gchar ** frags = g_strsplit(dirname, "/", -1);
|
||||
gint offset = 0;
|
||||
GString * new_path = g_string_new(NULL);
|
||||
while (*(frags + offset))
|
||||
{
|
||||
if ((strcmp(*(frags + offset), "..") == 0) || (strcmp(*(frags + offset), ".") == 0))
|
||||
{
|
||||
offset++;
|
||||
continue;
|
||||
}
|
||||
g_string_append(new_path, *(frags + offset));
|
||||
g_string_append_c(new_path, G_DIR_SEPARATOR);
|
||||
offset++;
|
||||
}
|
||||
g_string_append(new_path, basename);
|
||||
g_free(basename);
|
||||
g_free(dirname);
|
||||
g_strfreev(frags);
|
||||
return new_path;
|
||||
}
|
||||
|
||||
int js_check_num_args(JSContextRef ctx, gchar const * func_name, int argc, int num_expected, JSValueRef * exception)
|
||||
{
|
||||
GString * buf;
|
||||
if (argc < num_expected)
|
||||
{
|
||||
buf = g_string_new(NULL);
|
||||
g_string_printf(buf, "%s: Not enough args, expected %d got %d", func_name, num_expected, argc);
|
||||
*exception = JSValueMakeString(ctx, JS_STR(buf->str));
|
||||
g_string_free(buf, TRUE);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
GString * js_get_cstr_from_val(JSContextRef ctx, JSValueRef val)
|
||||
{
|
||||
gchar * buf;
|
||||
GString * ret;
|
||||
size_t len;
|
||||
JSStringRef str;
|
||||
if (!JSValueIsString(ctx, val))
|
||||
return NULL;
|
||||
str = JSValueToStringCopy(ctx, val, NULL);
|
||||
len = JSStringGetMaximumUTF8CStringSize(str);
|
||||
buf = (gchar *)g_malloc(len);
|
||||
ret = g_string_sized_new(JSStringGetUTF8CString(str, buf, len));
|
||||
g_string_assign(ret, buf);
|
||||
g_free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValueRef js_log(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
if (!js_check_num_args(ctx, "log", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
GString * buffer;
|
||||
buffer = js_get_cstr_from_val(ctx, argv[0]);
|
||||
if (!buffer)
|
||||
return JS_NULL;
|
||||
g_message("JS Log: %s", buffer->str);
|
||||
g_string_free(buffer, TRUE);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValueRef js_exists_in_mod(JSContextRef ctx, JSObjectRef func,
|
||||
JSObjectRef this, size_t argc,
|
||||
const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * mod_buf, * file_buf, * search_path, * search_path_sanitized;
|
||||
JSValueRef return_value = JS_FALSE;
|
||||
FILE * f;
|
||||
if (!js_check_num_args(ctx, "existsInMod", argc, 2, exception))
|
||||
return JS_NULL;
|
||||
|
||||
file_buf = js_get_cstr_from_val(ctx, argv[0]);
|
||||
if (!file_buf)
|
||||
return JS_NULL;
|
||||
mod_buf = js_get_cstr_from_val(ctx, argv[1]);
|
||||
if (!mod_buf)
|
||||
return JS_NULL;
|
||||
|
||||
search_path = g_string_new(NULL);
|
||||
g_string_printf(search_path, "mods/%s/%s", mod_buf->str, file_buf->str);
|
||||
search_path_sanitized = sanitize_path(search_path->str);
|
||||
|
||||
g_string_free(search_path, TRUE);
|
||||
g_string_free(file_buf, TRUE);
|
||||
g_string_free(mod_buf, TRUE);
|
||||
|
||||
g_message("JS ExistsInMod: Looking for %s", search_path_sanitized->str);
|
||||
|
||||
f = fopen(search_path_sanitized->str, "r");
|
||||
|
||||
g_string_free(search_path_sanitized, TRUE);
|
||||
|
||||
if (f != NULL)
|
||||
{
|
||||
g_message("JS ExistsInMod: Found");
|
||||
fclose(f);
|
||||
return_value = JS_TRUE;
|
||||
}
|
||||
else
|
||||
g_message("JS ExistsInMod: Not found");
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
JSValueRef js_launch_mod(JSContextRef ctx, JSObjectRef func,
|
||||
JSObjectRef this, size_t argc,
|
||||
const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * mod_key, * mod_list;
|
||||
mod_t * mod;
|
||||
|
||||
if (!js_check_num_args(ctx, "launchMod", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
if (!JSValueIsString(ctx, argv[0]))
|
||||
{
|
||||
*exception = JSValueMakeString(ctx, JS_STR("One or more args are incorrect types."));
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
mod_key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
g_message("JS LaunchMod: %s", mod_key->str);
|
||||
|
||||
mod = get_mod(mod_key->str);
|
||||
|
||||
mod_list = g_string_new(mod_key->str);
|
||||
|
||||
g_string_free(mod_key, TRUE);
|
||||
|
||||
while (strlen(mod->requires) > 0)
|
||||
{
|
||||
gchar * r = g_strdup(mod->requires), * comma;
|
||||
if (NULL != (comma = g_strstr_len(r, -1, ",")))
|
||||
{
|
||||
*comma = '\0';
|
||||
}
|
||||
|
||||
mod = get_mod(r);
|
||||
if (mod == NULL)
|
||||
{
|
||||
GString * exception_msg = g_string_new(NULL);
|
||||
g_string_printf(exception_msg, "The mod %s is missing, cannot launch.", r);
|
||||
*exception = JSValueMakeString(ctx, JS_STR(exception_msg->str));
|
||||
g_string_free(exception_msg, TRUE);
|
||||
g_string_free(mod_list, TRUE);
|
||||
return JS_NULL;
|
||||
}
|
||||
g_string_append_printf(mod_list, ",%s", r);
|
||||
g_free(r);
|
||||
}
|
||||
|
||||
{
|
||||
gchar * launch_args[] = { "mono", "OpenRA.Game.exe", NULL, NULL, "SupportDir=~/.openra", NULL };
|
||||
GString * game_mods_arg = g_string_new(NULL);
|
||||
GString * renderer_arg = g_string_new(NULL);
|
||||
|
||||
g_string_printf(game_mods_arg, "Game.Mods=%s", mod_list->str);
|
||||
|
||||
g_string_printf(renderer_arg, "Graphics.Renderer=%s", get_renderer() == RENDERER_GL ? "Gl" : "Cg");
|
||||
|
||||
launch_args[2] = game_mods_arg->str;
|
||||
launch_args[3] = renderer_arg->str;
|
||||
|
||||
g_spawn_async(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
|
||||
g_string_free(game_mods_arg, TRUE);
|
||||
g_string_free(renderer_arg, TRUE);
|
||||
}
|
||||
g_string_free(mod_list, TRUE);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
typedef struct js_callback
|
||||
{
|
||||
JSObjectRef func;
|
||||
JSContextGroupRef ctx_group;
|
||||
} js_callback;
|
||||
|
||||
typedef struct download_t
|
||||
{
|
||||
gchar * key;
|
||||
gchar * url;
|
||||
gchar * dest;
|
||||
int current_bytes;
|
||||
int total_bytes;
|
||||
js_callback * download_progressed_cb;
|
||||
js_callback * extraction_progressed_cb;
|
||||
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(gchar const * key)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_downloads; i++)
|
||||
{
|
||||
if (0 == strcmp(downloads[i].key, key))
|
||||
return downloads + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_download_status(JSContextRef ctx, download_t * download, const char * status)
|
||||
{
|
||||
JSValueUnprotect(ctx, download->status);
|
||||
|
||||
download->status = JSValueMakeString(ctx, JS_STR(status));
|
||||
|
||||
JSValueProtect(ctx, download->status);
|
||||
}
|
||||
|
||||
void set_download_error(JSContextRef ctx, download_t * download, const char * error)
|
||||
{
|
||||
JSValueUnprotect(ctx, download->error);
|
||||
|
||||
download->error = JSValueMakeString(ctx, JS_STR(error));
|
||||
|
||||
JSValueProtect(ctx, download->error);
|
||||
}
|
||||
|
||||
void free_download(download_t * download)
|
||||
{
|
||||
g_free(download->key);
|
||||
g_free(download->url);
|
||||
g_free(download->dest);
|
||||
g_free(download->download_progressed_cb);
|
||||
g_free(download->extraction_progressed_cb);
|
||||
}
|
||||
|
||||
JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key, * url, * filename;
|
||||
download_t * download;
|
||||
JSValueRef o;
|
||||
FILE * f;
|
||||
if (!js_check_num_args(ctx, "registerDownload", argc, 3, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
g_message("JS RegisterDownload: Registering %s", key->str);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
download = downloads + num_downloads++;
|
||||
if (num_downloads >= MAX_DOWNLOADS)
|
||||
{
|
||||
num_downloads = MAX_DOWNLOADS - 1;
|
||||
return JS_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free_download(download);
|
||||
memset(download, 0, sizeof(download_t));
|
||||
|
||||
download->download_progressed_cb = (js_callback *)g_malloc(sizeof(js_callback));
|
||||
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("downloadProgressed"), NULL);
|
||||
download->download_progressed_cb->ctx_group = JSContextGetGroup(ctx);
|
||||
download->download_progressed_cb->func = JSValueToObject(ctx, o, NULL);
|
||||
|
||||
download->extraction_progressed_cb = (js_callback *)g_malloc(sizeof(js_callback));
|
||||
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("extractProgressed"), NULL);
|
||||
download->extraction_progressed_cb->ctx_group = JSContextGetGroup(ctx);
|
||||
download->extraction_progressed_cb->func = JSValueToObject(ctx, o, NULL);
|
||||
|
||||
download->key = g_strdup(key->str);
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
url = js_get_cstr_from_val(ctx, argv[1]);
|
||||
download->url = g_strdup(url->str);
|
||||
g_string_free(url, TRUE);
|
||||
|
||||
filename = js_get_cstr_from_val(ctx, argv[2]);
|
||||
{
|
||||
GString * path = g_string_new(NULL), * sanitized_path;
|
||||
g_string_printf(path, "/tmp/%s", filename->str);
|
||||
g_string_free(filename, TRUE);
|
||||
sanitized_path = sanitize_path(path->str);
|
||||
g_string_free(path, TRUE);
|
||||
download->dest = g_strdup(sanitized_path->str);
|
||||
g_string_free(sanitized_path, TRUE);
|
||||
}
|
||||
|
||||
f = fopen(download->dest, "r");
|
||||
|
||||
if (NULL != f)
|
||||
{
|
||||
fclose(f);
|
||||
set_download_status(ctx, download, "DOWNLOADED");
|
||||
}
|
||||
else
|
||||
set_download_status(ctx, download, "AVAILABLE");
|
||||
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpointer data)
|
||||
{
|
||||
int ret = TRUE;
|
||||
download_t * download = (download_t *)data;
|
||||
gchar * line;
|
||||
gsize line_length;
|
||||
GIOStatus io_status;
|
||||
JSValueRef args[1];
|
||||
JSContextRef ctx;
|
||||
|
||||
ctx = JSGlobalContextCreateInGroup(download->download_progressed_cb->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 (g_str_has_prefix(line, "Error:"))
|
||||
{
|
||||
set_download_status(ctx, download, "ERROR");
|
||||
set_download_error(ctx, download, line + 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_download_status(ctx, download, "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:
|
||||
if (!JSStringIsEqualToUTF8CString(JSValueToStringCopy(ctx, download->status, NULL), "ERROR"))
|
||||
set_download_status(ctx, download, "DOWNLOADED");
|
||||
g_io_channel_shutdown(source, FALSE, NULL);
|
||||
ret = FALSE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
|
||||
JSObjectCallAsFunction(ctx, download->download_progressed_cb->func, NULL, 1, args, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValueRef js_start_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key;
|
||||
download_t * download;
|
||||
int fd;
|
||||
GPid pid;
|
||||
|
||||
if (!js_check_num_args(ctx, "startDownload", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
g_message("Starting download %s", download->key);
|
||||
|
||||
set_download_status(ctx, download, "DOWNLOADING");
|
||||
|
||||
fd = util_do_download(download->url, download->dest, &pid);
|
||||
|
||||
if (!fd)
|
||||
return JS_FALSE;
|
||||
|
||||
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 JS_TRUE;
|
||||
}
|
||||
|
||||
JSValueRef js_cancel_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "cancelDownload", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (download->pid)
|
||||
{
|
||||
set_download_status(ctx, download, "ERROR");
|
||||
set_download_error(ctx, download, "Download Cancelled");
|
||||
kill(download->pid, SIGTERM);
|
||||
remove(download->dest);
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSValueRef js_download_status(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "downloadStatus", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JSValueMakeString(ctx, JS_STR("NOT_REGISTERED"));
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
return download->status;
|
||||
}
|
||||
|
||||
JSValueRef js_download_error(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "downloadError", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
g_message("JS DownloadError: Retrieving error message for %s", key->str);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JSValueMakeString(ctx, JS_STR(""));
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
return download->error;
|
||||
}
|
||||
|
||||
JSValueRef js_bytes_completed(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key;
|
||||
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]);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JSValueMakeNumber(ctx, -1);
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
return JSValueMakeNumber(ctx, download->current_bytes);
|
||||
}
|
||||
|
||||
JSValueRef js_bytes_total(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key;
|
||||
download_t * download;
|
||||
|
||||
if (!js_check_num_args(ctx, "bytesTotal", argc, 1, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JSValueMakeNumber(ctx, -1);
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
return JSValueMakeNumber(ctx, download->total_bytes);
|
||||
}
|
||||
|
||||
gboolean update_extraction_progress(GIOChannel * source, GIOCondition condition, gpointer data)
|
||||
{
|
||||
int ret = TRUE;
|
||||
download_t * download = (download_t *)data;
|
||||
gchar * line;
|
||||
gsize line_length;
|
||||
GIOStatus io_status;
|
||||
JSValueRef args[1];
|
||||
JSContextRef ctx;
|
||||
|
||||
ctx = JSGlobalContextCreateInGroup(download->extraction_progressed_cb->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) && (g_str_has_prefix(line, "Error:")))
|
||||
{
|
||||
set_download_status(ctx, download, "ERROR");
|
||||
set_download_error(ctx, download, line + 7);
|
||||
}
|
||||
free(line);
|
||||
break;
|
||||
case G_IO_HUP:
|
||||
if (!JSStringIsEqualToUTF8CString(JSValueToStringCopy(ctx, download->status, NULL), "ERROR"))
|
||||
set_download_status(ctx, download, "EXTRACTED");
|
||||
g_io_channel_shutdown(source, FALSE, NULL);
|
||||
ret = FALSE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
|
||||
JSObjectCallAsFunction(ctx, download->extraction_progressed_cb->func, NULL, 1, args, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValueRef js_extract_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * key, * dir, * mod, * status, * dest_path, * sanitized_dest_path;
|
||||
download_t * download;
|
||||
int fd;
|
||||
GPid pid;
|
||||
|
||||
if (!js_check_num_args(ctx, "extractDownload", argc, 3, exception))
|
||||
return JS_NULL;
|
||||
|
||||
key = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
if (NULL == (download = find_download(key->str)))
|
||||
{
|
||||
g_string_free(key, TRUE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
g_string_free(key, TRUE);
|
||||
|
||||
status = js_get_cstr_from_val(ctx, download->status);
|
||||
|
||||
if (0 != strcmp(status->str, "DOWNLOADED"))
|
||||
{
|
||||
g_string_free(status, TRUE);
|
||||
return JSValueMakeBoolean(ctx, 0);
|
||||
}
|
||||
|
||||
g_string_free(status, TRUE);
|
||||
|
||||
set_download_status(ctx, download, "EXTRACTING");
|
||||
|
||||
dir = js_get_cstr_from_val(ctx, argv[1]);
|
||||
mod = js_get_cstr_from_val(ctx, argv[2]);
|
||||
|
||||
dest_path = g_string_new(NULL);
|
||||
g_string_printf(dest_path, "%s/%s", mod->str, dir->str);
|
||||
sanitized_dest_path = sanitize_path(dest_path->str);
|
||||
g_string_free(dest_path, TRUE);
|
||||
g_string_free(mod, TRUE);
|
||||
g_string_free(dir, TRUE);
|
||||
|
||||
fd = util_do_extract(download->dest, sanitized_dest_path->str, &pid);
|
||||
g_string_free(sanitized_dest_path, TRUE);
|
||||
|
||||
if (!fd)
|
||||
return JS_FALSE;
|
||||
|
||||
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_extraction_progress, download);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSValueRef js_metadata(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * field, * mod;
|
||||
|
||||
if (!js_check_num_args(ctx, "metadata", argc, 2, exception))
|
||||
return JS_NULL;
|
||||
|
||||
field = js_get_cstr_from_val(ctx, argv[0]);
|
||||
if (!field)
|
||||
return JS_NULL;
|
||||
|
||||
mod = js_get_cstr_from_val(ctx, argv[1]);
|
||||
if (!mod)
|
||||
{
|
||||
g_string_free(field, TRUE);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
if (0 == strcmp(field->str, "VERSION"))
|
||||
{
|
||||
mod_t * m = get_mod(mod->str);
|
||||
if (m)
|
||||
{
|
||||
g_string_free(mod, TRUE);
|
||||
g_string_free(field, TRUE);
|
||||
return JSValueMakeString(ctx, JS_STR(m->version));
|
||||
}
|
||||
}
|
||||
|
||||
g_string_free(mod, TRUE);
|
||||
g_string_free(field, TRUE);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void request_finished(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
GString * msg;
|
||||
JSValueRef args[1];
|
||||
callback_data * d = (callback_data *)data;
|
||||
js_callback * cb = (js_callback *)d->user_data;
|
||||
JSContextRef ctx = JSGlobalContextCreateInGroup(cb->ctx_group, NULL);
|
||||
|
||||
msg = util_get_output(d->output_fd);
|
||||
|
||||
close(d->output_fd);
|
||||
|
||||
if (!msg)
|
||||
{
|
||||
g_free(cb);
|
||||
g_free(d);
|
||||
return;
|
||||
}
|
||||
|
||||
args[0] = JSValueMakeString(ctx, JS_STR(msg->str));
|
||||
g_message("Request response: %s", msg->str);
|
||||
JSObjectCallAsFunction(ctx, cb->func, NULL, 1, args, NULL);
|
||||
|
||||
g_string_free(msg, TRUE);
|
||||
|
||||
g_free(cb);
|
||||
g_free(d);
|
||||
g_spawn_close_pid(pid);
|
||||
}
|
||||
|
||||
JSValueRef js_http_request(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||
{
|
||||
GString * url, * callbackname;
|
||||
js_callback * cb = (js_callback *)g_malloc(sizeof(js_callback));
|
||||
JSValueRef o;
|
||||
|
||||
if (!js_check_num_args(ctx, "httpRequest", argc, 2, exception))
|
||||
return JS_NULL;
|
||||
|
||||
url = js_get_cstr_from_val(ctx, argv[0]);
|
||||
|
||||
if (!url)
|
||||
return JS_NULL;
|
||||
|
||||
callbackname = js_get_cstr_from_val(ctx, argv[1]);
|
||||
|
||||
if (!callbackname)
|
||||
{
|
||||
g_string_free(url, TRUE);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR(callbackname->str), NULL);
|
||||
cb->func = JSValueToObject(ctx, o, NULL);
|
||||
|
||||
cb->ctx_group = JSContextGetGroup(ctx);
|
||||
|
||||
util_do_http_request(url->str, request_finished, cb);
|
||||
|
||||
g_string_free(url, TRUE);
|
||||
g_string_free(callbackname, TRUE);
|
||||
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
void js_add_functions(JSGlobalContextRef ctx, JSObjectRef target, char ** names,
|
||||
JSObjectCallAsFunctionCallback * callbacks, size_t count)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
JSObjectRef func = JS_FUNC(ctx, callbacks[i]);
|
||||
JSObjectSetProperty(ctx, target, JS_STR(names[i]), func, kJSPropertyAttributeNone, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void bind_js_bridge(WebKitWebView * view, WebKitWebFrame * frame,
|
||||
gpointer context, gpointer window_object,
|
||||
gpointer user_data)
|
||||
{
|
||||
JSGlobalContextRef js_ctx;
|
||||
JSObjectRef window_obj, external_obj;
|
||||
|
||||
int func_count = 13;
|
||||
char * names[] = { "log", "existsInMod", "launchMod", "registerDownload",
|
||||
"startDownload", "cancelDownload", "downloadStatus",
|
||||
"downloadError", "bytesCompleted", "bytesTotal",
|
||||
"extractDownload", "metadata", "httpRequest"};
|
||||
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_metadata, js_http_request };
|
||||
|
||||
js_ctx = (JSGlobalContextRef)context;
|
||||
|
||||
external_obj = JSObjectMake(js_ctx, NULL, NULL);
|
||||
|
||||
window_obj = (JSObjectRef)window_object;
|
||||
JSObjectSetProperty(js_ctx, window_obj, JS_STR("external"),
|
||||
external_obj, 0, NULL);
|
||||
|
||||
js_add_functions(js_ctx, external_obj, names, callbacks, func_count);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
|
||||
void bind_js_bridge(WebKitWebView * view, WebKitWebFrame * frame,
|
||||
gpointer context, gpointer window_object,
|
||||
gpointer user_data);
|
||||
@@ -1,497 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <webkit/webkit.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "server.h"
|
||||
#include "bridge.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define WEBSERVER_PORT 48764
|
||||
|
||||
GtkWindow * window;
|
||||
WebKitWebView * browser;
|
||||
GtkTreeStore * tree_store;
|
||||
GtkTreeView * tree;
|
||||
GdkPixbuf * generic_mod_icon;
|
||||
GtkRadioButton * gl_button, * cg_button;
|
||||
|
||||
static mod_t mods[MAX_NUM_MODS];
|
||||
static int mod_count = 0;
|
||||
|
||||
static int renderer = RENDERER_GL;
|
||||
|
||||
void free_mod(mod_t * mod)
|
||||
{
|
||||
g_free(mod->key);
|
||||
g_free(mod->title);
|
||||
g_free(mod->version);
|
||||
g_free(mod->author);
|
||||
g_free(mod->description);
|
||||
g_free(mod->requires);
|
||||
}
|
||||
|
||||
gboolean window_delete(GtkWidget * widget, GdkEvent * event,
|
||||
gpointer user_data)
|
||||
{
|
||||
int i;
|
||||
server_teardown();
|
||||
for (i = 0; i < mod_count; i++)
|
||||
free_mod(mods + i);
|
||||
gtk_main_quit();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typedef void ( * lines_callback ) (GString const * line, gpointer data);
|
||||
|
||||
//Splits console output into lines and passes each one to a callback
|
||||
void process_lines(GString * lines, lines_callback cb, gpointer data)
|
||||
{
|
||||
int prev = 0, current = 0;
|
||||
while (current < lines->len)
|
||||
{
|
||||
if (lines->str[current] == '\n')
|
||||
{
|
||||
GString * line = g_string_new_len(lines->str + prev, current - prev);
|
||||
cb(line, data);
|
||||
g_string_free(line, TRUE);
|
||||
prev = current + 1;
|
||||
}
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
#define ASSIGN_TO_MOD(FIELD) \
|
||||
mod->FIELD = g_strdup(val_start)
|
||||
|
||||
void mod_metadata_line(GString const * line, gpointer data)
|
||||
{
|
||||
mod_t * mod = (mod_t *)data;
|
||||
gchar * val_start = g_strstr_len(line->str, -1, ":") + 2;
|
||||
if (g_str_has_prefix(line->str, "Mod:"))
|
||||
{
|
||||
ASSIGN_TO_MOD(key);
|
||||
}
|
||||
else if (g_str_has_prefix(line->str, " Title:"))
|
||||
{
|
||||
ASSIGN_TO_MOD(title);
|
||||
}
|
||||
else if (g_str_has_prefix(line->str, " Version:"))
|
||||
{
|
||||
ASSIGN_TO_MOD(version);
|
||||
}
|
||||
else if (g_str_has_prefix(line->str, " Author:"))
|
||||
{
|
||||
ASSIGN_TO_MOD(author);
|
||||
}
|
||||
else if (g_str_has_prefix(line->str, " Description:"))
|
||||
{
|
||||
ASSIGN_TO_MOD(description);
|
||||
}
|
||||
else if (g_str_has_prefix(line->str, " Requires:"))
|
||||
{
|
||||
ASSIGN_TO_MOD(requires);
|
||||
}
|
||||
else if (g_str_has_prefix(line->str, " Standalone:"))
|
||||
{
|
||||
if (strcmp(val_start, "True") == 0)
|
||||
mod->standalone = TRUE;
|
||||
else
|
||||
mod->standalone = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
mod_t * get_mod(gchar const * key)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < mod_count; i++)
|
||||
{
|
||||
if (strcmp(mods[i].key, key) == 0)
|
||||
return mods + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean append_to_mod(GtkTreeModel * model, GtkTreePath * path,
|
||||
GtkTreeIter * iter, gpointer data)
|
||||
{
|
||||
mod_t * mod = (mod_t *)data;
|
||||
gchar * key;
|
||||
GtkTreeIter new_iter;
|
||||
|
||||
gtk_tree_model_get(model, iter, KEY_COLUMN, &key, -1);
|
||||
|
||||
if (!key)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strcmp(mod->requires, key) == 0)
|
||||
{
|
||||
gtk_tree_store_append(GTK_TREE_STORE(model), &new_iter, iter);
|
||||
gtk_tree_store_set(GTK_TREE_STORE(model), &new_iter,
|
||||
ICON_COLUMN, generic_mod_icon,
|
||||
KEY_COLUMN, mod->key,
|
||||
NAME_COLUMN, mod->title,
|
||||
-1);
|
||||
g_free(key);
|
||||
return TRUE;
|
||||
}
|
||||
g_free(key);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void mod_metadata_callback(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
int * out_fd = (int *)data;
|
||||
GString * msg = NULL;
|
||||
mod_t * mod = mods + mod_count;
|
||||
GtkTreeIter iter, mod_iter;
|
||||
|
||||
mod_count = (mod_count + 1) % MAX_NUM_MODS;
|
||||
|
||||
free_mod(mod);
|
||||
memset(mod, 0, sizeof(mod_t));
|
||||
|
||||
msg = util_get_output(*out_fd);
|
||||
|
||||
close(*out_fd);
|
||||
free(out_fd);
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
process_lines(msg, mod_metadata_line, mod);
|
||||
|
||||
g_string_free(msg, TRUE);
|
||||
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(tree_store), &mod_iter);
|
||||
|
||||
if (mod->standalone)
|
||||
{
|
||||
gtk_tree_store_append(tree_store, &iter, &mod_iter);
|
||||
gtk_tree_store_set(tree_store, &iter,
|
||||
ICON_COLUMN, generic_mod_icon,
|
||||
KEY_COLUMN, mod->key,
|
||||
NAME_COLUMN, mod->title,
|
||||
-1);
|
||||
}
|
||||
else if (!strlen(mod->requires))
|
||||
{
|
||||
GtkTreeIter broken_mods_iter;
|
||||
if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(tree_store),
|
||||
&broken_mods_iter, "1"))
|
||||
{
|
||||
gtk_tree_store_append(tree_store, &broken_mods_iter, NULL);
|
||||
}
|
||||
gtk_tree_store_set(tree_store, &broken_mods_iter,
|
||||
ICON_COLUMN, generic_mod_icon,
|
||||
KEY_COLUMN, mod->key,
|
||||
NAME_COLUMN, mod->title,
|
||||
-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_tree_model_foreach(GTK_TREE_MODEL(tree_store), append_to_mod, mod);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct tree_node
|
||||
{
|
||||
gchar const * key;
|
||||
gchar * node_path;
|
||||
} tree_node;
|
||||
|
||||
gboolean find_mod(GtkTreeModel * model, GtkTreePath * path,
|
||||
GtkTreeIter * iter, gpointer data)
|
||||
{
|
||||
tree_node * n = (tree_node *)data;
|
||||
gchar * key;
|
||||
|
||||
gtk_tree_model_get(model, iter,
|
||||
KEY_COLUMN, &key,
|
||||
-1);
|
||||
|
||||
if (!key)
|
||||
return FALSE;
|
||||
|
||||
if (0 == strcmp(n->key, key))
|
||||
{
|
||||
n->node_path = gtk_tree_path_to_string(path);
|
||||
g_free(key);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_free(key);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void last_mod_callback(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
int * out_fd = (int *)data;
|
||||
gchar * comma_pos = 0, * newline_pos = 0;
|
||||
GString * msg = NULL;
|
||||
tree_node n;
|
||||
|
||||
memset(&n, 0, sizeof(tree_node));
|
||||
|
||||
msg = util_get_output(*out_fd);
|
||||
|
||||
close(*out_fd);
|
||||
free(out_fd);
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (g_str_has_prefix(msg->str, "Error:"))
|
||||
{
|
||||
g_string_truncate(msg, 2);
|
||||
g_string_overwrite(msg, 0, "ra");
|
||||
}
|
||||
else if (NULL != (comma_pos = g_strstr_len(msg->str, -1, ",")))
|
||||
*comma_pos = '\0';
|
||||
else if (NULL != (newline_pos = g_strstr_len(msg->str, -1, "\n")))
|
||||
*newline_pos = '\0';
|
||||
|
||||
n.key = msg->str;
|
||||
|
||||
gtk_tree_model_foreach(GTK_TREE_MODEL(tree_store), find_mod, &n);
|
||||
|
||||
if (n.node_path)
|
||||
{
|
||||
GtkTreePath * path;
|
||||
path = gtk_tree_path_new_from_string(n.node_path);
|
||||
if (path == NULL)
|
||||
g_warning("Invalid Path");
|
||||
gtk_tree_view_expand_to_path(tree, path);
|
||||
gtk_tree_view_set_cursor(tree, path, NULL, FALSE);
|
||||
gtk_tree_path_free(path);
|
||||
g_free(n.node_path);
|
||||
}
|
||||
|
||||
g_string_free(msg, TRUE);
|
||||
}
|
||||
|
||||
void mod_list_line(GString const * mod, gpointer user)
|
||||
{
|
||||
util_get_mod_metadata(mod->str, mod_metadata_callback);
|
||||
}
|
||||
|
||||
void mod_list_callback(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
callback_data * d = (callback_data *)data;
|
||||
GString * msg = NULL;
|
||||
|
||||
msg = util_get_output(d->output_fd);
|
||||
|
||||
close(d->output_fd);
|
||||
g_free(d);
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
mod_count = 0;
|
||||
|
||||
process_lines(msg, mod_list_line, NULL);
|
||||
|
||||
util_get_setting("Game.Mods", last_mod_callback);
|
||||
|
||||
g_string_free(msg, TRUE);
|
||||
|
||||
g_spawn_close_pid(pid);
|
||||
}
|
||||
|
||||
void tree_view_selection_changed(GtkTreeView * tree_view, gpointer data)
|
||||
{
|
||||
GtkTreePath * path;
|
||||
GtkTreeIter iter;
|
||||
gchar * key;
|
||||
GString * url;
|
||||
|
||||
gtk_tree_view_get_cursor(tree_view, &path, NULL);
|
||||
|
||||
if (path == NULL)
|
||||
return;
|
||||
|
||||
gtk_tree_model_get_iter(GTK_TREE_MODEL(tree_store), &iter, path);
|
||||
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter,
|
||||
KEY_COLUMN, &key,
|
||||
-1);
|
||||
|
||||
if (!key)
|
||||
{
|
||||
gtk_tree_path_free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
url = g_string_new(NULL);
|
||||
|
||||
g_string_printf(url, "http://localhost:%d/mods/%s/mod.html", WEBSERVER_PORT, key);
|
||||
|
||||
webkit_web_view_load_uri(browser, url->str);
|
||||
|
||||
g_free(key);
|
||||
gtk_tree_path_free(path);
|
||||
g_string_free(url, TRUE);
|
||||
}
|
||||
|
||||
void make_tree_view(void)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GtkCellRenderer * pixbuf_renderer, * text_renderer;
|
||||
GtkTreeViewColumn * icon_column, * name_column;
|
||||
|
||||
tree_store = gtk_tree_store_new(N_COLUMNS, GDK_TYPE_PIXBUF,
|
||||
G_TYPE_STRING, G_TYPE_STRING);
|
||||
|
||||
gtk_tree_store_append(tree_store, &iter, NULL);
|
||||
gtk_tree_store_set(tree_store, &iter,
|
||||
NAME_COLUMN, "MODS",
|
||||
-1);
|
||||
|
||||
tree = GTK_TREE_VIEW(gtk_tree_view_new_with_model(
|
||||
GTK_TREE_MODEL(tree_store)));
|
||||
g_object_set(tree, "headers-visible", FALSE, NULL);
|
||||
g_signal_connect(tree, "cursor-changed",
|
||||
G_CALLBACK(tree_view_selection_changed), NULL);
|
||||
|
||||
pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
|
||||
text_renderer = gtk_cell_renderer_text_new();
|
||||
|
||||
icon_column = gtk_tree_view_column_new_with_attributes
|
||||
("Icon", pixbuf_renderer,
|
||||
"pixbuf", ICON_COLUMN,
|
||||
NULL);
|
||||
|
||||
gtk_tree_view_column_set_sizing(icon_column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
|
||||
|
||||
name_column = gtk_tree_view_column_new_with_attributes
|
||||
("Name", text_renderer,
|
||||
"text", NAME_COLUMN,
|
||||
NULL);
|
||||
|
||||
gtk_tree_view_column_set_sizing(name_column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
|
||||
|
||||
gtk_tree_view_append_column(tree, icon_column);
|
||||
gtk_tree_view_append_column(tree, name_column);
|
||||
}
|
||||
|
||||
void renderer_callback(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
int * fd = (int *)data;
|
||||
GString * msg;
|
||||
|
||||
msg = util_get_output(*fd);
|
||||
|
||||
close(*fd);
|
||||
g_free(fd);
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (g_str_has_prefix(msg->str, "Error:"))
|
||||
{
|
||||
g_string_free(msg, TRUE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == strcmp(msg->str, "Gl"))
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gl_button), TRUE);
|
||||
else
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cg_button), TRUE);
|
||||
|
||||
g_string_free(msg, TRUE);
|
||||
}
|
||||
|
||||
void renderer_changed(GtkToggleButton * widget, gpointer user_data)
|
||||
{
|
||||
if (!gtk_toggle_button_get_active(widget))
|
||||
return;
|
||||
|
||||
if (GTK_RADIO_BUTTON(widget) == gl_button)
|
||||
renderer = RENDERER_GL;
|
||||
else
|
||||
renderer = RENDERER_CG;
|
||||
}
|
||||
|
||||
int get_renderer(void)
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
GtkWidget * hbox1, * hbox2, * vbox;
|
||||
|
||||
int res = chdir("/usr/share/openra");
|
||||
|
||||
if (0 != res)
|
||||
res = chdir("/usr/local/share/openra");
|
||||
|
||||
if (0 != res)
|
||||
{
|
||||
g_error("Couldn't change to OpenRA working directory");
|
||||
return 1;
|
||||
}
|
||||
|
||||
server_init(WEBSERVER_PORT);
|
||||
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
|
||||
gtk_window_set_title(window, "OpenRA Launcher");
|
||||
gtk_window_set_default_size(window, 800, 600);
|
||||
|
||||
browser = WEBKIT_WEB_VIEW(webkit_web_view_new());
|
||||
g_signal_connect(browser, "window-object-cleared",
|
||||
G_CALLBACK(bind_js_bridge), 0);
|
||||
|
||||
generic_mod_icon = gdk_pixbuf_new_from_file_at_size("soviet-logo.png",
|
||||
16, 16, NULL);
|
||||
|
||||
make_tree_view();
|
||||
|
||||
util_get_mod_list(mod_list_callback);
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 0);
|
||||
hbox1 = gtk_hbox_new(FALSE, 0);
|
||||
hbox2 = gtk_hbox_new(FALSE, 0);
|
||||
|
||||
gtk_widget_set_size_request(GTK_WIDGET(tree), 250, 0);
|
||||
|
||||
gtk_box_pack_end(GTK_BOX(hbox1), GTK_WIDGET(browser), TRUE, TRUE, 0);
|
||||
gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(tree), TRUE, TRUE, 0);
|
||||
|
||||
gl_button = GTK_RADIO_BUTTON(gtk_radio_button_new_with_label(NULL, "GL Renderer"));
|
||||
g_signal_connect(gl_button, "toggled", G_CALLBACK(renderer_changed), 0);
|
||||
cg_button = GTK_RADIO_BUTTON(gtk_radio_button_new_with_label_from_widget(gl_button, "CG Renderer"));
|
||||
g_signal_connect(cg_button, "toggled", G_CALLBACK(renderer_changed), 0);
|
||||
|
||||
util_get_setting("Graphics.Renderer", renderer_callback);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(hbox2), GTK_WIDGET(gl_button), FALSE, FALSE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(hbox2), GTK_WIDGET(cg_button), FALSE, FALSE, 5);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox1, TRUE, TRUE, 0);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 10);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(window), vbox);
|
||||
|
||||
gtk_widget_show_all(GTK_WIDGET(window));
|
||||
g_signal_connect(window, "delete-event", G_CALLBACK(window_delete), 0);
|
||||
|
||||
gtk_main();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#ifndef OPENRA_MAIN_H
|
||||
#define OPENRA_MAIN_H
|
||||
|
||||
enum
|
||||
{
|
||||
ICON_COLUMN,
|
||||
KEY_COLUMN,
|
||||
NAME_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
RENDERER_GL,
|
||||
RENDERER_CG
|
||||
};
|
||||
|
||||
#define MAX_NUM_MODS 64
|
||||
|
||||
typedef struct mod_t
|
||||
{
|
||||
gchar * key;
|
||||
gchar * title;
|
||||
gchar * version;
|
||||
gchar * author;
|
||||
gchar * description;
|
||||
gchar * requires;
|
||||
int standalone;
|
||||
} mod_t;
|
||||
|
||||
mod_t * get_mod(gchar const * key);
|
||||
|
||||
int get_renderer(void);
|
||||
|
||||
#endif
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define MHD_PLATFORM_H
|
||||
#include <microhttpd.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct MHD_Daemon * server;
|
||||
|
||||
int try_file_response(const char * url, struct MHD_Connection * connection)
|
||||
{
|
||||
int fd, ret;
|
||||
struct MHD_Response * response;
|
||||
struct stat sbuf;
|
||||
|
||||
g_message("Web server: Opening %s", url + 1);
|
||||
|
||||
if ((-1 == (fd = open(url + 1, O_RDONLY))) ||
|
||||
(0 != fstat(fd, &sbuf)))
|
||||
{
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
response = MHD_create_response_from_fd(sbuf.st_size, fd);
|
||||
MHD_add_response_header(response, "Content-Type", "text/html");
|
||||
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
|
||||
MHD_destroy_response(response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int access_handler_callback(void * userdata,
|
||||
struct MHD_Connection * connection,
|
||||
const char * url,
|
||||
const char * method,
|
||||
const char * version,
|
||||
const char * upload_data,
|
||||
size_t * upload_data_size,
|
||||
void ** userpointer)
|
||||
{
|
||||
struct MHD_Response * response;
|
||||
int ret = MHD_NO;
|
||||
char * text = "1";
|
||||
g_message("Web server: Got request for %s", url);
|
||||
|
||||
if ((ret = try_file_response(url, connection)))
|
||||
return ret;
|
||||
|
||||
text = "<html><head><title>Not found</title></head><body>File not found</body></html>";
|
||||
|
||||
response = MHD_create_response_from_data(strlen(text), (void *)text,
|
||||
MHD_NO, MHD_NO);
|
||||
|
||||
ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
|
||||
MHD_destroy_response(response);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int server_init(int port)
|
||||
{
|
||||
server = MHD_start_daemon(MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY,
|
||||
port, NULL, NULL,
|
||||
&access_handler_callback, NULL,
|
||||
MHD_OPTION_END);
|
||||
|
||||
if (!server)
|
||||
{
|
||||
g_critical("Could not start web server.");
|
||||
return 0;
|
||||
}
|
||||
g_message("Server initialised.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void * server_get_daemon(void)
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
void server_teardown(void)
|
||||
{
|
||||
MHD_stop_daemon(server);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
|
||||
int server_init(int);
|
||||
void server_teardown(void);
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
gboolean util_do_command_async(gchar * command, GChildWatchFunc callback, gpointer user_data)
|
||||
{
|
||||
GPid child_pid;
|
||||
gchar * spawn_args[] = { "mono", "OpenRA.Utility.exe", command, NULL };
|
||||
callback_data * d = (callback_data *)g_malloc(sizeof(callback_data));
|
||||
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, &(d->output_fd), NULL, NULL);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
g_free(d);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
d->user_data = user_data;
|
||||
g_child_watch_add(child_pid, callback, d);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean util_get_mod_list (GChildWatchFunc callback)
|
||||
{
|
||||
return util_do_command_async("-l", callback, NULL);
|
||||
}
|
||||
|
||||
gboolean util_do_command_blocking(gchar * command, GChildWatchFunc callback)
|
||||
{
|
||||
GPid child_pid;
|
||||
int status;
|
||||
gint * out_fd = (gint *)g_malloc(sizeof(gint));
|
||||
gchar * 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)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
waitpid(child_pid, &status, 0);
|
||||
|
||||
callback(child_pid, status, out_fd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean util_get_mod_metadata(gchar const * mod, GChildWatchFunc callback)
|
||||
{
|
||||
GString * util_args = g_string_new(NULL);
|
||||
gboolean return_val;
|
||||
|
||||
g_string_printf(util_args, "-i=%s", mod);
|
||||
return_val = util_do_command_blocking(util_args->str, callback);
|
||||
g_string_free(util_args, TRUE);
|
||||
return return_val;
|
||||
}
|
||||
|
||||
gboolean util_get_setting(gchar const * setting, GChildWatchFunc callback)
|
||||
{
|
||||
GString * command = g_string_new(NULL);
|
||||
gboolean return_val;
|
||||
|
||||
g_string_printf(command, "--settings-value=~/.openra,%s", setting);
|
||||
return_val = util_do_command_blocking(command->str, callback);
|
||||
g_string_free(command, TRUE);
|
||||
return return_val;
|
||||
}
|
||||
|
||||
gchar * get_graphical_sudo_client(void)
|
||||
{
|
||||
int num_clients = 2, i;
|
||||
gchar * clients[] = { "gksudo", "kdesudo" };
|
||||
|
||||
for (i = 0; i < num_clients; i++)
|
||||
{
|
||||
FILE * f;
|
||||
GString * test_path = g_string_new(NULL);
|
||||
g_string_printf(test_path, "/usr/bin/%s", clients[i]);
|
||||
f = fopen(test_path->str, "r");
|
||||
g_string_free(test_path, TRUE);
|
||||
if (f)
|
||||
return g_strdup(clients[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gint util_spawn_with_command(gchar const * command, gchar const * arg1, gchar const * arg2, gboolean root, GPid * pid)
|
||||
{
|
||||
GString * complete_command = g_string_new(NULL);
|
||||
gint out_fd;
|
||||
gboolean result;
|
||||
|
||||
g_string_printf(complete_command, "%s%s,%s", command, arg1, arg2);
|
||||
|
||||
if (root)
|
||||
{
|
||||
gchar * sudo = get_graphical_sudo_client();
|
||||
gchar * launch_args[] = { sudo, "--", "mono", "OpenRA.Utility.exe", NULL, NULL };
|
||||
launch_args[4] = complete_command->str;
|
||||
result = g_spawn_async_with_pipes(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, pid, NULL, &out_fd, NULL, NULL);
|
||||
g_free(sudo);
|
||||
}
|
||||
else
|
||||
{
|
||||
gchar * launch_args[] = { "mono", "OpenRA.Utility.exe", NULL, NULL };
|
||||
launch_args[2] = complete_command->str;
|
||||
result = g_spawn_async_with_pipes(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, pid, NULL, &out_fd, NULL, NULL);
|
||||
}
|
||||
|
||||
g_string_free(complete_command, TRUE);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return out_fd;
|
||||
}
|
||||
|
||||
gint util_do_download(gchar const * url, gchar const * dest, GPid * pid)
|
||||
{
|
||||
return util_spawn_with_command("--download-url=", url, dest, FALSE, pid);
|
||||
}
|
||||
|
||||
gint util_do_extract(gchar const * target, gchar const * dest, GPid * pid)
|
||||
{
|
||||
return util_spawn_with_command("--extract-zip=", target, dest, TRUE, pid);
|
||||
}
|
||||
|
||||
gboolean util_do_http_request(gchar const * url, GChildWatchFunc callback, gpointer user_data)
|
||||
{
|
||||
gboolean b;
|
||||
GString * command = g_string_new(NULL);
|
||||
g_string_printf(command, "--download-url=%s", url);
|
||||
b = util_do_command_async(command->str, callback, user_data);
|
||||
g_string_free(command, TRUE);
|
||||
return b;
|
||||
}
|
||||
|
||||
GString * util_get_output(int fd)
|
||||
{
|
||||
char buffer[1024];
|
||||
GString * msg = g_string_new(NULL);
|
||||
int read_bytes = 0;
|
||||
while (0 != (read_bytes = read(fd, buffer, 1024)))
|
||||
{
|
||||
if (-1 == read_bytes)
|
||||
{
|
||||
g_error("Error reading from command output");
|
||||
g_string_free(msg, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_string_append_len(msg, buffer, read_bytes);
|
||||
}
|
||||
|
||||
g_string_append_c(msg, '\0');
|
||||
|
||||
return msg;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
|
||||
typedef struct callback_data
|
||||
{
|
||||
gint output_fd;
|
||||
gpointer user_data;
|
||||
} callback_data;
|
||||
|
||||
gboolean util_get_mod_list (GChildWatchFunc);
|
||||
gboolean util_get_mod_metadata(gchar const *, GChildWatchFunc);
|
||||
gboolean util_get_setting(gchar const *, GChildWatchFunc);
|
||||
gint util_do_download(gchar const *, gchar const *, GPid *);
|
||||
gint util_do_extract(gchar const *, gchar const *, GPid *);
|
||||
gboolean util_do_http_request(gchar const *, GChildWatchFunc, gpointer);
|
||||
GString * util_get_output(int);
|
||||
@@ -1,227 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
public enum DownloadStatus
|
||||
{
|
||||
NOT_REGISTERED, AVAILABLE, DOWNLOADING, DOWNLOADED, EXTRACTING, EXTRACTED, ERROR
|
||||
}
|
||||
|
||||
class Download : IDisposable
|
||||
{
|
||||
DownloadStatus status = DownloadStatus.NOT_REGISTERED;
|
||||
string url = "", target = "", key = "";
|
||||
BackgroundWorker downloadBGWorker, extractBGWorker;
|
||||
int bytesTotal = 0, bytesDone = 0;
|
||||
string errorMessage = "";
|
||||
HtmlDocument document;
|
||||
|
||||
public string ErrorMessage
|
||||
{
|
||||
get { return errorMessage; }
|
||||
}
|
||||
|
||||
public int BytesDone
|
||||
{
|
||||
get { return bytesDone; }
|
||||
}
|
||||
|
||||
public int BytesTotal
|
||||
{
|
||||
get { return bytesTotal; }
|
||||
}
|
||||
|
||||
public DownloadStatus Status
|
||||
{
|
||||
get { return status; }
|
||||
}
|
||||
|
||||
public Download(HtmlDocument document, string key, string url, string filename)
|
||||
{
|
||||
this.url = url;
|
||||
this.key = key;
|
||||
this.document = document;
|
||||
target = Path.Combine(Path.GetTempPath(), filename);
|
||||
if (File.Exists(target))
|
||||
status = DownloadStatus.DOWNLOADED;
|
||||
else
|
||||
status = DownloadStatus.AVAILABLE;
|
||||
|
||||
downloadBGWorker = new BackgroundWorker()
|
||||
{
|
||||
WorkerReportsProgress = true,
|
||||
WorkerSupportsCancellation = true
|
||||
};
|
||||
|
||||
downloadBGWorker.DoWork += RunDownload;
|
||||
downloadBGWorker.ProgressChanged += UpdateProgress;
|
||||
downloadBGWorker.RunWorkerCompleted += DownloadFinished;
|
||||
|
||||
extractBGWorker = new BackgroundWorker();
|
||||
|
||||
extractBGWorker.DoWork += DoExtraction;
|
||||
extractBGWorker.RunWorkerCompleted += ExtractionFinished;
|
||||
}
|
||||
|
||||
public void StartDownload()
|
||||
{
|
||||
if (!downloadBGWorker.IsBusy)
|
||||
{
|
||||
status = DownloadStatus.DOWNLOADING;
|
||||
downloadBGWorker.RunWorkerAsync(new string[] { url, target });
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelDownload()
|
||||
{
|
||||
if (downloadBGWorker.IsBusy)
|
||||
downloadBGWorker.CancelAsync();
|
||||
}
|
||||
|
||||
public void ExtractDownload(string destPath)
|
||||
{
|
||||
if (!extractBGWorker.IsBusy)
|
||||
{
|
||||
status = DownloadStatus.EXTRACTING;
|
||||
extractBGWorker.RunWorkerAsync(new string[] { target, destPath });
|
||||
}
|
||||
}
|
||||
|
||||
static void RunDownload(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
var bgWorker = sender as BackgroundWorker;
|
||||
string[] args = e.Argument as string[];
|
||||
string url = args[0];
|
||||
string dest = args[1];
|
||||
string pipename = UtilityProgram.GetPipeName();
|
||||
var p = UtilityProgram.Call("--download-url", pipename, null, url, dest);
|
||||
Regex r = new Regex(@"(\d{1,3})% (\d+)/(\d+) bytes");
|
||||
|
||||
NamedPipeClientStream pipe = new NamedPipeClientStream(".", pipename, PipeDirection.In);
|
||||
pipe.Connect();
|
||||
|
||||
using (var response = new StreamReader(pipe))
|
||||
{
|
||||
while (!p.HasExited)
|
||||
{
|
||||
string s = response.ReadLine();
|
||||
if (string.IsNullOrEmpty(s)) continue;
|
||||
if (Util.IsError(ref s))
|
||||
throw new Exception(s);
|
||||
|
||||
if (bgWorker.CancellationPending)
|
||||
{
|
||||
e.Cancel = true;
|
||||
p.Kill();
|
||||
return;
|
||||
}
|
||||
if (!r.IsMatch(s)) continue;
|
||||
var m = r.Match(s);
|
||||
bgWorker.ReportProgress(int.Parse(m.Groups[1].Value),
|
||||
new string[] { m.Groups[2].Value, m.Groups[3].Value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateProgress(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
string[] s = e.UserState as string[];
|
||||
bytesDone = int.Parse(s[0]);
|
||||
bytesTotal = int.Parse(s[1]);
|
||||
document.InvokeScript("downloadProgressed", new object[] { key });
|
||||
}
|
||||
|
||||
void DownloadFinished(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
if (e.Error != null)
|
||||
{
|
||||
status = DownloadStatus.ERROR;
|
||||
errorMessage = e.Error.Message;
|
||||
//if (File.Exists(target))
|
||||
// File.Delete(target);
|
||||
document.InvokeScript("downloadProgressed", new object[] { key });
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Cancelled)
|
||||
{
|
||||
status = DownloadStatus.ERROR;
|
||||
errorMessage = "Download Cancelled";
|
||||
//if (File.Exists(target))
|
||||
// File.Delete(target);
|
||||
document.InvokeScript("downloadProgressed", new object[] { key });
|
||||
return;
|
||||
}
|
||||
|
||||
status = DownloadStatus.DOWNLOADED;
|
||||
document.InvokeScript("downloadProgressed", new object[] { key });
|
||||
}
|
||||
|
||||
void DoExtraction(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
string[] args = e.Argument as string[];
|
||||
string zipFile = args[0];
|
||||
string destPath = args[1];
|
||||
|
||||
string pipename = UtilityProgram.GetPipeName();
|
||||
var p = UtilityProgram.CallWithAdmin("--extract-zip", pipename, zipFile, destPath);
|
||||
var pipe = new NamedPipeClientStream(".", pipename, PipeDirection.In);
|
||||
|
||||
pipe.Connect();
|
||||
|
||||
using (var reader = new StreamReader(pipe))
|
||||
{
|
||||
while (!p.HasExited)
|
||||
{
|
||||
string s = reader.ReadLine();
|
||||
if (string.IsNullOrEmpty(s)) continue;
|
||||
if (Util.IsError(ref s))
|
||||
throw new Exception(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractionFinished(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
if (e.Error != null)
|
||||
{
|
||||
status = DownloadStatus.ERROR;
|
||||
errorMessage = e.Error.Message;
|
||||
document.InvokeScript("extractProgressed", new object[] { key });
|
||||
}
|
||||
|
||||
|
||||
status = DownloadStatus.EXTRACTED;
|
||||
document.InvokeScript("extractProgressed", new object[] { key });
|
||||
}
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
~Download()
|
||||
{
|
||||
if (!disposed)
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (status == DownloadStatus.DOWNLOADING && File.Exists(target))
|
||||
File.Delete(target);
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
public class JSBridge
|
||||
{
|
||||
Dictionary<string, Mod> allMods = new Dictionary<string,Mod>();
|
||||
|
||||
public Dictionary<string, Mod> AllMods
|
||||
{
|
||||
get { return allMods; }
|
||||
set { allMods = value; }
|
||||
}
|
||||
Dictionary<string, Download> downloads = new Dictionary<string,Download>();
|
||||
|
||||
readonly Control hostControl;
|
||||
HtmlDocument document = null;
|
||||
|
||||
public JSBridge(Control hostControl)
|
||||
{
|
||||
this.hostControl = hostControl;
|
||||
}
|
||||
|
||||
public HtmlDocument Document
|
||||
{
|
||||
get { return document; }
|
||||
set { document = value; }
|
||||
}
|
||||
|
||||
public bool existsInMod(string file, string mod)
|
||||
{
|
||||
string cleanedPath = CleanPath(file);
|
||||
return File.Exists(string.Format("mods{0}{1}{0}{2}", Path.DirectorySeparatorChar, mod, cleanedPath));
|
||||
}
|
||||
|
||||
public void log(string message)
|
||||
{
|
||||
Console.WriteLine("js: " + message);
|
||||
}
|
||||
|
||||
public bool launchMod(string mod)
|
||||
{
|
||||
string m = mod;
|
||||
List<string> modList = new List<string>();
|
||||
modList.Add(m);
|
||||
if (!allMods.ContainsKey(m))
|
||||
{
|
||||
MessageBox.Show("allMods does not contain " + m);
|
||||
return false;
|
||||
}
|
||||
while (!string.IsNullOrEmpty(allMods[m].Requires))
|
||||
{
|
||||
m = allMods[m].Requires;
|
||||
modList.Add(m);
|
||||
}
|
||||
|
||||
Process p = new Process();
|
||||
p.StartInfo.FileName = "OpenRA.Game.exe";
|
||||
p.StartInfo.Arguments = "Game.Mods=" + string.Join(",", modList.ToArray());
|
||||
p.StartInfo.Arguments += " Graphics.Renderer=" + Launcher.Renderer;
|
||||
p.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
Regex p = new Regex(@"\.\.[/\\]?");
|
||||
string CleanPath(string path)
|
||||
{
|
||||
string root = Path.GetPathRoot(path);
|
||||
string cleanedPath = path.Remove(0, root.Length);
|
||||
return p.Replace(cleanedPath, "");
|
||||
}
|
||||
|
||||
public void registerDownload(string key, string url, string filename)
|
||||
{
|
||||
string cleanedPath = CleanPath(filename);
|
||||
if (!downloads.ContainsKey(key))
|
||||
downloads.Add(key, new Download(document, key, url, cleanedPath));
|
||||
else
|
||||
downloads[key] = new Download(document, key, url, cleanedPath);
|
||||
}
|
||||
|
||||
public bool startDownload(string key)
|
||||
{
|
||||
if (!downloads.ContainsKey(key))
|
||||
return false;
|
||||
|
||||
downloads[key].StartDownload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool cancelDownload(string key)
|
||||
{
|
||||
if (!downloads.ContainsKey(key))
|
||||
return false;
|
||||
|
||||
downloads[key].CancelDownload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string downloadStatus(string key)
|
||||
{
|
||||
if (!downloads.ContainsKey(key))
|
||||
return DownloadStatus.NOT_REGISTERED.ToString();
|
||||
|
||||
return downloads[key].Status.ToString();
|
||||
}
|
||||
|
||||
public string downloadError(string key)
|
||||
{
|
||||
if (!downloads.ContainsKey(key))
|
||||
return "";
|
||||
|
||||
return downloads[key].ErrorMessage;
|
||||
}
|
||||
|
||||
public int bytesCompleted(string key)
|
||||
{
|
||||
if (!downloads.ContainsKey(key))
|
||||
return -1;
|
||||
|
||||
return downloads[key].BytesDone;
|
||||
}
|
||||
|
||||
public int bytesTotal(string key)
|
||||
{
|
||||
if (!downloads.ContainsKey(key))
|
||||
return -1;
|
||||
|
||||
return downloads[key].BytesTotal;
|
||||
}
|
||||
|
||||
public bool extractDownload(string key, string targetDir, string mod)
|
||||
{
|
||||
string cleanedPath = CleanPath(targetDir);
|
||||
|
||||
string targetPath = Path.Combine(mod, cleanedPath);
|
||||
|
||||
if (!downloads.ContainsKey(key))
|
||||
return false;
|
||||
|
||||
if (downloads[key].Status != DownloadStatus.DOWNLOADED)
|
||||
return false;
|
||||
|
||||
downloads[key].ExtractDownload(targetPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public string metadata(string field, string mod)
|
||||
{
|
||||
if (field == "VERSION")
|
||||
return allMods[mod].Version;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public void httpRequest(string url, string callbackName)
|
||||
{
|
||||
string pipename = UtilityProgram.GetPipeName();
|
||||
var pipe = new NamedPipeClientStream(".", pipename, PipeDirection.In);
|
||||
|
||||
var p = UtilityProgram.Call("--download-url", pipename,
|
||||
(_, e) =>
|
||||
{
|
||||
using (var reader = new StreamReader(pipe))
|
||||
{
|
||||
var data = reader.ReadToEnd();
|
||||
hostControl.Invoke((Delegate)(Action)(() =>
|
||||
document.InvokeScript(callbackName, new object[] { data })));
|
||||
}
|
||||
}, url);
|
||||
|
||||
pipe.Connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
174
OpenRA.Launcher/Launcher.Designer.cs
generated
174
OpenRA.Launcher/Launcher.Designer.cs
generated
@@ -1,174 +0,0 @@
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
partial class Launcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("Mods", -2, -2);
|
||||
this.installButton = new System.Windows.Forms.Button();
|
||||
this.installModDialog = new System.Windows.Forms.OpenFileDialog();
|
||||
this.treeView = new System.Windows.Forms.TreeView();
|
||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||
this.webBrowser = new System.Windows.Forms.WebBrowser();
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.cgButton = new System.Windows.Forms.RadioButton();
|
||||
this.glButton = new System.Windows.Forms.RadioButton();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.Panel2.SuspendLayout();
|
||||
this.splitContainer1.SuspendLayout();
|
||||
this.panel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// installButton
|
||||
//
|
||||
this.installButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.installButton.Location = new System.Drawing.Point(12, 12);
|
||||
this.installButton.Name = "installButton";
|
||||
this.installButton.Size = new System.Drawing.Size(116, 23);
|
||||
this.installButton.TabIndex = 2;
|
||||
this.installButton.Text = "Install Mod...";
|
||||
this.installButton.UseVisualStyleBackColor = true;
|
||||
this.installButton.Click += new System.EventHandler(this.InstallMod);
|
||||
//
|
||||
// installModDialog
|
||||
//
|
||||
this.installModDialog.Filter = "Zip files|*.zip";
|
||||
this.installModDialog.RestoreDirectory = true;
|
||||
//
|
||||
// treeView
|
||||
//
|
||||
this.treeView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.treeView.Location = new System.Drawing.Point(0, 0);
|
||||
this.treeView.Name = "treeView";
|
||||
treeNode1.ImageIndex = -2;
|
||||
treeNode1.Name = "ModsNode";
|
||||
treeNode1.SelectedImageIndex = -2;
|
||||
treeNode1.Text = "Mods";
|
||||
this.treeView.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
|
||||
treeNode1});
|
||||
this.treeView.ShowLines = false;
|
||||
this.treeView.Size = new System.Drawing.Size(160, 465);
|
||||
this.treeView.TabIndex = 3;
|
||||
this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
|
||||
//
|
||||
// splitContainer1
|
||||
//
|
||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
|
||||
this.splitContainer1.Name = "splitContainer1";
|
||||
//
|
||||
// splitContainer1.Panel1
|
||||
//
|
||||
this.splitContainer1.Panel1.Controls.Add(this.treeView);
|
||||
//
|
||||
// splitContainer1.Panel2
|
||||
//
|
||||
this.splitContainer1.Panel2.Controls.Add(this.webBrowser);
|
||||
this.splitContainer1.Size = new System.Drawing.Size(671, 465);
|
||||
this.splitContainer1.SplitterDistance = 160;
|
||||
this.splitContainer1.TabIndex = 4;
|
||||
//
|
||||
// webBrowser
|
||||
//
|
||||
this.webBrowser.AllowWebBrowserDrop = false;
|
||||
this.webBrowser.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.webBrowser.Location = new System.Drawing.Point(0, 0);
|
||||
this.webBrowser.MinimumSize = new System.Drawing.Size(20, 20);
|
||||
this.webBrowser.Name = "webBrowser";
|
||||
this.webBrowser.ScrollBarsEnabled = false;
|
||||
this.webBrowser.Size = new System.Drawing.Size(507, 465);
|
||||
this.webBrowser.TabIndex = 0;
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.Controls.Add(this.cgButton);
|
||||
this.panel1.Controls.Add(this.glButton);
|
||||
this.panel1.Controls.Add(this.installButton);
|
||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.panel1.Location = new System.Drawing.Point(0, 465);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(671, 47);
|
||||
this.panel1.TabIndex = 5;
|
||||
//
|
||||
// cgButton
|
||||
//
|
||||
this.cgButton.AutoSize = true;
|
||||
this.cgButton.Location = new System.Drawing.Point(481, 18);
|
||||
this.cgButton.Name = "cgButton";
|
||||
this.cgButton.Size = new System.Drawing.Size(87, 17);
|
||||
this.cgButton.TabIndex = 4;
|
||||
this.cgButton.TabStop = true;
|
||||
this.cgButton.Text = "CG Renderer";
|
||||
this.cgButton.UseVisualStyleBackColor = true;
|
||||
this.cgButton.CheckedChanged += new System.EventHandler(this.rendererChanged);
|
||||
//
|
||||
// glButton
|
||||
//
|
||||
this.glButton.AutoSize = true;
|
||||
this.glButton.Location = new System.Drawing.Point(574, 18);
|
||||
this.glButton.Name = "glButton";
|
||||
this.glButton.Size = new System.Drawing.Size(86, 17);
|
||||
this.glButton.TabIndex = 3;
|
||||
this.glButton.TabStop = true;
|
||||
this.glButton.Text = "GL Renderer";
|
||||
this.glButton.UseVisualStyleBackColor = true;
|
||||
this.glButton.CheckedChanged += new System.EventHandler(this.rendererChanged);
|
||||
//
|
||||
// Launcher
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(671, 512);
|
||||
this.Controls.Add(this.splitContainer1);
|
||||
this.Controls.Add(this.panel1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "Launcher";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "OpenRA Launcher";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.formClosing);
|
||||
this.splitContainer1.Panel1.ResumeLayout(false);
|
||||
this.splitContainer1.Panel2.ResumeLayout(false);
|
||||
this.splitContainer1.ResumeLayout(false);
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.panel1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button installButton;
|
||||
private System.Windows.Forms.OpenFileDialog installModDialog;
|
||||
private System.Windows.Forms.TreeView treeView;
|
||||
private System.Windows.Forms.SplitContainer splitContainer1;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.WebBrowser webBrowser;
|
||||
private System.Windows.Forms.RadioButton cgButton;
|
||||
private System.Windows.Forms.RadioButton glButton;
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using System.IO.Pipes;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
public partial class Launcher : Form
|
||||
{
|
||||
Dictionary<string, Mod> allMods;
|
||||
public static string Renderer = "Gl";
|
||||
static string SupportDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + Path.DirectorySeparatorChar + "OpenRA";
|
||||
|
||||
public Launcher()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Util.UacShield(installButton);
|
||||
|
||||
webBrowser.ObjectForScripting = new JSBridge(this);
|
||||
webBrowser.DocumentCompleted += (o, e) =>
|
||||
{
|
||||
var b = o as WebBrowser;
|
||||
(b.ObjectForScripting as JSBridge).Document = b.Document;
|
||||
};
|
||||
RefreshMods();
|
||||
string response = UtilityProgram.CallSimpleResponse("--settings-value", SupportDir, "Graphics.Renderer");
|
||||
if (Util.IsError(ref response) || response.Equals("gl", StringComparison.InvariantCultureIgnoreCase))
|
||||
glButton.Checked = true;
|
||||
else
|
||||
cgButton.Checked = true;
|
||||
}
|
||||
|
||||
Mod GetMetadata(string mod)
|
||||
{
|
||||
string responseString = UtilityProgram.CallSimpleResponse("-i", mod);
|
||||
|
||||
if (Util.IsError(ref responseString)) return null;
|
||||
string[] lines = responseString.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
lines[i] = lines[i].Trim('\r');
|
||||
|
||||
string title = "", version = "", author = "", description = "", requires = "";
|
||||
bool standalone = false;
|
||||
foreach (string line in lines)
|
||||
{
|
||||
string s = line.Trim(' ', '\r', '\n');
|
||||
int i = s.IndexOf(':');
|
||||
if (i + 2 > s.Length) continue;
|
||||
string value = s.Substring(i + 2);
|
||||
switch (s.Substring(0, i))
|
||||
{
|
||||
case "Title":
|
||||
title = value;
|
||||
break;
|
||||
case "Version":
|
||||
version = value;
|
||||
break;
|
||||
case "Author":
|
||||
author = value;
|
||||
break;
|
||||
case "Description":
|
||||
description = value;
|
||||
break;
|
||||
case "Requires":
|
||||
requires = value;
|
||||
break;
|
||||
case "Standalone":
|
||||
standalone = bool.Parse(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Mod(title, version, author, description, requires, standalone);
|
||||
}
|
||||
|
||||
void RefreshMods()
|
||||
{
|
||||
string responseString = UtilityProgram.CallSimpleResponse("--list-mods");
|
||||
|
||||
string[] mods;
|
||||
if (!Util.IsError(ref responseString))
|
||||
mods = responseString.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
else
|
||||
throw new Exception(string.Format("Could not list mods: {0}", responseString));
|
||||
|
||||
for (int i = 0; i < mods.Length; i++)
|
||||
mods[i] = mods[i].Trim('\r');
|
||||
|
||||
allMods = mods.ToDictionary(x => x, x => GetMetadata(x));
|
||||
|
||||
(webBrowser.ObjectForScripting as JSBridge).AllMods = allMods;
|
||||
|
||||
RefreshModTree(treeView, allMods.Keys.ToArray());
|
||||
}
|
||||
|
||||
private void InstallMod(object sender, EventArgs e)
|
||||
{
|
||||
if (installModDialog.ShowDialog() != DialogResult.OK) return;
|
||||
string pipename = UtilityProgram.GetPipeName();
|
||||
var p = UtilityProgram.CallWithAdmin("--extract-zip", pipename, installModDialog.FileName, "");
|
||||
var pipe = new NamedPipeClientStream(".", pipename, PipeDirection.In);
|
||||
pipe.Connect();
|
||||
|
||||
p.WaitForExit();
|
||||
|
||||
using (var response = new StreamReader(pipe))
|
||||
{
|
||||
response.ReadToEnd();
|
||||
}
|
||||
|
||||
RefreshMods();
|
||||
}
|
||||
|
||||
void RefreshModTree(TreeView treeView, string[] modList)
|
||||
{
|
||||
treeView.Nodes["ModsNode"].Nodes.Clear();
|
||||
Dictionary<string, TreeNode> nodes;
|
||||
nodes = modList.Where(x => allMods[x].Standalone).ToDictionary(x => x,
|
||||
x => new TreeNode(allMods[x].Title) { Name = x });
|
||||
string[] rootMods = modList.Where(x => allMods[x].Standalone).ToArray();
|
||||
Stack<string> remaining = new Stack<string>(modList.Except(nodes.Keys));
|
||||
|
||||
bool progress = true;
|
||||
while (remaining.Count > 0 && progress)
|
||||
{
|
||||
progress = false;
|
||||
string s = remaining.Pop();
|
||||
var n = new TreeNode(allMods[s].Title) { Name = s };
|
||||
if (allMods[s].Requires == null) { remaining.Push(s); continue; }
|
||||
if (!nodes.ContainsKey(allMods[s].Requires)) { remaining.Push(s); continue; }
|
||||
nodes[allMods[s].Requires].Nodes.Add(n);
|
||||
nodes.Add(s, n);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
foreach (string s in rootMods)
|
||||
treeView.Nodes["ModsNode"].Nodes.Add(nodes[s]);
|
||||
|
||||
if (remaining.Count > 0)
|
||||
{
|
||||
var unspecified = new TreeNode("<Unspecified Dependency>") { ForeColor = SystemColors.GrayText };
|
||||
var missing = new TreeNode("<Missing Dependency>") { ForeColor = SystemColors.GrayText };
|
||||
|
||||
foreach (var s in remaining)
|
||||
{
|
||||
if (allMods[s].Requires == null)
|
||||
unspecified.Nodes.Add(new TreeNode(allMods[s].Title)
|
||||
{ ForeColor = SystemColors.GrayText, Name = s });
|
||||
else if (!nodes.ContainsKey(allMods[s].Requires))
|
||||
missing.Nodes.Add(new TreeNode(allMods[s].Title)
|
||||
{ ForeColor = SystemColors.GrayText, Name = s });
|
||||
}
|
||||
string brokenKey = "BrokenModsNode";
|
||||
if (treeView.Nodes[brokenKey] != null)
|
||||
treeView.Nodes.RemoveByKey(brokenKey);
|
||||
treeView.Nodes.Add(brokenKey, "Broken Mods");
|
||||
treeView.Nodes[brokenKey].Nodes.Add(unspecified);
|
||||
treeView.Nodes[brokenKey].Nodes.Add(missing);
|
||||
}
|
||||
treeView.Nodes["ModsNode"].ExpandAll();
|
||||
treeView.Invalidate();
|
||||
|
||||
string responseString = UtilityProgram.CallSimpleResponse("--settings-value", SupportDir, "Game.Mods");
|
||||
|
||||
if (Util.IsError(ref responseString))
|
||||
treeView.SelectedNode = treeView.Nodes["ModsNode"].Nodes["ra"];
|
||||
else
|
||||
treeView.SelectedNode = treeView.Nodes["ModsNode"].Nodes[responseString];
|
||||
}
|
||||
|
||||
void treeView_AfterSelect(object sender, TreeViewEventArgs e)
|
||||
{
|
||||
Mod selectedMod;
|
||||
if (!allMods.TryGetValue(e.Node.Name, out selectedMod)) return;
|
||||
string modHtmlPath = string.Format("mods{0}{1}{0}mod.html", Path.DirectorySeparatorChar, e.Node.Name);
|
||||
if (!File.Exists(modHtmlPath)) return;
|
||||
webBrowser.Navigate(Path.GetFullPath(modHtmlPath));
|
||||
}
|
||||
|
||||
private void rendererChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (sender == glButton)
|
||||
Renderer = "Gl";
|
||||
else
|
||||
Renderer = "Cg";
|
||||
}
|
||||
|
||||
void formClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="installModDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -1,33 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
using System;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
public class Mod
|
||||
{
|
||||
public string Title { get; private set; }
|
||||
public string Version { get; private set; }
|
||||
public string Author { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public string Requires { get; private set; }
|
||||
public bool Standalone { get; private set; }
|
||||
|
||||
public Mod(string title, string version, string author, string description, string requires, bool standalone)
|
||||
{
|
||||
Title = title;
|
||||
Version = version;
|
||||
Author = author;
|
||||
Description = description;
|
||||
Requires = requires;
|
||||
Standalone = standalone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{2EA6C9C7-28A8-46C6-A42C-9275692EB285}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>OpenRA.Launcher</RootNamespace>
|
||||
<AssemblyName>OpenRA.Launcher</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Compile Include="Download.cs" />
|
||||
<Compile Include="JSBridge.cs" />
|
||||
<Compile Include="Launcher.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Launcher.Designer.cs">
|
||||
<DependentUpon>Launcher.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Mod.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
<Compile Include="UtilityProgram.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Launcher.resx">
|
||||
<DependentUpon>Launcher.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,26 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new Launcher());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
static class Util
|
||||
{
|
||||
[DllImport("user32")]
|
||||
public static extern UInt32 SendMessage
|
||||
(IntPtr hWnd, UInt32 msg, UInt32 wParam, UInt32 lParam);
|
||||
|
||||
internal const int BCM_FIRST = 0x1600; //Normal button
|
||||
|
||||
internal const int BCM_SETSHIELD = (BCM_FIRST + 0x000C); //Elevated button
|
||||
|
||||
static public void UacShield(Button b)
|
||||
{
|
||||
b.FlatStyle = FlatStyle.System;
|
||||
SendMessage(b.Handle, BCM_SETSHIELD, 0, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
static public bool IsError(ref string utilityResponseLine)
|
||||
{
|
||||
utilityResponseLine = utilityResponseLine.Trim('\r', '\n');
|
||||
if (utilityResponseLine.StartsWith("Error:"))
|
||||
{
|
||||
utilityResponseLine = utilityResponseLine.Remove(0, 7);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2010 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
* see LICENSE.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
class UtilityProgramResponse
|
||||
{
|
||||
public bool IsError
|
||||
{
|
||||
get { return response.StartsWith("Error:"); }
|
||||
}
|
||||
|
||||
string response;
|
||||
|
||||
public string Response
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsError)
|
||||
return response.Remove(0, 7);
|
||||
else
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public string[] ResponseLines
|
||||
{
|
||||
get
|
||||
{
|
||||
string s = response;
|
||||
if (IsError)
|
||||
s = response.Remove(0, 7);
|
||||
return s.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
|
||||
public UtilityProgramResponse(string response)
|
||||
{
|
||||
this.response = response.Trim('\r', '\n');
|
||||
}
|
||||
}
|
||||
|
||||
static class UtilityProgram
|
||||
{
|
||||
public static string GetPipeName()
|
||||
{
|
||||
return "OpenRA.Utility" + Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
static string BuildArgs(string command, string[] args)
|
||||
{
|
||||
StringBuilder arguments = new StringBuilder();
|
||||
arguments.Append("\"");
|
||||
arguments.Append(command + "=");
|
||||
arguments.Append(string.Join(",", args));
|
||||
arguments.Append("\"");
|
||||
return arguments.ToString();
|
||||
}
|
||||
|
||||
public static Process Call(string command, string pipename, EventHandler onExit, params string[] args)
|
||||
{
|
||||
Process p = new Process();
|
||||
p.StartInfo.FileName = "OpenRA.Utility.exe";
|
||||
p.StartInfo.Arguments = BuildArgs(command, args) + " --pipe=" + pipename;
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.CreateNoWindow = true;
|
||||
|
||||
if (onExit != null)
|
||||
{
|
||||
p.EnableRaisingEvents = true;
|
||||
p.Exited += onExit;
|
||||
}
|
||||
|
||||
p.Start();
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public static string CallSimpleResponse(string command, params string[] args)
|
||||
{
|
||||
string pipename = GetPipeName();
|
||||
Call(command, pipename, null, args);
|
||||
string responseString;
|
||||
NamedPipeClientStream pipe = new NamedPipeClientStream(".", pipename, PipeDirection.In);
|
||||
pipe.Connect();
|
||||
using (var response = new StreamReader(pipe))
|
||||
{
|
||||
responseString = response.ReadToEnd();
|
||||
}
|
||||
|
||||
return responseString;
|
||||
}
|
||||
|
||||
public static Process CallWithAdmin(string command, string pipename, params string[] args)
|
||||
{
|
||||
Process p = new Process();
|
||||
p.StartInfo.FileName = "OpenRA.Utility.exe";
|
||||
p.StartInfo.Arguments = BuildArgs(command, args) + " --pipe=" + pipename;
|
||||
p.StartInfo.Verb = "runas";
|
||||
|
||||
try
|
||||
{
|
||||
p.Start();
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
if (e.NativeErrorCode == 1223) //ERROR_CANCELLED
|
||||
return null;
|
||||
throw e;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rf packaging/linux/rootx64
|
||||
rm -rf packaging/linux/rootx86
|
||||
rm -rf packaging/linux/root
|
||||
rm -rf packaging/built
|
||||
rm packaging/linux/package32.log
|
||||
rm packaging/linux/package64.log
|
||||
rm packaging/linux/package.log
|
||||
rm packaging/linux/deb/package.log
|
||||
rm packaging/linux/rpm/package.log
|
||||
rm packaging/linux/pkgbuild/package.log
|
||||
|
||||
@@ -1,35 +1,26 @@
|
||||
#!/bin/bash
|
||||
# OpenRA packaging master script for linux packages
|
||||
|
||||
if [ $# -ne "4" ]; then
|
||||
echo "Usage: `basename $0` version files-dir outputdir arch"
|
||||
if [ $# -ne "3" ]; then
|
||||
echo "Usage: `basename $0` version files-dir outputdir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
BUILTDIR=$2
|
||||
PACKAGEDIR=$3
|
||||
ROOTDIR=root$4
|
||||
ROOTDIR=root
|
||||
|
||||
# Clean up
|
||||
rm -rf $ROOTDIR
|
||||
|
||||
# Game files
|
||||
mkdir -p $ROOTDIR/usr/bin/
|
||||
if [ $4 = "x64" ]; then
|
||||
cp -T $BUILTDIR/gtklaunch $ROOTDIR/usr/bin/openra
|
||||
else
|
||||
cp -T $BUILTDIR/gtklaunch32 $ROOTDIR/usr/bin/openra
|
||||
fi
|
||||
cp -T openra-bin root/usr/bin/openra
|
||||
|
||||
mkdir -p $ROOTDIR/usr/share/openra/
|
||||
cp -R $BUILTDIR/* "$ROOTDIR/usr/share/openra/" || exit 3
|
||||
|
||||
# Remove unneeded files
|
||||
rm $ROOTDIR/usr/share/openra/OpenRA.Launcher.exe
|
||||
rm $ROOTDIR/usr/share/openra/gtklaunch
|
||||
rm $ROOTDIR/usr/share/openra/gtklaunch32
|
||||
|
||||
# Desktop Icons
|
||||
mkdir -p $ROOTDIR/usr/share/applications/
|
||||
sed "s/{VERSION}/$VERSION/" openra.desktop > $ROOTDIR/usr/share/applications/openra.desktop
|
||||
@@ -47,28 +38,25 @@ cp -r hicolor $ROOTDIR/usr/share/icons/
|
||||
(
|
||||
echo "Building Debian package."
|
||||
cd deb
|
||||
./buildpackage.sh "$VERSION" ../$ROOTDIR "$PACKAGEDIR" $4 &> package.log
|
||||
./buildpackage.sh "$VERSION" ../$ROOTDIR "$PACKAGEDIR" &> package.log
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Debian package build failed, refer to $PWD/package.log."
|
||||
fi
|
||||
) &
|
||||
|
||||
if [ $4 = 'x86' ]
|
||||
then
|
||||
(
|
||||
echo "Building Arch-Linux package."
|
||||
cd pkgbuild
|
||||
sh buildpackage.sh "$VERSION" ../$ROOTDIR "$PACKAGEDIR" $4 &> package.log
|
||||
sh buildpackage.sh "$VERSION" ../$ROOTDIR "$PACKAGEDIR" &> package.log
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Arch-Linux package build failed, refer to $PWD/package.log."
|
||||
fi
|
||||
) &
|
||||
fi
|
||||
|
||||
(
|
||||
echo "Building RPM package."
|
||||
cd rpm
|
||||
sh buildpackage.sh "$VERSION" ../$ROOTDIR ~/rpmbuild "$PACKAGEDIR" $4 &> package.log
|
||||
sh buildpackage.sh "$VERSION" ../$ROOTDIR ~/rpmbuild "$PACKAGEDIR" &> package.log
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "RPM package build failed, refer to $PWD/package.log."
|
||||
fi
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
Package: openra
|
||||
Version: {VERSION}
|
||||
Architecture: {ARCH}
|
||||
Architecture: all
|
||||
Maintainer: Matthew Bowra-Dean <matthew@ijw.co.nz>
|
||||
Uploaders: Maikel van den Hout <ma1kelvdh@gmail.com>, Paul Chote <sleipnir@sleipnirstuff.com>
|
||||
Installed-Size: {SIZE}
|
||||
Depends: libopenal1, mono-runtime, libmono-winforms2.0-cil, libfreetype6, libsdl1.2debian, libgl1-mesa-glx, libgl1-mesa-dri, libmono-i18n2.0-cil, libgtk2.0-0, libwebkit-1.0-2
|
||||
Depends: libopenal1, mono-runtime, libmono-winforms2.0-cil, libfreetype6, libsdl1.2debian, libgl1-mesa-glx, libgl1-mesa-dri, libmono-i18n2.0-cil
|
||||
Section: games
|
||||
Priority: extra
|
||||
Homepage: http://www.open-ra.org/
|
||||
Description: A multiplayer reimplementation of the Command & Conquer: Red Alert game engine in .NET/Mono, OpenGL, OpenAL and SDL. Has extensive modding support and includes Command & Conquer as an official mod.
|
||||
Description: A multiplayer reimplementation of the Command & Conquer: Red Alert game engine in .NET/Mono.
|
||||
.
|
||||
Please note: OpenRA is currently at an alpha release stage. Releases may be buggy
|
||||
or unstable. If you have any problems, please report them to the IRC channel (#openra on
|
||||
|
||||
@@ -1,40 +1,34 @@
|
||||
#!/bin/bash
|
||||
# OpenRA packaging script for Debian based distributions
|
||||
E_BADARGS=85
|
||||
if [ $# -ne "4" ]
|
||||
if [ $# -ne "3" ]
|
||||
then
|
||||
echo "Usage: `basename $0` version root-dir outputdir arch"
|
||||
echo "Usage: `basename $0` version root-dir outputdir"
|
||||
exit $E_BADARGS
|
||||
fi
|
||||
VERSION=$1
|
||||
rootdir=`readlink -f $2`
|
||||
PACKAGE_SIZE=`du --apparent-size -c $rootdir/usr | grep "total" | awk '{print $1}'`
|
||||
if [ $4 = "x64" ]
|
||||
then
|
||||
ARCH=amd64
|
||||
else
|
||||
ARCH=i386
|
||||
fi
|
||||
|
||||
# Copy template files into a clean build directory (required)
|
||||
mkdir root$ARCH
|
||||
cp -R DEBIAN root$ARCH
|
||||
cp -R $rootdir/usr root$ARCH
|
||||
mkdir root
|
||||
cp -R DEBIAN root
|
||||
cp -R $rootdir/usr root
|
||||
|
||||
# Create the control and changelog files from templates
|
||||
sed "s/{VERSION}/$VERSION/" DEBIAN/control | sed "s/{SIZE}/$PACKAGE_SIZE/" | sed "s/{ARCH}/$ARCH/" > root$ARCH/DEBIAN/control
|
||||
sed "s/{VERSION}/$VERSION/" DEBIAN/changelog > root$ARCH/DEBIAN/changelog
|
||||
sed "s/{VERSION}/$VERSION/" DEBIAN/control | sed "s/{SIZE}/$PACKAGE_SIZE/" > root/DEBIAN/control
|
||||
sed "s/{VERSION}/$VERSION/" DEBIAN/changelog > root/DEBIAN/changelog
|
||||
|
||||
# Build it in the temp directory, but place the finished deb in our starting directory
|
||||
pushd root$ARCH
|
||||
pushd root
|
||||
|
||||
# Calculate md5sums and clean up the /usr/ part of them
|
||||
md5sum `find . -type f | grep -v '^[.]/DEBIAN/'` | sed 's/\.\/usr\//usr\//g' > DEBIAN/md5sums
|
||||
|
||||
# Start building, the file should appear in the output directory
|
||||
dpkg-deb -b . $3/openra-$VERSION-$ARCH.deb
|
||||
dpkg-deb -b . $3/openra-$VERSION.deb
|
||||
|
||||
# Clean up
|
||||
popd
|
||||
rm -rf root$ARCH
|
||||
rm -rf root
|
||||
|
||||
|
||||
3
packaging/linux/openra-bin
Normal file
3
packaging/linux/openra-bin
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd /usr/share/openra
|
||||
mono /usr/share/openra/OpenRA.Game.exe SupportDir=~/.openra "$@"
|
||||
@@ -3,13 +3,14 @@ pkgname=openra
|
||||
pkgver={VERSION}
|
||||
pkgrel=1
|
||||
pkgdesc="Open Source rebuild of the Red Alert game engine using Mono/OpenGL."
|
||||
arch=('{ARCH}')
|
||||
arch=('any')
|
||||
url="http://open-ra.org"
|
||||
license=('GPL3')
|
||||
groups=()
|
||||
depends=('mono' 'openal' 'mesa' 'freetype2' 'sdl' 'gtk2' 'libwebkit')
|
||||
depends=('mono' 'openal' 'mesa' 'freetype2' 'sdl')
|
||||
makedepends=('git' 'unzip' 'wget')
|
||||
optdepends=()
|
||||
optdepends=('gksu: Elevation for installing game packages on Gnome platforms'
|
||||
'kdesudo: Elevation for installing game packages on KDE platforms')
|
||||
provides=()
|
||||
conflicts=()
|
||||
replaces=()
|
||||
|
||||
@@ -1,32 +1,22 @@
|
||||
#!/bin/bash
|
||||
E_BADARGS=85
|
||||
if [ $# -ne "4" ]
|
||||
if [ $# -ne "3" ]
|
||||
then
|
||||
echo "Usage: `basename $0` version root-dir outputdir arch"
|
||||
echo "Usage: `basename $0` version root-dir outputdir"
|
||||
exit $E_BADARGS
|
||||
fi
|
||||
|
||||
if [ $4 = "x64" ]
|
||||
then
|
||||
ARCH=x86_64
|
||||
else
|
||||
ARCH=i686
|
||||
fi
|
||||
|
||||
# Replace any dashes in the version string with periods
|
||||
PKGVERSION=`echo $1 | sed "s/-/\\./g"`
|
||||
|
||||
cp PKGBUILD PKGBUILD$ARCH
|
||||
|
||||
sed -i "s/{VERSION}/$PKGVERSION/" PKGBUILD$ARCH
|
||||
sed -i "s/{VERSION}/$PKGVERSION/" PKGBUILD
|
||||
rootdir=`readlink -f $2`
|
||||
sed -i "s|{ROOT}|$rootdir|" PKGBUILD$ARCH
|
||||
sed -i "s/{ARCH}/$ARCH/" PKGBUILD$ARCH
|
||||
sed -i "s|{ROOT}|$rootdir|" PKGBUILD
|
||||
|
||||
makepkg --holdver -p PKGBUILD$ARCH
|
||||
makepkg --holdver -p PKGBUILD
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv openra-$PKGVERSION-1-$ARCH.pkg.tar.xz $3
|
||||
mv openra-$PKGVERSION-1-any.pkg.tar.xz $3
|
||||
|
||||
|
||||
@@ -1,42 +1,33 @@
|
||||
#!/bin/bash
|
||||
E_BADARGS=85
|
||||
if [ $# -ne "5" ]
|
||||
if [ $# -ne "4" ]
|
||||
then
|
||||
echo "Usage: `basename $0` version root-dir packaging-dir outputdir arch"
|
||||
echo "Usage: `basename $0` version root-dir packaging-dir outputdir"
|
||||
exit $E_BADARGS
|
||||
fi
|
||||
|
||||
if [ $5 = "x64" ]
|
||||
then
|
||||
ARCH=x86_64
|
||||
else
|
||||
ARCH=i386
|
||||
fi
|
||||
|
||||
# Replace any dashes in the version string with periods
|
||||
PKGVERSION=`echo $1 | sed "s/-/\\./g"`
|
||||
|
||||
cp openra.spec openra$ARCH.spec
|
||||
|
||||
sed -i "s/{VERSION_FIELD}/$PKGVERSION/" openra$ARCH.spec
|
||||
sed -i "s/{VERSION_FIELD}/$PKGVERSION/" openra.spec
|
||||
rootdir=`readlink -f $2`
|
||||
sed -i "s|{ROOT_DIR}|$rootdir|" openra$ARCH.spec
|
||||
sed -i "s|{ROOT_DIR}|$rootdir|" openra.spec
|
||||
|
||||
for x in `find $rootdir -type f`
|
||||
do
|
||||
y="${x#$rootdir}"
|
||||
sed -i "/%files/ a $y" openra$ARCH.spec
|
||||
sed -i "/%files/ a $y" openra.spec
|
||||
done
|
||||
|
||||
cp openra$ARCH.spec "$3/SPECS/"
|
||||
cp openra.spec "$3/SPECS/"
|
||||
|
||||
cd "$3"
|
||||
|
||||
rpmbuild --target $ARCH --buildroot /tmp/openra$ARCH/ -bb SPECS/openra$ARCH.spec
|
||||
rpmbuild --target noarch --buildroot /tmp/openra/ -bb SPECS/openra.spec
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd RPMS/$ARCH/
|
||||
mv openra-$PKGVERSION-1.$ARCH.rpm $4
|
||||
cd RPMS/noarch/
|
||||
mv openra-$PKGVERSION-1.noarch.rpm $4
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ License: GPL3
|
||||
URL: http://open-ra.org
|
||||
Group: Amusements/Games
|
||||
Packager: Matthew Bowra-Dean <matthew@ijw.co.nz>
|
||||
Requires: mono-core mono-devel SDL openal mesa-dri-drivers-experimental gtk2 webkitgtk
|
||||
Requires: mono-core mono-devel SDL openal mesa-dri-drivers-experimental
|
||||
Prefix: /usr
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
Buildroot: /tmp/openra
|
||||
|
||||
@@ -17,12 +17,9 @@ fi
|
||||
cp -rv ../../OpenRA.Launcher.Mac/build/Release/OpenRA.app OpenRA.app
|
||||
cp -rv $2/* "OpenRA.app/Contents/Resources/" || exit 3
|
||||
|
||||
# Icon isn't used, and editor doesn't work, OpenRA.Launcher is Windows specific, gtklaunch is Linux specific.
|
||||
# Icon isn't used, and editor doesn't work.
|
||||
rm OpenRA.app/Contents/Resources/OpenRA.ico
|
||||
rm OpenRA.app/Contents/Resources/OpenRA.Editor.exe
|
||||
rm OpenRA.app/Contents/Resources/OpenRA.Launcher.exe
|
||||
rm OpenRA.app/Contents/Resources/gtklaunch
|
||||
rm OpenRA.app/Contents/Resources/gtklaunch32
|
||||
|
||||
# Package app bundle into a zip and clean up
|
||||
zip OpenRA-$1 -r -9 OpenRA.app
|
||||
|
||||
@@ -26,9 +26,9 @@ find . -path "*.mdb" -delete
|
||||
# List of files that are packaged on all platforms
|
||||
# Note that the Tao dlls are shipped on all platforms except osx and that
|
||||
# they are now installed to the game directory instead of placed in the gac
|
||||
FILES="OpenRA.Launcher.exe OpenRA.Game.exe OpenRA.Editor.exe OpenRA.Utility.exe OpenRA.Renderer.Cg.dll \
|
||||
FILES="OpenRA.Game.exe OpenRA.Editor.exe OpenRA.Utility.exe OpenRA.Renderer.Cg.dll \
|
||||
OpenRA.Renderer.Gl.dll OpenRA.Renderer.Null.dll OpenRA.FileFormats.dll FreeSans.ttf FreeSansBold.ttf titles.ttf \
|
||||
cg glsl mods/ra mods/cnc COPYING HACKING INSTALL gtklaunch gtklaunch32"
|
||||
cg glsl mods/ra mods/cnc COPYING HACKING INSTALL
|
||||
|
||||
echo "Copying files..."
|
||||
for i in $FILES; do
|
||||
@@ -76,15 +76,10 @@ echo "Creating packages..."
|
||||
|
||||
(
|
||||
cd linux
|
||||
sh buildpackage.sh "$VERSION" "$BUILTDIR" "$OUTPUTDIR" "x86" &> package32.log
|
||||
sh buildpackage.sh "$VERSION" "$BUILTDIR" "$OUTPUTDIR" &> package.log
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "linux package build failed, refer to linux/package.log."
|
||||
fi
|
||||
sh buildpackage.sh "$VERSION" "$BUILTDIR" "$OUTPUTDIR" "x64" &> package64.log
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "linux package build failed, refer to linux/package.log."
|
||||
fi
|
||||
|
||||
) &
|
||||
wait
|
||||
|
||||
|
||||
@@ -25,11 +25,9 @@ esac
|
||||
|
||||
LINUXVERSION=`echo $VERSION | sed "s/-/\\./g"`
|
||||
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$VERSION-i386.deb "deblatest32" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$LINUXVERSION-1.i386.rpm "rpmlatest32" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$LINUXVERSION-1-i686.pkg.tar.xz "archlatest32" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$VERSION-amd64.deb "deblatest" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$LINUXVERSION-1.x86_64.rpm "rpmlatest" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$VERSION.deb "deblatest" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$LINUXVERSION-1.noarch.rpm "rpmlatest" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
./uploader.sh linux "$VERSION" $PKGDIR/openra-$LINUXVERSION-1-any.pkg.tar.xz "archlatest" "$FTPSERVER" "$FTPPATH" "$3" "$4"
|
||||
|
||||
if [ "$TYPE" = "release" ]; then
|
||||
wput --basename=../ -u ../VERSION ftp://$3:$4@$FTPSERVER/$FTPPATHBASE/master/
|
||||
|
||||
@@ -75,7 +75,6 @@ SectionEnd
|
||||
|
||||
Section "Client" Client
|
||||
SetOutPath "$INSTDIR"
|
||||
File "${SRCDIR}\OpenRA.Launcher.exe"
|
||||
File "${SRCDIR}\OpenRA.Game.exe"
|
||||
File "${SRCDIR}\OpenRA.Utility.exe"
|
||||
File "${SRCDIR}\OpenRA.FileFormats.dll"
|
||||
@@ -92,8 +91,6 @@ Section "Client" Client
|
||||
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\OpenRA Launcher.lnk" $OUTDIR\OpenRA.Launcher.exe "" \
|
||||
"$OUTDIR\OpenRA.ico" "" "" "" ""
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\OpenRA - Red Alert.lnk" $OUTDIR\OpenRA.Game.exe "Game.Mods=ra" \
|
||||
"$OUTDIR\OpenRA.ico" "" "" "" ""
|
||||
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\OpenRA - Command & Conquer.lnk" $OUTDIR\OpenRA.Game.exe "Game.Mods=cnc" \
|
||||
|
||||
Reference in New Issue
Block a user