Implemented game launching API for the frontlib.
authorMedo <smaxein@googlemail.com>
Sat, 09 Jun 2012 03:28:38 +0200 (2012-06-09)
changeset 7179 f84805e6df03
parent 7177 bf6cf4dd847a
child 7182 076aba32abd3
Implemented game launching API for the frontlib. It is still buggy though, and not all game settings can be conveniently created/modified yet.
project_files/frontlib/buffer.c
project_files/frontlib/buffer.h
project_files/frontlib/frontlib.c
project_files/frontlib/hwconsts.h
project_files/frontlib/ini/inihelper.c
project_files/frontlib/ini/inihelper.h
project_files/frontlib/ipc.c
project_files/frontlib/ipc.h
project_files/frontlib/ipc/demo.c
project_files/frontlib/ipc/demo.h
project_files/frontlib/ipc/gameconn.c
project_files/frontlib/ipc/gameconn.h
project_files/frontlib/ipc/ipcconn.c
project_files/frontlib/ipc/ipcconn.h
project_files/frontlib/ipc/ipcprotocol.c
project_files/frontlib/ipc/ipcprotocol.h
project_files/frontlib/ipc/mapconn.c
project_files/frontlib/ipc/mapconn.h
project_files/frontlib/logging.c
project_files/frontlib/logging.h
project_files/frontlib/model/cfg.c
project_files/frontlib/model/gamesetup.c
project_files/frontlib/model/gamesetup.h
project_files/frontlib/model/map.c
project_files/frontlib/model/team.c
project_files/frontlib/model/team.h
project_files/frontlib/model/weapon.c
project_files/frontlib/socket.c
project_files/frontlib/util.c
project_files/frontlib/util.h
project_files/frontlib/util/buffer.c
project_files/frontlib/util/buffer.h
project_files/frontlib/util/inihelper.c
project_files/frontlib/util/inihelper.h
project_files/frontlib/util/logging.c
project_files/frontlib/util/logging.h
project_files/frontlib/util/util.c
project_files/frontlib/util/util.h
--- a/project_files/frontlib/buffer.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#include "buffer.h"
-#include "logging.h"
-
-#include <stdlib.h>
-#include <limits.h>
-#include <string.h>
-
-typedef struct _flib_vector {
-	void *data;
-	size_t size;
-	size_t capacity;
-} _flib_vector;
-
-flib_vector flib_vector_create() {
-	flib_vector result = malloc(sizeof(_flib_vector));
-	if(result == NULL) {
-		return NULL;
-	}
-	result->data = malloc(16);
-	if(result->data == NULL) {
-		free(result);
-		return NULL;
-	}
-	result->size = 0;
-	result->capacity = 16;
-	return result;
-}
-
-void flib_vector_destroy(flib_vector *vec) {
-	if(vec && *vec) {
-		free((*vec)->data);
-		free(*vec);
-		*vec = NULL;
-	}
-}
-
-static void try_realloc(flib_vector vec, size_t newCapacity) {
-	void *newData = realloc(vec->data, newCapacity);
-	if(newData) {
-		vec->data = newData;
-		vec->capacity = newCapacity;
-	}
-}
-
-static size_t getFreeCapacity(flib_vector vec) {
-	return vec->capacity - vec->size;
-}
-
-int flib_vector_append(flib_vector vec, const void *data, size_t len) {
-	if(getFreeCapacity(vec) < len) {
-		// Resize exponentially for constant amortized time,
-		// But at least by as much as we need of course,
-		// and be extra careful with integer overflows...
-		size_t extraCapacity = (vec->capacity)/2;
-
-		size_t minExtraCapacity = len - getFreeCapacity(vec);
-		if(extraCapacity < minExtraCapacity) {
-			extraCapacity = minExtraCapacity;
-		}
-
-		if(extraCapacity <= SIZE_MAX - vec->capacity) {
-			try_realloc(vec, vec->capacity+extraCapacity);
-		}
-
-		// Check if we were able to resize.
-		// If not, try to allocate at least what we need...
-		if(getFreeCapacity(vec) < len) {
-			try_realloc(vec, vec->capacity+minExtraCapacity);
-
-			// Still not working? Then we fail.
-			if(getFreeCapacity(vec) < len) {
-				return 0;
-			}
-		}
-	}
-
-	memmove(vec->data + vec->size, data, len);
-	vec->size += len;
-	return len;
-}
-
-flib_buffer flib_vector_as_buffer(flib_vector vec) {
-	flib_buffer result = {vec->data, vec->size};
-	return result;
-}
-
-flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) {
-	flib_constbuffer result = {vec->data, vec->size};
-	return result;
-}
--- a/project_files/frontlib/buffer.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#ifndef BUFFER_H_
-#define BUFFER_H_
-
-#include <stdint.h>
-#include <stddef.h>
-
-/**
- * A simple struct to hold both the pointer to an array and its size,
- * for e.g. conveniently returning it from a function.
- *
- * Convention: Size is zero iff data is a NULL pointer.
- */
-typedef struct {
-	void *data;
-	size_t size;
-} flib_buffer;
-
-/**
- * Just like flib_buffer, but the contents are not supposed to be modified.
- */
-typedef struct {
-	const void *data;
-	size_t size;
-} flib_constbuffer;
-
-/**
- * Simple variable-capacity data structure (opaque type).
- */
-struct _flib_vector;
-typedef struct _flib_vector *flib_vector;
-
-/**
- * Create a new vector. Needs to be destroyed again later with flib_vector_destroy.
- * May return NULL if memory runs out.
- */
-flib_vector flib_vector_create();
-
-/**
- * Free the memory of this vector and set it to NULL.
- */
-void flib_vector_destroy(flib_vector *vec);
-
-/**
- * Append the provided data to the end of the vector, enlarging it as required.
- * Returns the ammount of data appended, which is either len (success) or 0 (out of memory).
- * The vector remains unchanged if an out of memory situation occurs.
- */
-int flib_vector_append(flib_vector vec, const void *data, size_t len);
-
-/**
- * Return a buffer or constbuffer pointing to the current contents of the vector.
- * These will become invalid if the vector size or capacity is changed.
- */
-flib_buffer flib_vector_as_buffer(flib_vector vec);
-flib_constbuffer flib_vector_as_constbuffer(flib_vector vec);
-
-
-#endif
--- a/project_files/frontlib/frontlib.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/frontlib.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,8 +1,8 @@
 #include "frontlib.h"
-#include "logging.h"
+#include "util/logging.h"
 #include "model/map.h"
 #include "ipc/mapconn.h"
-#include "ipc.h"
+#include "ipc/gameconn.h"
 
 #include <SDL.h>
 #include <SDL_net.h>
@@ -41,97 +41,72 @@
 	}
 }
 
-static void onConfigQuery(void *context) {
-	flib_log_i("Sending config...");
-	flib_ipc ipc = (flib_ipc)context;
-	flib_ipc_send_messagestr(ipc, "TL");
-	flib_ipc_send_messagestr(ipc, "eseed loremipsum");
-	flib_ipc_send_messagestr(ipc, "e$mapgen 0");
-	flib_ipc_send_messagestr(ipc, "e$template_filter 0");
-	flib_ipc_send_messagestr(ipc, "etheme Jungle");
-	flib_ipc_send_messagestr(ipc, "eaddteam 11111111111111111111111111111111 255 Medo42");
-}
-
-static void onDisconnect(void *context) {
-	flib_log_i("Connection closed.");
-	flib_ipc_destroy((flib_ipc*)context);
-}
-
-static void onGameEnd(void *context, int gameEndType) {
-	switch(gameEndType) {
-	case GAME_END_FINISHED:
-		flib_log_i("Game finished.");
-		flib_constbuffer demobuf = flib_ipc_getdemo(context);
-		flib_log_i("Writing demo (%u bytes)...", (unsigned)demobuf.size);
-		FILE *file = fopen("testdemo.dem", "wb");
-		fwrite(demobuf.data, 1, demobuf.size, file);
-		fclose(file);
-		file = NULL;
-		break;
-	case GAME_END_HALTED:
-		flib_log_i("Game halted.");
-		break;
-	case GAME_END_INTERRUPTED:
-		flib_log_i("Game iterrupted.");
-		break;
-	}
-}
-
-static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {
-	printf("Drawing map for %i brave little hogs...", numHedgehogs);
-	int pixelnum = 0;
-	for(int y=0; y<MAPIMAGE_HEIGHT; y++) {
-		for(int x=0; x<MAPIMAGE_WIDTH; x++) {
-			if(bitmap[pixelnum>>3] & (1<<(7-(pixelnum&7)))) {
-				printf("#");
-			} else {
-				printf(" ");
-			}
-			pixelnum++;
-		}
-		printf("\n");
-	}
-
-	flib_mapconn **connptr = context;
-	flib_mapconn_destroy(*connptr);
+static void onDisconnect(void *context, int reason) {
+	flib_log_i("Connection closed. Reason: %i", reason);
+	flib_gameconn **connptr = context;
+	flib_gameconn_destroy(*connptr);
 	*connptr = NULL;
 }
 
-static void handleMapFailure(void *context, const char *errormessage) {
-	flib_log_e("Map rendering failed: %s", errormessage);
-
-	flib_mapconn **connptr = context;
-	flib_mapconn_destroy(*connptr);
-	*connptr = NULL;
+static void onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {
+	flib_log_i("Writing %s (%i bytes)...", isSavegame ? "savegame" : "demo", size);
+	FILE *file = fopen(isSavegame ? "testsave.42.hws" : "testdemo.42.hwd", "wb");
+	fwrite(record, 1, size, file);
+	fclose(file);
 }
 
 int main(int argc, char *argv[]) {
-/*	flib_init(0);
-
-	flib_cfg_meta *meta = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini");
-	flib_cfg *cfg = flib_cfg_create(meta, "DefaultScheme");
-	flib_cfg_to_ini(meta, "defaulttest.ini", cfg);
-
-	flib_cfg_meta_destroy(meta);
-
-	flib_quit();
-	return 0;*/
+	flib_init(0);
 
-	flib_init(0);
-	flib_map *mapconf = flib_map_create_regular("Jungle", TEMPLATEFILTER_CAVERN);
-	assert(mapconf);
-
-	flib_mapconn *mapconn = flib_mapconn_create("foobart", mapconf);
-	assert(mapconn);
+	flib_cfg_meta *metaconf = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini");
+	assert(metaconf);
+	flib_gamesetup setup;
+	setup.gamescheme = flib_cfg_from_ini(metaconf, "scheme_shoppa.ini");
+	setup.map = flib_map_create_maze("Jungle", MAZE_SIZE_MEDIUM_TUNNELS);
+	setup.seed = "apsfooasdgnds";
+	setup.teamcount = 2;
+	setup.teams = calloc(2, sizeof(flib_team));
+	setup.teams[0].color = 0xffff0000;
+	setup.teams[0].flag = "australia";
+	setup.teams[0].fort = "Plane";
+	setup.teams[0].grave = "Bone";
+	setup.teams[0].hogsInGame = 2;
+	setup.teams[0].name = "Team Awesome";
+	setup.teams[0].voicepack = "British";
+	setup.teams[0].weaponset = flib_weaponset_create("Defaultweaps");
+	setup.teams[0].hogs[0].difficulty = 2;
+	setup.teams[0].hogs[0].hat = "NoHat";
+	setup.teams[0].hogs[0].initialHealth = 100;
+	setup.teams[0].hogs[0].name = "Harry 120";
+	setup.teams[0].hogs[1].difficulty = 2;
+	setup.teams[0].hogs[1].hat = "chef";
+	setup.teams[0].hogs[1].initialHealth = 100;
+	setup.teams[0].hogs[1].name = "Chefkoch";
+	setup.teams[1].color = 0xff0000ff;
+	setup.teams[1].flag = "germany";
+	setup.teams[1].fort = "Cake";
+	setup.teams[1].grave = "Cherry";
+	setup.teams[1].hogsInGame = 2;
+	setup.teams[1].name = "The Krauts";
+	setup.teams[1].voicepack = "Pirate";
+	setup.teams[1].weaponset = flib_weaponset_create("Defaultweaps");
+	setup.teams[1].hogs[0].difficulty = 0;
+	setup.teams[1].hogs[0].hat = "quotecap";
+	setup.teams[1].hogs[0].initialHealth = 100;
+	setup.teams[1].hogs[0].name = "Quote";
+	setup.teams[1].hogs[1].difficulty = 0;
+	setup.teams[1].hogs[1].hat = "chef";
+	setup.teams[1].hogs[1].initialHealth = 100;
+	setup.teams[1].hogs[1].name = "Chefkoch2";
 
-	flib_map_destroy(mapconf);
-	mapconf = NULL;
+	flib_gameconn *gameconn = flib_gameconn_create("Medo42", metaconf, &setup, false);
+	assert(gameconn);
 
-	flib_mapconn_onFailure(mapconn, &handleMapFailure, &mapconn);
-	flib_mapconn_onSuccess(mapconn, &handleMapSuccess, &mapconn);
+	flib_gameconn_onDisconnect(gameconn, &onDisconnect, &gameconn);
+	flib_gameconn_onGameRecorded(gameconn, &onGameRecorded, &gameconn);
 
-	while(mapconn) {
-		flib_mapconn_tick(mapconn);
+	while(gameconn) {
+		flib_gameconn_tick(gameconn);
 	}
 	flib_log_i("Shutting down...");
 	flib_quit();
--- a/project_files/frontlib/hwconsts.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/hwconsts.h	Sat Jun 09 03:28:38 2012 +0200
@@ -9,6 +9,8 @@
 #define HEDGEHOGS_PER_TEAM 8
 #define NETGAME_DEFAULT_PORT 46631
 
+#define GAMEMOD_PERHOGAMMO_MASKBIT 22
+#define GAMEMOD_SHAREDAMMO_MASKBIT 16
 
 #define WEAPONS_COUNT 55
 #define AMMOLINE_DEFAULT_QT     "9391929422199121032235111001201000000211110101011111011"
--- a/project_files/frontlib/ini/inihelper.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-#include "inihelper.h"
-#include "../logging.h"
-#include "../util.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <limits.h>
-#include <errno.h>
-#include <stdarg.h>
-
-static bool keychar_needs_urlencoding(char c) {
-	return !isalnum(c);
-}
-
-char *inihelper_urlencode(const char *inbuf) {
-	if(!inbuf) {
-		return NULL;
-	}
-	size_t insize = strlen(inbuf);
-	if(insize > SIZE_MAX/4) {
-		return NULL;
-	}
-
-	char *outbuf = malloc(insize*3+1);
-	if(!outbuf) {
-		return NULL;
-	}
-
-    size_t inpos = 0, outpos = 0;
-    while(inbuf[inpos]) {
-        if(!keychar_needs_urlencoding(inbuf[inpos])) {
-        	outbuf[outpos++] = inbuf[inpos++];
-        } else {
-            if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
-            	free(outbuf);
-            	return NULL;
-            }
-            inpos++;
-            outpos += 3;
-        }
-    }
-    outbuf[outpos] = 0;
-    return outbuf;
-}
-
-char *inihelper_urldecode(const char *inbuf) {
-	char *outbuf = malloc(strlen(inbuf)+1);
-	if(!outbuf) {
-		return NULL;
-	}
-
-    size_t inpos = 0, outpos = 0;
-    while(inbuf[inpos]) {
-        if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) {
-            char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0};
-            outbuf[outpos++] = strtol(temp, NULL, 16);
-            inpos += 3;
-        } else {
-        	outbuf[outpos++] = inbuf[inpos++];
-        }
-    }
-    outbuf[outpos] = 0;
-    return outbuf;
-}
-
-char *inihelper_createDictKey(const char *sectionName, const char *keyName) {
-	if(!sectionName || !keyName) {
-		return NULL;
-	}
-	return flib_asprintf("%s:%s", sectionName, keyName);
-}
-
-char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	if(!inifile || !sectionName || !keyName) {
-		*error = true;
-		return NULL;
-	}
-	char *extendedkey = inihelper_createDictKey(sectionName, keyName);
-	if(!extendedkey) {
-		*error = true;
-		return NULL;
-	}
-	char *result = iniparser_getstring(inifile, extendedkey, NULL);
-	free(extendedkey);
-	if(!result) {
-		flib_log_i("Missing ini setting: %s/%s", sectionName, keyName);
-		*error = true;
-	}
-	return result;
-}
-
-char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
-}
-
-int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
-	if(!value) {
-		return 0;
-	} else {
-		errno = 0;
-		long val = strtol(value, NULL, 10);
-		if(errno!=0) {
-			*error = true;
-			return 0;
-		}
-		if(val<INT_MIN || val>INT_MAX) {
-			*error = true;
-			return 0;
-		}
-		return (int)val;
-	}
-}
-
-bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
-	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
-	if(!value) {
-		return false;
-	} else {
-		bool trueval = strchr("1tTyY", value[0]);
-		bool falseval = strchr("0fFnN", value[0]);
-		if(!trueval && !falseval) {
-			*error = true;
-			return false;
-		} else {
-			return trueval;
-		}
-	}
-}
-
-int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value) {
-	int result = -1;
-	if(!dict || !sectionName || !keyName || !value) {
-		flib_log_e("inihelper_setstr called with bad parameters");
-	} else {
-		char *extendedkey = inihelper_createDictKey(sectionName, keyName);
-		if(extendedkey) {
-			result = iniparser_set(dict, extendedkey, value);
-			free(extendedkey);
-		}
-	}
-	return result;
-}
-
-int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value) {
-	int result = -1;
-	if(!dict || !sectionName || !keyName) {
-		flib_log_e("inihelper_setint called with bad parameters");
-	} else {
-		char *strvalue = flib_asprintf("%i", value);
-		if(strvalue) {
-			result = inihelper_setstr(dict, sectionName, keyName, strvalue);
-			free(strvalue);
-		}
-	}
-	return result;
-}
-
-int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value) {
-	int result = -1;
-	if(!dict || !sectionName || !keyName) {
-		flib_log_e("inihelper_setint called with bad parameters");
-	} else {
-		result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false");
-	}
-	return result;
-}
--- a/project_files/frontlib/ini/inihelper.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * Some helper functions for working with the iniparser functions - in particular,
- * for interoperability with the ini format used by the QtSettings class.
- */
-
-#ifndef INIHELPER_H_
-#define INIHELPER_H_
-
-#include "../iniparser/iniparser.h"
-#include <stdbool.h>
-
-/**
- * Returned buffer must be free()d
- */
-char *inihelper_urlencode(const char *inbuf);
-
-/**
- * Returned buffer must be free()d
- */
-char *inihelper_urldecode(const char *inbuf);
-
-/**
- * Create a key in the format "sectionName:keyName"
- * Returned buffer must be free()d
- */
-char *inihelper_createDictKey(const char *sectionName, const char *keyName);
-
-/**
- * Returns an internal buffer, don't modify or free
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-/**
- * Returned buffer must be free()d
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-/**
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-/**
- * Sets error to true if something goes wrong, leaves it unchanged otherwise.
- */
-bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
-
-int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value);
-int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value);
-int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value);
-#endif /* INIHELPER_H_ */
--- a/project_files/frontlib/ipc.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,284 +0,0 @@
-#include "ipc.h"
-#include "ipc/ipcconn.h"
-#include "logging.h"
-
-#include <stdbool.h>
-#include <stdlib.h>
-
-typedef struct _flib_ipc {
-	flib_ipcconn connection;
-	IpcConnState oldConnState;
-
-	void (*onConnectCb)(void*);
-	void *onConnectCtx;
-
-	void (*onDisconnectCb)(void*);
-	void *onDisconnectCtx;
-
-	void (*onConfigQueryCb)(void*);
-	void *onConfigQueryCtx;
-
-	void (*onEngineErrorCb)(void*, const uint8_t*);
-	void *onEngineErrorCtx;
-
-	void (*onGameEndCb)(void*, int);
-	void *onGameEndCtx;
-
-	void (*onChatCb)(void*, const uint8_t*, int);
-	void *onChatCtx;
-
-	void (*onEngineMessageCb)(void*, const uint8_t*, int);
-	void *onEngineMessageCtx;
-
-	bool running;
-	bool destroyRequested;
-} _flib_ipc;
-
-static void emptyCallback(void* ptr) {}
-static void emptyCallback_int(void* ptr, int i) {}
-static void emptyCallback_str(void* ptr, const uint8_t* str) {}
-static void emptyCallback_str_int(void* ptr, const uint8_t* str, int i) {}
-
-static void clearCallbacks(flib_ipc ipc) {
-	ipc->onConnectCb = &emptyCallback;
-	ipc->onDisconnectCb = &emptyCallback;
-	ipc->onConfigQueryCb = &emptyCallback;
-	ipc->onEngineErrorCb = &emptyCallback_str;
-	ipc->onGameEndCb = &emptyCallback_int;
-	ipc->onChatCb = &emptyCallback_str_int;
-	ipc->onEngineMessageCb = &emptyCallback_str_int;
-}
-
-flib_ipc flib_ipc_create(bool recordDemo, const char *localPlayerName) {
-	flib_ipc result = malloc(sizeof(_flib_ipc));
-	flib_ipcconn connection = flib_ipcconn_create(recordDemo, localPlayerName);
-
-	if(!result || !connection) {
-		free(result);
-		flib_ipcconn_destroy(&connection);
-		return NULL;
-	}
-
-	result->connection = connection;
-	result->oldConnState = IPC_LISTENING;
-	result->running = false;
-	result->destroyRequested = false;
-
-	clearCallbacks(result);
-	return result;
-}
-
-void flib_ipc_destroy(flib_ipc *ipcptr) {
-	if(!ipcptr || !*ipcptr) {
-		return;
-	}
-	flib_ipc ipc = *ipcptr;
-	if(ipc->running) {
-		// The function was called from a callback of this ipc connection,
-		// so the tick function is still running and we delay the actual
-		// destruction. We ensure no further callbacks will be sent to prevent
-		// surprises.
-		clearCallbacks(ipc);
-		ipc->destroyRequested = true;
-	} else {
-		flib_ipcconn_destroy(&ipc->connection);
-		free(ipc);
-	}
-	*ipcptr = NULL;
-}
-
-void flib_ipc_onConnect(flib_ipc ipc, void (*callback)(void* context), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onConnect with ipc==null");
-		return;
-	}
-	ipc->onConnectCb = callback ? callback : &emptyCallback;
-	ipc->onConnectCtx = context;
-}
-
-void flib_ipc_onDisconnect(flib_ipc ipc, void (*callback)(void* context), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onDisconnect with ipc==null");
-		return;
-	}
-	ipc->onDisconnectCb = callback ? callback : &emptyCallback;
-	ipc->onDisconnectCtx = context;
-}
-
-void flib_ipc_onConfigQuery(flib_ipc ipc, void (*callback)(void* context), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onConfigQuery with ipc==null");
-		return;
-	}
-	ipc->onConfigQueryCb = callback ? callback : &emptyCallback;
-	ipc->onConfigQueryCtx = context;
-}
-
-void flib_ipc_onEngineError(flib_ipc ipc, void (*callback)(void* context, const uint8_t *error), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onEngineError with ipc==null");
-		return;
-	}
-	ipc->onEngineErrorCb = callback ? callback : &emptyCallback_str;
-	ipc->onEngineErrorCtx = context;
-}
-
-void flib_ipc_onGameEnd(flib_ipc ipc, void (*callback)(void* context, int gameEndType), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onGameEnd with ipc==null");
-		return;
-	}
-	ipc->onGameEndCb = callback ? callback : &emptyCallback_int;
-	ipc->onGameEndCtx = context;
-}
-
-void flib_ipc_onChat(flib_ipc ipc, void (*callback)(void* context, const uint8_t *messagestr, int teamchat), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onChat with ipc==null");
-		return;
-	}
-	ipc->onChatCb = callback ? callback : &emptyCallback_str_int;
-	ipc->onChatCtx = context;
-}
-
-void flib_ipc_onEngineMessage(flib_ipc ipc, void (*callback)(void* context, const uint8_t *message, int len), void* context) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_onEngineMessage with ipc==null");
-		return;
-	}
-	ipc->onEngineMessageCb = callback ? callback : &emptyCallback_str_int;
-	ipc->onEngineMessageCtx = context;
-}
-
-static void flib_ipc_wrappedtick(flib_ipc ipc) {
-	if(ipc->oldConnState == IPC_NOT_CONNECTED) {
-		return;
-	}
-
-	if(ipc->oldConnState == IPC_LISTENING) {
-		flib_ipcconn_accept(ipc->connection);
-		if(flib_ipcconn_state(ipc->connection) == IPC_CONNECTED) {
-			ipc->oldConnState = IPC_CONNECTED;
-			ipc->onConnectCb(ipc->onConnectCtx);
-		}
-	}
-
-	if(ipc->oldConnState == IPC_CONNECTED) {
-		uint8_t msgbuffer[257];
-		int len;
-		while(!ipc->destroyRequested && (len = flib_ipcconn_recv_message(ipc->connection, msgbuffer))>=0) {
-			if(len<2) {
-				flib_log_w("Received short message from IPC (<2 bytes)");
-				continue;
-			}
-			msgbuffer[len] = 0;
-			flib_log_i("[IPC in] %s", msgbuffer+1);
-			switch(msgbuffer[1]) {
-			case '?':
-				flib_ipcconn_send_messagestr(ipc->connection, "!");
-				break;
-			case 'C':
-				ipc->onConfigQueryCb(ipc->onConfigQueryCtx);
-				break;
-			case 'E':
-				if(len>=3) {
-					msgbuffer[len-2] = 0;
-					ipc->onEngineErrorCb(ipc->onEngineErrorCtx, msgbuffer+2);
-				}
-				break;
-			case 'i':
-				// TODO
-				break;
-			case 'Q':
-				ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_INTERRUPTED);
-				break;
-			case 'q':
-				ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_FINISHED);
-				break;
-			case 'H':
-				ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_HALTED);
-				break;
-			case 's':
-				if(len>=3) {
-					msgbuffer[len-2] = 0;
-					ipc->onChatCb(ipc->onChatCtx, msgbuffer+2, 0);
-				}
-				break;
-			case 'b':
-				if(len>=3) {
-					msgbuffer[len-2] = 0;
-					ipc->onChatCb(ipc->onChatCtx, msgbuffer+2, 1);
-				}
-				break;
-			default:
-				ipc->onEngineMessageCb(ipc->onEngineMessageCtx, msgbuffer, len);
-				break;
-			}
-		}
-	}
-
-	if(flib_ipcconn_state(ipc->connection) == IPC_NOT_CONNECTED) {
-		ipc->oldConnState = IPC_NOT_CONNECTED;
-		ipc->onDisconnectCb(ipc->onDisconnectCtx);
-	}
-}
-
-void flib_ipc_tick(flib_ipc ipc) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_tick with ipc==null");
-		return;
-	}
-	if(ipc->running) {
-		flib_log_w("Call to flib_ipc_tick from a callback");
-		return;
-	}
-
-	ipc->running = true;
-	flib_ipc_wrappedtick(ipc);
-	ipc->running = false;
-
-	if(ipc->destroyRequested) {
-		flib_ipc_destroy(&ipc);
-	}
-}
-
-int flib_ipc_send_raw(flib_ipc ipc, void *data, size_t len) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_raw with ipc==null");
-		return -1;
-	}
-	return flib_ipcconn_send_raw(ipc->connection, data, len);
-}
-
-int flib_ipc_send_message(flib_ipc ipc, void *data, size_t len) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_message with ipc==null");
-		return -1;
-	}
-	return flib_ipcconn_send_message(ipc->connection, data, len);
-}
-
-int flib_ipc_send_messagestr(flib_ipc ipc, char *data) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_messagestr with ipc==null");
-		return -1;
-	}
-	return flib_ipcconn_send_messagestr(ipc->connection, data);
-}
-
-uint16_t flib_ipc_port(flib_ipc ipc) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_messagestr with ipc==null");
-		return 0;
-	}
-	return flib_ipcconn_port(ipc->connection);
-}
-
-flib_constbuffer flib_ipc_getdemo(flib_ipc ipc) {
-	if(!ipc) {
-		flib_log_w("Call to flib_ipc_send_messagestr with ipc==null");
-		flib_constbuffer result = {NULL, 0};
-		return result;
-	}
-	return flib_ipcconn_getrecord(ipc->connection, false);
-}
--- a/project_files/frontlib/ipc.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#ifndef IPC_H_
-#define IPC_H_
-
-#include "buffer.h"
-#include "model/weapon.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-struct _flib_ipc;
-typedef struct _flib_ipc *flib_ipc;
-
-typedef enum {
-	GAME_END_FINISHED,
-	GAME_END_INTERRUPTED,
-	GAME_END_HALTED
-} flib_GameEndType;
-
-flib_ipc flib_ipc_create(bool recordDemo, const char *localPlayerName);
-void flib_ipc_destroy(flib_ipc *ipcptr);
-
-void flib_ipc_onConnect(flib_ipc ipc, void (*callback)(void* context), void* context);
-void flib_ipc_onDisconnect(flib_ipc ipc, void (*callback)(void* context), void* context);
-void flib_ipc_onConfigQuery(flib_ipc ipc, void (*callback)(void* context), void* context);
-void flib_ipc_onEngineError(flib_ipc ipc, void (*callback)(void* context, const uint8_t *error), void* context);
-void flib_ipc_onGameEnd(flib_ipc ipc, void (*callback)(void* context, int gameEndType), void* context);
-void flib_ipc_onChat(flib_ipc ipc, void (*callback)(void* context, const uint8_t *messagestr, int teamchat), void* context);
-void flib_ipc_onEngineMessage(flib_ipc ipc, void (*callback)(void* context, const uint8_t *message, int len), void* context);
-
-int flib_ipc_send_raw(flib_ipc ipc, void *data, size_t len);
-int flib_ipc_send_message(flib_ipc ipc, void *data, size_t len);
-int flib_ipc_send_messagestr(flib_ipc ipc, char *data);
-
-// Configuration
-int flib_ipc_send_seed(flib_ipc ipc, const char *seed);
-int flib_ipc_send_script(flib_ipc ipc, const char *scriptpath);
-int flib_ipc_send_map_regular(flib_ipc ipc, const char *theme, int templateFilter);
-int flib_ipc_send_map_maze(flib_ipc ipc, const char *theme, int mazeType);
-int flib_ipc_send_map_drawn(flib_ipc ipc, const char *theme, void *drawnMapData, size_t drawnMapDataLen);
-int flib_ipc_send_map_named(flib_ipc ipc, const char *mappath);
-int flib_ipc_send_gamemods(flib_ipc ipc, uint32_t modflags);
-int flib_ipc_send_gamesetting(flib_ipc ipc, const char *settingname, int modflags);
-int flib_ipc_send_weaponset(flib_ipc ipc, flib_weaponset *set);
-
-int flib_ipc_send_conf_end(flib_ipc ipc);
-
-
-uint16_t flib_ipc_port(flib_ipc ipc);
-flib_constbuffer flib_ipc_getdemo(flib_ipc ipc);
-
-void flib_ipc_tick(flib_ipc ipc);
-
-#endif /* IPC_H_ */
--- a/project_files/frontlib/ipc/demo.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/demo.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,5 +1,5 @@
 #include "demo.h"
-#include "../logging.h"
+#include "../util/logging.h"
 
 #include <stdbool.h>
 #include <stdio.h>
--- a/project_files/frontlib/ipc/demo.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/demo.h	Sat Jun 09 03:28:38 2012 +0200
@@ -5,7 +5,7 @@
 #ifndef DEMO_H_
 #define DEMO_H_
 
-#include "../buffer.h"
+#include "../util/buffer.h"
 
 /**
  * Record a message sent from the engine to the frontend.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,348 @@
+#include "gameconn.h"
+#include "ipcconn.h"
+#include "ipcprotocol.h"
+#include "../util/logging.h"
+#include "../hwconsts.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef enum {
+	AWAIT_CONNECTION,
+	CONNECTED,
+	FINISHED
+} gameconn_state;
+
+struct _flib_gameconn {
+	flib_ipcconn connection;
+	flib_vector configBuffer;
+
+	gameconn_state state;
+	bool netgame;
+
+	void (*onConnectCb)(void* context);
+	void *onConnectCtx;
+
+	void (*onDisconnectCb)(void* context, int reason);
+	void *onDisconnectCtx;
+
+	void (*onErrorMessageCb)(void* context, const char *msg);
+	void *onErrorMessageCtx;
+
+	void (*onChatCb)(void* context, const char *msg, bool teamchat);
+	void *onChatCtx;
+
+	void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame);
+	void *onGameRecordedCtx;
+
+	void (*onNetMessageCb)(void *context, const uint8_t *em, int size);
+	void *onNetMessageCtx;
+
+	bool running;
+	bool destroyRequested;
+};
+
+static void defaultCallback_onConnect(void* context) {}
+static void defaultCallback_onDisconnect(void* context, int reason) {}
+static void defaultCallback_onErrorMessage(void* context, const char *msg) {
+	flib_log_w("Error from engine (no callback set): %s", msg);
+}
+static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {}
+static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {}
+static void defaultCallback_onNetMessage(void *context, const uint8_t *em, int size) {}
+
+static void clearCallbacks(flib_gameconn *conn) {
+	conn->onConnectCb = &defaultCallback_onConnect;
+	conn->onDisconnectCb = &defaultCallback_onDisconnect;
+	conn->onErrorMessageCb = &defaultCallback_onErrorMessage;
+	conn->onChatCb = &defaultCallback_onChat;
+	conn->onGameRecordedCb = &defaultCallback_onGameRecorded;
+	conn->onNetMessageCb = &defaultCallback_onNetMessage;
+}
+
+static bool getGameMod(flib_cfg_meta *meta, flib_cfg *conf, int maskbit) {
+	for(int i=0; i<meta->modCount; i++) {
+		if(meta->mods[i].bitmaskIndex == maskbit) {
+			return conf->mods[i];
+		}
+	}
+	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
+	return false;
+}
+
+static int fillConfigBuffer(flib_vector configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+	bool error = false;
+	bool perHogAmmo = false;
+	bool sharedAmmo = false;
+
+	error |= flib_ipc_append_message(configBuffer, netgame ? "TN" : "TL");
+	error |= flib_ipc_append_seed(configBuffer, setup->seed);
+	if(setup->map) {
+		error |= flib_ipc_append_mapconf(configBuffer, setup->map, false);
+	}
+	if(setup->script) {
+		error |= flib_ipc_append_message(configBuffer, "escript %s", setup->script);
+	}
+	if(setup->gamescheme) {
+		error |= flib_ipc_append_gamescheme(configBuffer, setup->gamescheme, metaconf);
+		perHogAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
+		sharedAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
+	}
+	if(setup->teams) {
+		for(int i=0; i<setup->teamcount; i++) {
+			error |= flib_ipc_append_addteam(configBuffer, &setup->teams[i], perHogAmmo, sharedAmmo);
+		}
+	}
+	error |= flib_ipc_append_message(configBuffer, "!");
+	return error ? -1 : 0;
+}
+
+static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = calloc(1, sizeof(flib_gameconn));
+	if(tempConn) {
+		tempConn->connection = flib_ipcconn_create(record, playerName);
+		tempConn->configBuffer = flib_vector_create();
+		if(tempConn->connection && tempConn->configBuffer) {
+			tempConn->state = AWAIT_CONNECTION;
+			tempConn->netgame = netGame;
+			clearCallbacks(tempConn);
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create(const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
+	if(tempConn) {
+		if(fillConfigBuffer(tempConn->configBuffer, playerName, metaconf, setup, netgame) == 0) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false);
+	if(tempConn) {
+		if(flib_vector_append(tempConn->configBuffer, demo, size) == size) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size) {
+	flib_gameconn *result = NULL;
+	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
+	if(tempConn) {
+		if(flib_vector_append(tempConn->configBuffer, save, size) == size) {
+			result = tempConn;
+			tempConn = NULL;
+		}
+	}
+	flib_gameconn_destroy(tempConn);
+	return result;
+}
+
+void flib_gameconn_destroy(flib_gameconn *conn) {
+	if(conn) {
+		if(conn->running) {
+			/*
+			 * The function was called from a callback, so the tick function is still running
+			 * and we delay the actual destruction. We ensure no further callbacks will be
+			 * sent to prevent surprises.
+			 */
+			clearCallbacks(conn);
+			conn->destroyRequested = true;
+		} else {
+			flib_ipcconn_destroy(&conn->connection);
+			flib_vector_destroy(&conn->configBuffer);
+			free(conn);
+		}
+	}
+}
+
+int flib_gameconn_getport(flib_gameconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_getport");
+		return 0;
+	} else {
+		return flib_ipcconn_port(conn->connection);
+	}
+}
+
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onConnect");
+	} else {
+		conn->onConnectCb = callback ? callback : &defaultCallback_onConnect;
+		conn->onConnectCtx = context;
+	}
+}
+
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onDisconnect");
+	} else {
+		conn->onDisconnectCb = callback ? callback : &defaultCallback_onDisconnect;
+		conn->onDisconnectCtx = context;
+	}
+}
+
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onErrorMessage");
+	} else {
+		conn->onErrorMessageCb = callback ? callback : &defaultCallback_onErrorMessage;
+		conn->onErrorMessageCtx = context;
+	}
+}
+
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onChat");
+	} else {
+		conn->onChatCb = callback ? callback : &defaultCallback_onChat;
+		conn->onChatCtx = context;
+	}
+}
+
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onGameRecorded");
+	} else {
+		conn->onGameRecordedCb = callback ? callback : &defaultCallback_onGameRecorded;
+		conn->onGameRecordedCtx = context;
+	}
+}
+
+void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_onNetMessage");
+	} else {
+		conn->onNetMessageCb = callback ? callback : &defaultCallback_onNetMessage;
+		conn->onNetMessageCtx = context;
+	}
+}
+
+static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
+	if(conn->state == AWAIT_CONNECTION) {
+		flib_ipcconn_accept(conn->connection);
+		switch(flib_ipcconn_state(conn->connection)) {
+		case IPC_CONNECTED:
+			{
+				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
+				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
+					conn->state = FINISHED;
+					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+					return;
+				} else {
+					conn->state = CONNECTED;
+					conn->onConnectCb(conn->onConnectCtx);
+					if(conn->destroyRequested) {
+						return;
+					}
+				}
+			}
+			break;
+		case IPC_NOT_CONNECTED:
+			conn->state = FINISHED;
+			conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+			return;
+		default:
+			break;
+		}
+	}
+
+	if(conn->state == CONNECTED) {
+		uint8_t msgbuffer[257];
+		int len;
+		while(!conn->destroyRequested && (len = flib_ipcconn_recv_message(conn->connection, msgbuffer))>=0) {
+			if(len<2) {
+				flib_log_w("Received short message from IPC (<2 bytes)");
+				continue;
+			}
+			switch(msgbuffer[1]) {
+			case '?':
+				// The pong is already part of the config message
+				break;
+			case 'C':
+				// And we already send the config message on connecting.
+				break;
+			case 'E':
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
+				}
+				break;
+			case 'i':
+				// TODO stats
+				break;
+			case 'Q':
+			case 'H':
+			case 'q':
+				{
+					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
+					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
+					flib_constbuffer record = flib_ipcconn_getrecord(conn->connection, savegame);
+					if(record.size) {
+						conn->onGameRecordedCb(conn->onGameRecordedCtx, record.data, record.size, savegame);
+						if(conn->destroyRequested) {
+							return;
+						}
+					}
+					conn->state = FINISHED;
+					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
+					return;
+				}
+			case 's':
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
+				}
+				break;
+			case 'b':
+				if(len>=3) {
+					msgbuffer[len-2] = 0;
+					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
+				}
+				break;
+			default:
+				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
+				break;
+			}
+		}
+	}
+
+	if(flib_ipcconn_state(conn->connection) == IPC_NOT_CONNECTED) {
+		conn->state = FINISHED;
+		conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
+	}
+}
+
+void flib_gameconn_tick(flib_gameconn *conn) {
+	if(!conn) {
+		flib_log_e("null parameter in flib_gameconn_tick");
+	} else if(conn->running) {
+		flib_log_w("Call to flib_gameconn_tick from a callback");
+	} else if(conn->state == FINISHED) {
+		flib_log_w("Call to flib_gameconn_tick, but we are already done.");
+	} else {
+		conn->running = true;
+		flib_gameconn_wrappedtick(conn);
+		conn->running = false;
+
+		if(conn->destroyRequested) {
+			flib_gameconn_destroy(conn);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/ipc/gameconn.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,81 @@
+#ifndef GAMECONN_H_
+#define GAMECONN_H_
+
+#include "../util/buffer.h"
+#include "../model/gamesetup.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#define GAME_END_FINISHED 0
+#define GAME_END_INTERRUPTED 1
+#define GAME_END_HALTED 2
+#define GAME_END_ERROR 3
+
+struct _flib_gameconn;
+typedef struct _flib_gameconn flib_gameconn;
+
+flib_gameconn *flib_gameconn_create(const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame);
+flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size);
+flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size);
+void flib_gameconn_destroy(flib_gameconn *conn);
+
+/**
+ * Returns the port on which the gameconn is listening. Only fails if you
+ * pass NULL (not allowed), in that case 0 is returned.
+ */
+int flib_gameconn_getport(flib_gameconn *conn);
+
+/**
+ * Perform I/O operations and call callbacks if something interesting happens.
+ * Should be called regularly.
+ */
+void flib_gameconn_tick(flib_gameconn *conn);
+
+// TODO: Not needed yet, only for netgames
+/*
+flib_gameconn_send_enginemsg(flib_gameconn conn, uint8_t *data, int len);
+flib_gameconn_send_textmsg(flib_gameconn conn, int msgtype, const char *msg);
+flib_gameconn_send_chatmsg(flib_gameconn conn, const char *playername, const char *msg);
+*/
+
+/**
+ * handleConnect(void *context)
+ */
+void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context);
+
+/**
+ * handleDisconnect(void *context, int reason)
+ */
+void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context);
+
+/**
+ * Receives error messages sent by the engine
+ * handleErrorMessage(void* context, const char *msg)
+ */
+void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context);
+
+/**
+ * handleChat(void* context, const char *msg, bool teamchat)
+ */
+void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context);
+
+/**
+ * Called when the game ends
+ * handleGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame)
+ */
+void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context);
+
+/**
+ * Called when the game ends
+ * TODO handleStats(???)
+ */
+
+/**
+ * ...needs to be passed on to the server in a net game
+ * handleEngineMessage(void *context, const uint8_t *em, int size)
+ */
+void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context);
+
+#endif
--- a/project_files/frontlib/ipc/ipcconn.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcconn.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,7 +1,7 @@
 #include "ipcconn.h"
-#include "../logging.h"
+#include "demo.h"
+#include "../util/logging.h"
 #include "../socket.h"
-#include "demo.h"
 
 #include <string.h>
 #include <stdbool.h>
--- a/project_files/frontlib/ipc/ipcconn.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcconn.h	Sat Jun 09 03:28:38 2012 +0200
@@ -5,7 +5,7 @@
 #ifndef IPCCONN_H_
 #define IPCCONN_H_
 
-#include "../buffer.h"
+#include "../util/buffer.h"
 
 #include <stddef.h>
 #include <stdbool.h>
@@ -18,6 +18,8 @@
 typedef struct _flib_ipcconn *flib_ipcconn;
 
 /**
+ * TODO move demo recording up by one layer?
+ *
  * Start an engine connection by listening on a random port. The selected port can
  * be queried with flib_ipcconn_port and has to be passed to the engine.
  *
--- a/project_files/frontlib/ipc/ipcprotocol.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,10 +1,11 @@
 #include "ipcprotocol.h"
-#include "../util.h"
-#include "../logging.h"
+#include "../util/util.h"
+#include "../util/logging.h"
 
 #include <stdio.h>
 #include <stdbool.h>
 #include <string.h>
+#include <inttypes.h>
 
 int flib_ipc_append_message(flib_vector vec, const char *fmt, ...) {
 	int result = -1;
@@ -94,3 +95,87 @@
 		return flib_ipc_append_message(vec, "eseed %s", seed);
 	}
 }
+
+int flib_ipc_append_gamescheme(flib_vector vec, flib_cfg *scheme, flib_cfg_meta *meta) {
+	int result = -1;
+	flib_vector tempvector = flib_vector_create();
+	if(!vec || !scheme || !meta) {
+		flib_log_e("null parameter in flib_ipc_append_gamescheme");
+	} else if(tempvector) {
+		bool error = false;
+		uint32_t gamemods = 0;
+		for(int i=0; i<meta->modCount; i++) {
+			if(scheme->mods[i]) {
+				gamemods |= (1<<meta->mods[i].bitmaskIndex);
+			}
+		}
+		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, gamemods);
+		for(int i=0; i<meta->settingCount; i++) {
+			int value = scheme->settings[i];
+			if(meta->settings[i].checkOverMax) {
+				value = value>meta->settings[i].max ? meta->settings[i].max : value;
+			}
+			if(meta->settings[i].times1000) {
+				value *= 1000;
+			}
+			error |= flib_ipc_append_message(tempvector, "%s %i", meta->settings[i].engineCommand, value);
+		}
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(&tempvector);
+	return result;
+}
+
+// FIXME shared ammo will break per-team ammo
+int flib_ipc_append_addteam(flib_vector vec, flib_team *team, bool perHogAmmo, bool sharedAmmo) {
+	int result = -1;
+	flib_vector tempvector = flib_vector_create();
+	if(!vec || !team || !team->weaponset) {
+		flib_log_e("invalid parameter in flib_ipc_append_addteam");
+	} else if(tempvector) {
+		bool error = false;
+		error |= flib_ipc_append_message(tempvector, "eammloadt %s", team->weaponset->loadout);
+		error |= flib_ipc_append_message(tempvector, "eammprob %s", team->weaponset->crateprob);
+		error |= flib_ipc_append_message(tempvector, "eammdelay %s", team->weaponset->delay);
+		error |= flib_ipc_append_message(tempvector, "eammreinf %s", team->weaponset->crateammo);
+		if(!perHogAmmo) {
+			error |= flib_ipc_append_message(tempvector, "eammstore");
+		}
+
+		char *hash = team->hash ? team->hash : "00000000000000000000000000000000";
+		error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, team->color, team->name);
+
+		if(team->remoteDriven) {
+			error |= flib_ipc_append_message(tempvector, "erdriven");
+		}
+
+		error |= flib_ipc_append_message(tempvector, "egrave %s", team->grave);
+		error |= flib_ipc_append_message(tempvector, "efort %s", team->fort);
+		error |= flib_ipc_append_message(tempvector, "evoicepack %s", team->voicepack);
+		error |= flib_ipc_append_message(tempvector, "eflag %s", team->flag);
+
+		// TODO bindings
+
+		for(int i=0; i<team->hogsInGame; i++) {
+			error |= flib_ipc_append_message(tempvector, "eaddhh %i %i %s", team->hogs[i].difficulty, team->hogs[i].initialHealth, team->hogs[i].name);
+			error |= flib_ipc_append_message(tempvector, "ehat %s", team->hogs[i].hat);
+		}
+
+		if(!error) {
+			// Message created, now we can copy everything.
+			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
+			if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) {
+				result = 0;
+			}
+		}
+	}
+	flib_vector_destroy(&tempvector);
+	return result;
+}
--- a/project_files/frontlib/ipc/ipcprotocol.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/ipcprotocol.h	Sat Jun 09 03:28:38 2012 +0200
@@ -1,8 +1,10 @@
 #ifndef IPCPROTOCOL_H_
 #define IPCPROTOCOL_H_
 
-#include "../buffer.h"
+#include "../util/buffer.h"
 #include "../model/map.h"
+#include "../model/team.h"
+#include "../model/cfg.h"
 
 #include <stdbool.h>
 
@@ -35,4 +37,14 @@
  */
 int flib_ipc_append_seed(flib_vector vec, const char *seed);
 
+/**
+ * Append the game scheme to the buffer.
+ *
+ * Returns nonzero if something goes wrong. In that case the buffer
+ * contents are unaffected.
+ */
+int flib_ipc_append_gamescheme(flib_vector vec, flib_cfg *seed, flib_cfg_meta *meta);
+
+int flib_ipc_append_addteam(flib_vector vec, flib_team *team, bool perHogAmmo, bool sharedAmmo);
+
 #endif /* IPCPROTOCOL_H_ */
--- a/project_files/frontlib/ipc/mapconn.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.c	Sat Jun 09 03:28:38 2012 +0200
@@ -2,8 +2,8 @@
 #include "ipcconn.h"
 #include "ipcprotocol.h"
 
-#include "../logging.h"
-#include "../buffer.h"
+#include "../util/logging.h"
+#include "../util/buffer.h"
 
 #include <stdlib.h>
 
@@ -11,14 +11,14 @@
 	AWAIT_CONNECTION,
 	AWAIT_REPLY,
 	FINISHED
-} mapconn_progress;
+} mapconn_state;
 
 struct _flib_mapconn {
 	uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES];
 	flib_ipcconn connection;
 	flib_vector configBuffer;
 
-	mapconn_progress progress;
+	mapconn_state progress;
 
 	void (*onSuccessCb)(void*, const uint8_t*, int);
 	void *onSuccessCtx;
--- a/project_files/frontlib/ipc/mapconn.h	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/ipc/mapconn.h	Sat Jun 09 03:28:38 2012 +0200
@@ -2,6 +2,7 @@
 #define IPC_MAPCONN_H_
 
 #include "../model/map.h"
+
 #include <stdint.h>
 
 #define MAPIMAGE_WIDTH 256
--- a/project_files/frontlib/logging.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#include "logging.h"
-
-#include <time.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-
-char* flib_format_ip(uint32_t numip) {
-	static char ip[16];
-	snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff));
-	return ip;
-}
-
-static void log_time(FILE *file) {
-    time_t timer;
-    char buffer[25];
-    struct tm* tm_info;
-
-    time(&timer);
-    tm_info = localtime(&timer);
-
-    strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
-    fprintf(file, "%s", buffer);
-}
-
-static void flib_vflog(FILE *file, const char *prefix, const char *fmt, va_list args) {
-	log_time(file);
-	fprintf(file, " [%s]", prefix);
-	vfprintf(file, fmt, args);
-	fprintf(file, "\n");
-	fflush(file);
-}
-
-void flib_log_e(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog(stderr, "E", fmt, argp);
-	va_end(argp);
-}
-
-void flib_log_w(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog(stdout, "W", fmt, argp);
-	va_end(argp);
-}
-
-void flib_log_i(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	flib_vflog(stdout, "I", fmt, argp);
-	va_end(argp);
-}
--- a/project_files/frontlib/logging.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-/*
- *
- */
-
-#ifndef LOGGING_H_
-#define LOGGING_H_
-
-#include<stdint.h>
-
-char* flib_format_ip(uint32_t numip);
-
-void flib_log_e(const char *fmt, ...);
-void flib_log_w(const char *fmt, ...);
-void flib_log_i(const char *fmt, ...);
-
-#endif /* LOGGING_H_ */
--- a/project_files/frontlib/model/cfg.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/model/cfg.c	Sat Jun 09 03:28:38 2012 +0200
@@ -2,9 +2,9 @@
 
 #include "../iniparser/iniparser.h"
 #include "../iniparser/dictionary.h"
-#include "../ini/inihelper.h"
-#include "../logging.h"
-#include "../util.h"
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
 
 #include <stdio.h>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,2 @@
+#include "gamesetup.h"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/gamesetup.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,25 @@
+/**
+ * A complete game configuration that contains all settings for a
+ * local or networked game.
+ *
+ * It should be noted that the meta-configuration is not included.
+ */
+
+#ifndef MODEL_GAMESETUP_H_
+#define MODEL_GAMESETUP_H_
+
+#include "cfg.h"
+#include "weapon.h"
+#include "map.h"
+#include "team.h"
+
+typedef struct {
+    char *seed;						// required
+    char *script;					// optional
+    flib_cfg *gamescheme;			// optional
+    flib_map *map;					// optional
+	flib_team *teams;				// optional
+	int teamcount;
+} flib_gamesetup;
+
+#endif
--- a/project_files/frontlib/model/map.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/model/map.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,8 +1,8 @@
 #include "map.h"
 
-#include "../ini/inihelper.h"
-#include "../util.h"
-#include "../logging.h"
+#include "../util/inihelper.h"
+#include "../util/util.h"
+#include "../util/logging.h"
 
 #include <stdlib.h>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,1 @@
+#include "team.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/model/team.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,51 @@
+#ifndef TEAM_H_
+#define TEAM_H_
+
+#include "weapon.h"
+#include "../hwconsts.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TEAM_DEFAULT_HOGNAME "Hog"
+#define TEAM_DEFAULT_HAT "NoHat"
+#define TEAM_DEFAULT_DIFFICULTY 0
+#define TEAM_DEFAULT_HEALTH 100
+
+typedef struct {
+	char *name;
+	char *hat;
+
+	// Statistics. They are irrelevant for the engine or server,
+	// but provided for ini reading/writing by the frontend.
+	int rounds;
+	int deaths;
+	int kills;
+	int suicides;
+
+	// These settings are sometimes used on a per-team basis.
+	int difficulty;
+	int initialHealth;
+} flib_hog;
+
+typedef struct {
+	flib_hog hogs[HEDGEHOGS_PER_TEAM];
+	char *name;
+	char *grave;
+	char *fort;
+	char *voicepack;
+	char *flag;
+
+	// TODO binds
+
+	// Transient settings used in game setup
+	uint32_t color;
+	int hogsInGame;
+	bool remoteDriven;
+	char *hash;
+
+	// This setting is sometimes used on a per-game basis.
+	flib_weaponset *weaponset;
+} flib_team;
+
+#endif /* TEAM_H_ */
--- a/project_files/frontlib/model/weapon.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/model/weapon.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,9 +1,9 @@
 #include "weapon.h"
 
-#include "../ini/inihelper.h"
 #include "../iniparser/iniparser.h"
-#include "../logging.h"
-#include "../util.h"
+#include "../util/inihelper.h"
+#include "../util/logging.h"
+#include "../util/util.h"
 
 #include <stdlib.h>
 #include <ctype.h>
--- a/project_files/frontlib/socket.c	Fri Jun 08 19:52:24 2012 +0200
+++ b/project_files/frontlib/socket.c	Sat Jun 09 03:28:38 2012 +0200
@@ -1,5 +1,5 @@
 #include "socket.h"
-#include "logging.h"
+#include "util/logging.h"
 #include <stdlib.h>
 #include <SDL_net.h>
 #include <time.h>
--- a/project_files/frontlib/util.c	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#include "util.h"
-
-#include <stddef.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-char *flib_asprintf(const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	char *result = flib_vasprintf(fmt, argp);
-	va_end(argp);
-	return result;
-}
-
-char *flib_vasprintf(const char *fmt, va_list args) {
-	char *result = NULL;
-	int requiredSize = vsnprintf(NULL, 0, fmt, args)+1;				// Figure out how much memory we need,
-	if(requiredSize>=0) {
-		char *tmpbuf = malloc(requiredSize);						// allocate it
-		if(tmpbuf) {
-			if(vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) {		// and then do the actual formatting.
-				result = tmpbuf;
-				tmpbuf = NULL;
-			}
-		}
-		free(tmpbuf);
-	}
-	return result;
-}
-
-char *flib_strdupnull(const char *str) {
-	if(!str) {
-		return NULL;
-	}
-	return flib_asprintf("%s", str);
-}
-
-void *flib_bufdupnull(const void *buf, size_t size) {
-	if(!buf || size==0) {
-		return NULL;
-	}
-	void *result = malloc(size);
-	if(result) {
-		memcpy(result, buf, size);
-	}
-	return result;
-}
--- a/project_files/frontlib/util.h	Fri Jun 08 19:52:24 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#ifndef FLIB_UTIL_H_
-#define FLIB_UTIL_H_
-
-#include <stddef.h>
-#include <stdarg.h>
-
-/**
- * Prints a format string to a newly allocated buffer of the required size.
- * Parameters are like those for printf. Returns NULL on error.
- *
- * Returned buffer must be free()d
- */
-char *flib_asprintf(const char *fmt, ...);
-
-/**
- * Exactly as flib_asprintf, but accepts va_args.
- */
-char *flib_vasprintf(const char *fmt, va_list args);
-
-/**
- * Return a duplicate of the provided string, or NULL if an error
- * occurs or if str is already NULL.
- *
- * Returned buffer must be free()d
- */
-char *flib_strdupnull(const char *str);
-
-/**
- * Return a duplicate of the provided buffer, or NULL if an error
- * occurs or if buf is already NULL or if size is 0.
- *
- * Returned buffer must be free()d
- */
-void *flib_bufdupnull(const void *buf, size_t size);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,90 @@
+#include "buffer.h"
+#include "logging.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+typedef struct _flib_vector {
+	void *data;
+	size_t size;
+	size_t capacity;
+} _flib_vector;
+
+flib_vector flib_vector_create() {
+	flib_vector result = malloc(sizeof(_flib_vector));
+	if(result == NULL) {
+		return NULL;
+	}
+	result->data = malloc(16);
+	if(result->data == NULL) {
+		free(result);
+		return NULL;
+	}
+	result->size = 0;
+	result->capacity = 16;
+	return result;
+}
+
+void flib_vector_destroy(flib_vector *vec) {
+	if(vec && *vec) {
+		free((*vec)->data);
+		free(*vec);
+		*vec = NULL;
+	}
+}
+
+static void try_realloc(flib_vector vec, size_t newCapacity) {
+	void *newData = realloc(vec->data, newCapacity);
+	if(newData) {
+		vec->data = newData;
+		vec->capacity = newCapacity;
+	}
+}
+
+static size_t getFreeCapacity(flib_vector vec) {
+	return vec->capacity - vec->size;
+}
+
+int flib_vector_append(flib_vector vec, const void *data, size_t len) {
+	if(getFreeCapacity(vec) < len) {
+		// Resize exponentially for constant amortized time,
+		// But at least by as much as we need of course,
+		// and be extra careful with integer overflows...
+		size_t extraCapacity = (vec->capacity)/2;
+
+		size_t minExtraCapacity = len - getFreeCapacity(vec);
+		if(extraCapacity < minExtraCapacity) {
+			extraCapacity = minExtraCapacity;
+		}
+
+		if(extraCapacity <= SIZE_MAX - vec->capacity) {
+			try_realloc(vec, vec->capacity+extraCapacity);
+		}
+
+		// Check if we were able to resize.
+		// If not, try to allocate at least what we need...
+		if(getFreeCapacity(vec) < len) {
+			try_realloc(vec, vec->capacity+minExtraCapacity);
+
+			// Still not working? Then we fail.
+			if(getFreeCapacity(vec) < len) {
+				return 0;
+			}
+		}
+	}
+
+	memmove(vec->data + vec->size, data, len);
+	vec->size += len;
+	return len;
+}
+
+flib_buffer flib_vector_as_buffer(flib_vector vec) {
+	flib_buffer result = {vec->data, vec->size};
+	return result;
+}
+
+flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) {
+	flib_constbuffer result = {vec->data, vec->size};
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/buffer.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,58 @@
+#ifndef BUFFER_H_
+#define BUFFER_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * A simple struct to hold both the pointer to an array and its size,
+ * for e.g. conveniently returning it from a function.
+ *
+ * Convention: Size is zero iff data is a NULL pointer.
+ */
+typedef struct {
+	void *data;
+	size_t size;
+} flib_buffer;
+
+/**
+ * Just like flib_buffer, but the contents are not supposed to be modified.
+ */
+typedef struct {
+	const void *data;
+	size_t size;
+} flib_constbuffer;
+
+/**
+ * Simple variable-capacity data structure (opaque type).
+ */
+struct _flib_vector;
+typedef struct _flib_vector *flib_vector;
+
+/**
+ * Create a new vector. Needs to be destroyed again later with flib_vector_destroy.
+ * May return NULL if memory runs out.
+ */
+flib_vector flib_vector_create();
+
+/**
+ * Free the memory of this vector and set it to NULL.
+ */
+void flib_vector_destroy(flib_vector *vec);
+
+/**
+ * Append the provided data to the end of the vector, enlarging it as required.
+ * Returns the ammount of data appended, which is either len (success) or 0 (out of memory).
+ * The vector remains unchanged if an out of memory situation occurs.
+ */
+int flib_vector_append(flib_vector vec, const void *data, size_t len);
+
+/**
+ * Return a buffer or constbuffer pointing to the current contents of the vector.
+ * These will become invalid if the vector size or capacity is changed.
+ */
+flib_buffer flib_vector_as_buffer(flib_vector vec);
+flib_constbuffer flib_vector_as_constbuffer(flib_vector vec);
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,168 @@
+#include "inihelper.h"
+#include "logging.h"
+#include "util.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdarg.h>
+
+static bool keychar_needs_urlencoding(char c) {
+	return !isalnum(c);
+}
+
+char *inihelper_urlencode(const char *inbuf) {
+	if(!inbuf) {
+		return NULL;
+	}
+	size_t insize = strlen(inbuf);
+	if(insize > SIZE_MAX/4) {
+		return NULL;
+	}
+
+	char *outbuf = malloc(insize*3+1);
+	if(!outbuf) {
+		return NULL;
+	}
+
+    size_t inpos = 0, outpos = 0;
+    while(inbuf[inpos]) {
+        if(!keychar_needs_urlencoding(inbuf[inpos])) {
+        	outbuf[outpos++] = inbuf[inpos++];
+        } else {
+            if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) {
+            	free(outbuf);
+            	return NULL;
+            }
+            inpos++;
+            outpos += 3;
+        }
+    }
+    outbuf[outpos] = 0;
+    return outbuf;
+}
+
+char *inihelper_urldecode(const char *inbuf) {
+	char *outbuf = malloc(strlen(inbuf)+1);
+	if(!outbuf) {
+		return NULL;
+	}
+
+    size_t inpos = 0, outpos = 0;
+    while(inbuf[inpos]) {
+        if(inbuf[inpos] == '%' && isxdigit(inbuf[inpos+1]) && isxdigit(inbuf[inpos+2])) {
+            char temp[3] = {inbuf[inpos+1],inbuf[inpos+2],0};
+            outbuf[outpos++] = strtol(temp, NULL, 16);
+            inpos += 3;
+        } else {
+        	outbuf[outpos++] = inbuf[inpos++];
+        }
+    }
+    outbuf[outpos] = 0;
+    return outbuf;
+}
+
+char *inihelper_createDictKey(const char *sectionName, const char *keyName) {
+	if(!sectionName || !keyName) {
+		return NULL;
+	}
+	return flib_asprintf("%s:%s", sectionName, keyName);
+}
+
+char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	if(!inifile || !sectionName || !keyName) {
+		*error = true;
+		return NULL;
+	}
+	char *extendedkey = inihelper_createDictKey(sectionName, keyName);
+	if(!extendedkey) {
+		*error = true;
+		return NULL;
+	}
+	char *result = iniparser_getstring(inifile, extendedkey, NULL);
+	free(extendedkey);
+	if(!result) {
+		flib_log_i("Missing ini setting: %s/%s", sectionName, keyName);
+		*error = true;
+	}
+	return result;
+}
+
+char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName));
+}
+
+int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
+	if(!value) {
+		return 0;
+	} else {
+		errno = 0;
+		long val = strtol(value, NULL, 10);
+		if(errno!=0) {
+			*error = true;
+			return 0;
+		}
+		if(val<INT_MIN || val>INT_MAX) {
+			*error = true;
+			return 0;
+		}
+		return (int)val;
+	}
+}
+
+bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) {
+	char *value = inihelper_getstring(inifile, error, sectionName, keyName);
+	if(!value) {
+		return false;
+	} else {
+		bool trueval = strchr("1tTyY", value[0]);
+		bool falseval = strchr("0fFnN", value[0]);
+		if(!trueval && !falseval) {
+			*error = true;
+			return false;
+		} else {
+			return trueval;
+		}
+	}
+}
+
+int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value) {
+	int result = -1;
+	if(!dict || !sectionName || !keyName || !value) {
+		flib_log_e("inihelper_setstr called with bad parameters");
+	} else {
+		char *extendedkey = inihelper_createDictKey(sectionName, keyName);
+		if(extendedkey) {
+			result = iniparser_set(dict, extendedkey, value);
+			free(extendedkey);
+		}
+	}
+	return result;
+}
+
+int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value) {
+	int result = -1;
+	if(!dict || !sectionName || !keyName) {
+		flib_log_e("inihelper_setint called with bad parameters");
+	} else {
+		char *strvalue = flib_asprintf("%i", value);
+		if(strvalue) {
+			result = inihelper_setstr(dict, sectionName, keyName, strvalue);
+			free(strvalue);
+		}
+	}
+	return result;
+}
+
+int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value) {
+	int result = -1;
+	if(!dict || !sectionName || !keyName) {
+		flib_log_e("inihelper_setint called with bad parameters");
+	} else {
+		result = inihelper_setstr(dict, sectionName, keyName, value ? "true" : "false");
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/inihelper.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,54 @@
+/**
+ * Some helper functions for working with the iniparser functions - in particular,
+ * for interoperability with the ini format used by the QtSettings class.
+ */
+
+#ifndef INIHELPER_H_
+#define INIHELPER_H_
+
+#include "../iniparser/iniparser.h"
+
+#include <stdbool.h>
+
+/**
+ * Returned buffer must be free()d
+ */
+char *inihelper_urlencode(const char *inbuf);
+
+/**
+ * Returned buffer must be free()d
+ */
+char *inihelper_urldecode(const char *inbuf);
+
+/**
+ * Create a key in the format "sectionName:keyName"
+ * Returned buffer must be free()d
+ */
+char *inihelper_createDictKey(const char *sectionName, const char *keyName);
+
+/**
+ * Returns an internal buffer, don't modify or free
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+char *inihelper_getstring(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Returned buffer must be free()d
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+/**
+ * Sets error to true if something goes wrong, leaves it unchanged otherwise.
+ */
+bool inihelper_getbool(dictionary *inifile, bool *error, const char *sectionName, const char *keyName);
+
+int inihelper_setstr(dictionary *dict, const char *sectionName, const char *keyName, const char *value);
+int inihelper_setint(dictionary *dict, const char *sectionName, const char *keyName, int value);
+int inihelper_setbool(dictionary *dict, const char *sectionName, const char *keyName, bool value);
+#endif /* INIHELPER_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,53 @@
+#include "logging.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+char* flib_format_ip(uint32_t numip) {
+	static char ip[16];
+	snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff));
+	return ip;
+}
+
+static void log_time(FILE *file) {
+    time_t timer;
+    char buffer[25];
+    struct tm* tm_info;
+
+    time(&timer);
+    tm_info = localtime(&timer);
+
+    strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info);
+    fprintf(file, "%s", buffer);
+}
+
+static void flib_vflog(FILE *file, const char *prefix, const char *fmt, va_list args) {
+	log_time(file);
+	fprintf(file, " [%s]", prefix);
+	vfprintf(file, fmt, args);
+	fprintf(file, "\n");
+	fflush(file);
+}
+
+void flib_log_e(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog(stderr, "E", fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_w(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog(stdout, "W", fmt, argp);
+	va_end(argp);
+}
+
+void flib_log_i(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	flib_vflog(stdout, "I", fmt, argp);
+	va_end(argp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/logging.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,16 @@
+/*
+ *
+ */
+
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+#include<stdint.h>
+
+char* flib_format_ip(uint32_t numip);
+
+void flib_log_e(const char *fmt, ...);
+void flib_log_w(const char *fmt, ...);
+void flib_log_i(const char *fmt, ...);
+
+#endif /* LOGGING_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.c	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,49 @@
+#include "util.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+char *flib_asprintf(const char *fmt, ...) {
+	va_list argp;
+	va_start(argp, fmt);
+	char *result = flib_vasprintf(fmt, argp);
+	va_end(argp);
+	return result;
+}
+
+char *flib_vasprintf(const char *fmt, va_list args) {
+	char *result = NULL;
+	int requiredSize = vsnprintf(NULL, 0, fmt, args)+1;				// Figure out how much memory we need,
+	if(requiredSize>=0) {
+		char *tmpbuf = malloc(requiredSize);						// allocate it
+		if(tmpbuf) {
+			if(vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) {		// and then do the actual formatting.
+				result = tmpbuf;
+				tmpbuf = NULL;
+			}
+		}
+		free(tmpbuf);
+	}
+	return result;
+}
+
+char *flib_strdupnull(const char *str) {
+	if(!str) {
+		return NULL;
+	}
+	return flib_asprintf("%s", str);
+}
+
+void *flib_bufdupnull(const void *buf, size_t size) {
+	if(!buf || size==0) {
+		return NULL;
+	}
+	void *result = malloc(size);
+	if(result) {
+		memcpy(result, buf, size);
+	}
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/frontlib/util/util.h	Sat Jun 09 03:28:38 2012 +0200
@@ -0,0 +1,36 @@
+#ifndef FLIB_UTIL_H_
+#define FLIB_UTIL_H_
+
+#include <stddef.h>
+#include <stdarg.h>
+
+/**
+ * Prints a format string to a newly allocated buffer of the required size.
+ * Parameters are like those for printf. Returns NULL on error.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_asprintf(const char *fmt, ...);
+
+/**
+ * Exactly as flib_asprintf, but accepts va_args.
+ */
+char *flib_vasprintf(const char *fmt, va_list args);
+
+/**
+ * Return a duplicate of the provided string, or NULL if an error
+ * occurs or if str is already NULL.
+ *
+ * Returned buffer must be free()d
+ */
+char *flib_strdupnull(const char *str);
+
+/**
+ * Return a duplicate of the provided buffer, or NULL if an error
+ * occurs or if buf is already NULL or if size is 0.
+ *
+ * Returned buffer must be free()d
+ */
+void *flib_bufdupnull(const void *buf, size_t size);
+
+#endif